تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Building Toolbar Layout0:01

I'm pretty excited for this episode. It's so cool when you can see something come together, and this is one of those episodes. Let's add a toolbar to our TipTap Markdown Editor. So in the component itself, why don't we add a menu item, we'll stay reasonably semantic, and we'll have a list item in there, with a button, and we'll say Bold. OK, there's our Bold button, and we'll need to make sure that the type is Button rather than Submit, so that it doesn't submit the form that it's inside of. And let's go ahead and give it a class of, I don't know, px-3, py-2, perhaps? Yeah, a little bit of spacing. And when you hover, how about we set the background to a grey-200? There we go.

And when you hover, how about we set the background to a grey 200? There we go. And you'll see that it's actually overriding the top left corner radius, so we could probably say RoundedTL for top left, and then MD, and there we go. Yeah, that looks much better. Just so we can see what two menu items look like at the same time, let's get rid of the RoundedTL MD, it's not needed on this one, and let's say that this can be the Italic button. OK, so on the menu we'll want to give it a class of Flex, that's much better. And how about we divide on the x-axis, so we can put a little spacer between items,

Adding RemixIcon Icons1:16

OK, so on the menu we'll want to give it a class of Flex, that's much better. And how about we divide on the x-axis, so we can put a little spacer between items, and let's also say that this has a border on the bottom to divide up the editor and the buttons along the top. Let's turn our attention to iconography a second. So if we go to the TipTap documentation, Menus, and there's a section called Icons at the bottom. I like to use Heroicons where I can, but Heroicons doesn't have any icons for editors, whereas RemixIcon, which is suggested here, that does have editor icons.

but Heroicons doesn't have any icons for editors, whereas RemixIcon, which is suggested here, that does have editor icons, so we'll make use of that. I need to pull this in, so let's go to the GitHub repository, and inside here, npm install RemixIconSave, let's go ahead and run that. And once that's installed, we should be able to import the font like so, and I'm going to import it inside the editor itself, since this is the only place we're using RemixIcons, but if that changes and we decide to use RemixIcons for other things,

since this is the only place we're using RemixIcons, but if that changes and we decide to use RemixIcons for other things, I can add it globally to the general import, maybe even inside my style.css file. So we should be able to make use of this now, and replace the text here with an icon tag, and the icon class is... how do we use that inside Remix? Let's have a look. So ri-, and I imagine ri-bold is an option, for example. Let's take a look.

So ri-, and I imagine ri-bold is an option, for example. Let's take a look. Yeah, there we go, there's the bold icon, and we should be able to do very much the same here, so i, and then class equals ri-italic, and there we have our italic icon, and it's slightly slanted, which is very nice. And the last thing we'll want to do is add titles to these buttons so that screen readers understand what the button's for, and so that users who don't understand iconography.

so that screen readers understand what the button's for, and so that users who don't understand iconography know what they're for as well. So here we'll say that this is the bold icon, and that this is the italic icon. Okay, and if we hover over these buttons now, there we go, we can see that that one's bold, and that one's italic. So visually this is looking great, but obviously clicking our buttons won't do anything at all at the moment.

Wiring Bold/Italic Actions3:29

So visually this is looking great, but obviously clicking our buttons won't do anything at all at the moment. We need to tell TipTap that when we click those buttons, an action should be performed inside the editor itself. Let's get them wired up. We'll start with our bold button, and we need an @click handler, and that click handler is going to be a closure, and we want to grab the editor instance. Tell you what, let's put these on separate lines so that we can see a little better.

Tell you what, let's put these on separate lines so that we can see a little better. So grab the editor instance, and then there's a method you need to call called chain, and chain is basically a way of saying run the following commands all together. So what commands do we need to run? Well, the first command I'm going to run is the focus command, and essentially when we click, say, the bold icon, it's going to move the focus away from the editor.

and essentially when we click, say, the bold icon, it's going to move the focus away from the editor. That's how HTML works. But the focus method informs TipTap that it should refocus back to the editor straight away, which is exactly what we want. After focusing, we want to toggle, in this case, bold. So in other words, if the text that's selected is not bold, it will be made bold. If it already is bold, it will be made normal again.

it will be made bold. If it already is bold, it will be made normal again. It will have standard weight. And then finally, to execute focus and toggleBold, we call the run method. Let's see if this works. Let's write ourselves a sentence. This is a sentence, and let's select one word inside that sentence and hit bold, and you can see it is now bold.

and let's select one word inside that sentence and hit bold, and you can see it is now bold. That's how easy it is to configure this kind of functionality using TipTap. If I select sentence again and click it once more, now the sentence is not bold. If I click bold and then start typing, everything I type is going to be in bold until I once again click bold and come off it. Now currently, the bold button has no state indication

Showing Active Button State5:13

until I once again click bold and come off it. Now currently, the bold button has no state indication to tell us whether the bold font is active or not. That should be quite straightforward as well. So we can come to our class here, and let's make use of object syntax. So with object syntax, you pass some classes, let's say BGRed500, and then you pass a boolean, and if that boolean is true, then that class will take effect,

and then you pass a boolean, and if that boolean is true, then that class will take effect, but if that boolean evaluates to false, then that class will not take effect. So in this case, we could say that the background should be Indigo500, and it should be Indigo500 if the editor.isActive, so that's a method you can call on the editor instance, and we want to check for the bold string. So that's the identifier of the bold plugin

and we want to check for the bold string. So that's the identifier of the bold plugin that's being used under the hood, and because the editor instance could be null, I'm going to wrap the entire div in a v-if, so v-if editor, and then I don't have to add any null checks inside the HTML itself. Okay, so you can see now we're currently not focused on a bold section, but if I select this text here, it is bold.

we're currently not focused on a bold section, but if I select this text here, it is bold. We should probably use white text instead of black text, so let's say text-white, and there we go. That looks great. I don't like the fact that when I hover over that button, it goes gray even if it's active, so we could do this one of two ways. We could duplicate this, and we could say that we should only use bg-gray-200.

We could duplicate this, and we could say that we should only use bg-gray-200 if the editor is not active, and that should work, but I think a better way would be to use a race syntax here instead, so we can say, well, if the editor is active, then I'll use shorthand if, so a ternary operator, and I'll say, in that case, apply these classes. Otherwise, apply this class here, and that should work just as well,

Otherwise, apply this class here, and that should work just as well, but it just simplifies it a little. Let's get rid of that, clean it up a little, and let's see if it works. So if I hover over this button now, nothing, but if I turn it off, and then I hover, now we have the correct hover state again. So there we go, that works very well, and we can immediately see when Bold is active.

So there we go, that works very well, and we can immediately see when Bold is active or when it's not active, and this works for text that you're about to type as well, so if I add a space and then click the Bold icon, you can see that button is highlighted in our toolbar, and indeed, the text we've selected is, in fact, Bold. Awesome. We'll just clean up the HTML a little to put everything on its own line,

We'll just clean up the HTML a little to put everything on its own line, and then rather than changing the italic button that we created, I'm just going to copy and paste and make the changes here in a new button. So we'll get rid of that. The icon needs to change to Italic. This is going to be Italic rather than Bold. What else do we need to change? We don't need rounded TLMD on this one,

What else do we need to change? We don't need rounded TLMD on this one, and we don't want to toggle Bold, we want to toggle Italic. There we go. Anything else? I think that looks fine. Oh, we need to change the title to Italic as well. Okay, let's see if that works. So I'll select a piece of text,

Okay, let's see if that works. So I'll select a piece of text, click the Italic button, and yeah, you can see it is now Italic. If I click the Bold button as well, now both of those states are true. It is both Bold and Italic, and at any moment I can turn Italic off, I can turn Bold off, in order to return it to its standard format.

I can turn Bold off, in order to return it to its standard format. We could still use markdown formatting here, by the way. So I can use double star and say this is Bold, and you'll see, yeah, it does change to Bold, but the toolbar is also able to understand that it's Bold as well. So you can use either or, whichever makes sense for your current use case. Let's add a few more.

Adding More Formatting Buttons8:56

for your current use case. Let's add a few more. How about Strikethrough? So I'll copy and paste this, and let's say instead of toggling Italic, we want to toggle Strike, and this should be Strikethrough. We want to check for Strikethrough in lowercase. By the way, all of this kind of stuff comes from the documentation for TipTap,

By the way, all of this kind of stuff comes from the documentation for TipTap, so the name of each plugin, for example, or alternatively, you can go to the starterkit.ts file, and you'll see in here all of the plugins that come configured inside Starterkit. So Strike is one of them, and the name here is what you're looking for when you're defining that isActive check. So it's Strike, not Strikethrough.

when you're defining that isActive check. So it's Strike, not Strikethrough. So let's select that, replace it with Strike, and what else do we have? We have the icon. What have they got? Strikethrough looks like it should do the job. Let's see if it works. So here's our new Strikethrough button. Let's select a word, hit Strikethrough,

So here's our new Strikethrough button. Let's select a word, hit Strikethrough, and the Strikethrough works perfectly. Let's do a few more. So block quotes are going to be very popular for films, for example. Let's copy this, let's paste it, and instead of ToggleStrike, I imagine we want ToggleBlockQuote. This will be BlockQuote,

I imagine we want ToggleBlockQuote. This will be BlockQuote, and what else do we need to change? We need to change this to BlockQuote, and we need to change Strikethrough here to, let's see what icons are available, QuoteText, DoubleQuoteL. I think that will look quite nice. Let's see if that works. Here is our quotes.

Let's see if that works. Here is our quotes. Let's start a new quote, and we'll say Pizza Time. Okay, that works perfectly as well. What about lists? Lists are very popular. So again, let's copy this. We'll start with a bullet list. Let's say ToggleBulletList.

We'll start with a bullet list. Let's say ToggleBulletList. There we go, ToggleBulletList. We'll change the title to BulletList, and I don't know what the ID is here, so again, let's go to the StarterKit.ts file, find BulletList, and it's BulletList with CamelCase, so good job we checked that because I'd have got that wrong.

so good job we checked that because I'd have got that wrong. Let's paste it in there, and let's see if there's a bullet icon or an unordered list icon. Yeah, list unordered, lovely. So does this work? Here's a bullet point. Look at that. This is wonderful.

Look at that. This is wonderful. This movie is great. I love it, and TipTap is pretty awesome too. Now, what if we want to change this list from bullet points to an ordered list, so 1, 2, 3, etc.? Well, that should be pretty simple as well. Let's take this list.

Well, that should be pretty simple as well. Let's take this list. Let's copy it. Instead of Bullet List, let's have a numeric list, and let's have a look. Toggle Ordered List maybe? Yep, toggle Ordered List. Again, StarterKit, find that plugin, Ordered List,

Again, StarterKit, find that plugin, OrderedList, and it's OrderedList in CamelCase, so we'll drop that in as the ID, and I imagine they have an OrderedList icon as well, which they do. Awesome. There we go. So here's the OrderedList.

There we go. So here's the Ordered List rather than the Bullet List. I've selected the Bullet List, so I'm hoping if I click this Ordered List, the bullet should change to numbers. There we go. 1, 2, 3. I can go back to the Bullet List. I can go back to the numeric list,

I can go back to the Bullet List. I can go back to the numeric list, and I imagine as well that, as before, our standard markdown formatting works, so if I do a hyphen and a space, I immediately go into a new Bullet List. Okay. What else do we want? What about headings? So H1, H2, H3.

Configuring Headings and Links12:28

What about headings? So H1, H2, H3. That's quite a popular thing as well, isn't it? Let's tackle that next. Okay, so let's drop a new button in, and I imagine they have a toggle heading option, which they do, but how do we specify the level of heading? Let's go and take a look at the TipTap documentation. So head back to the TipTap documentation,

Let's go and take a look at the documentation. So head back to the TipTap documentation, and let's search for heading. Okay, here we go. Take a look down here. Okay, so we can configure the levels that are actually available, which we will want to do. We'll come back to that. But we also want to be able to toggle a heading,

We'll come back to that. But we also want to be able to toggle a heading, and we can stipulate which level it should be. So that's exactly what we want. So when we say toggle heading here, we should be able to say the level we're interested in is level 1. Let's set this to heading 1, and do they have a H1 icon? H-1 or H-1?

and do they have a H1 icon? H-1 or H-1? Yes, they do. And this, again, is a good question. How do you check for which heading is currently active? Let's have a look. Maybe the source code could tell us. Ah, it's active. And then you say heading, and then, again, as a second parameter,

And then you say heading, and then, again, as a second parameter, you check the level that you're actually interested in. So I'm going to check for heading, and I want to check, is the heading level 1? If it's not, then this button shouldn't be active. Okay, let's see if it works. So here's H1. This is a big title. Yes, it works perfectly.

This is a big title. Yes, it works perfectly. Now, although H1 is what we want the user to think they're activating, what we actually want them to activate is a H2 under the hood because this title here is the real H1, right? And we don't want multiple H1s on our page. So what they actually should be typing is a H2. They just don't know it. So here's what I'm going to do. I'm going to set the level to 2 and the level to 2 here,

So here's what I'm going to do. I'm going to set the level to 2 and the level to 2 here, but I'll keep the title and icon as level 1, so to speak. And we'll grab this list item. We'll copy that out and copy it out again. And I'm going to change this one to level 3 and level 3, and I'll change the title to heading 2 and the icon to heading 2. And with the third button, well, that's going to be level 4 and level 4, but the title is heading 3 and the icon is heading 3. So although this is H1, H2, H3,

but the title is heading 3 and the icon is heading 3. So although this is H1, H2, H3, what they're actually creating is a H2, a H3, or a H4. Now, I would imagine if we use markdown syntax, so one hash and then a space, they're still going to create a big title, and that is accurate if we take a look at the text area underneath. It just has that one hash. We don't want to allow them to do that for the same reason that H1 isn't a real H1.

We don't want to allow them to do that for the same reason that H1 isn't a real H1. But I think we can use this option up here. Where is it? heading.configure to actually say we're not allowed to create H1s. So we're only allowed to create 2, 3, and 4. Let's see if we can make that happen. So I'll come down to where we declare StarterKit, and I think I can say starterkit.configure, and then I can pass in the subplugin configuration options.

and I think I can say starterkit.configure, and then I can pass in the subplugin configuration options. So in this case, I'm looking for heading, and I only want to support levels of 2, 3, and 4. Let's see if that works. We'll come back, and I'm going to try typing one hash and then a space, and look at that. So I imagine what's happened is, yeah, they've converted it to a H2 automatically. And if I tried to do 1, 2, 3, 4, 5, and then a space,

they've converted it to a H2 automatically. And if I tried to do 1, 2, 3, 4, 5, and then a space, this is a title, nothing happens. It's escaped the whole thing in the Markdown output because we don't support headings that high. So that's a great way to be able to configure the plugins and say you're only allowed to do this, even though technically Markdown allows you to do even more than that. I think the final thing we're missing in our toolbar

to do even more than that. I think the final thing we're missing in our toolbar is the ability to add links to our comments, to our posts. So in this case, if I say https://laracasts.com, that is not a link in any way. That's just plain text, and I really think it should be a link. So let's take a look at how we can make a link. In the TipTap documentation, we have link here. Looks like we have to install the extension. I don't think this is available in the Starter Kit.

Looks like we have to install the extension. I don't think this is available in the Starter Kit. Let's go to starterkit.ts and take a quick look here. No, it's not available. So we'll grab this extension, and let's install it. And once it's installed, I imagine we can come to our Markdown editor, and I can just import that new linkCheck. We'll have to make sure we import the correct one, so I'll import it manually up here.

We'll have to make sure we import the correct one, so I'll import it manually up here. I don't want to accidentally import the link from Inertia, for example. So import link from TipTap extension link. All right, let's see if that works. So I'm going to come straight back to the editor, and I'll just say https://laracasts.com. You can see straight away it's formatted it. It's recognized it as a link. And down here in the output, it's used angle brackets,

It's recognized it as a link. And down here in the output, it's used angle brackets, which is basically a way in Markdown of saying this is a link. So that's pretty great already. That works perfectly. However, what if you want to use pretty text? So what if you want to say Laracast, but then this is actually a href, a hyperlink, that leads to laracast.com? I think we're going to have to introduce a button in the toolbar for this.

that leads to laracast.com? I think we're going to have to introduce a button in the toolbar for this. So let's add a button. Come back up here. Let's grab this. And actually, we'll place this button in between the list items and the headings. And we'll just tackle the easy things first. So let's say add link, and then we want maybe a link icon if there is one.

So let's say addLink, and then we want maybe a link icon if there is one. Yes, there is. How does that look? Brilliant. So the way I see it, we are going to select a piece of text, click this icon, and it should open up a prompt asking us to enter the link we want to attach to this piece of text. We can do that because we own the full process.

to this piece of text. We can do that because we own the full process. It's a headless editor. We have Vue and JavaScript on our side. So instead of performing this action directly when we click this button, what if we prompt the user for the href? And obviously, this is going to be a callable that we declare down at the bottom. So we'll add that, const promptUserForHref. So what do we want to do inside this closure?

So we'll add that, const prompt user for href. So what do we want to do inside this closure? First of all, we need to ask the user where they want to link to. So let's say const href, and we'll use the built-in prompt function for now. So where do you want to link to question? And then we'll check if they actually provide something. So if not href, then we should just be able to return nothing. So nothing's going to happen. Maybe we want to refocus the editor again though.

So nothing's going to happen. Maybe we want to refocus the editor.value.chain.focus.run. But if they have provided a href, then we want to use that href as the link. So let's take a look at the TipTap documentation to see how we do that. Let's come down here, and we should have, yeah, setLink or toggleLink. I think maybe setLink would be the correct option here.

and we should have, yeah, setLink or toggleLink. I think maybe setLink would be the correct option here. So editor.commands.setLink, and you pass in a href parameter. So let's do that. Return editor.value.chain. I still want to refocus, so I'll call .focus.setLink, and then the attribute is href. And because we declared the const as href, we can actually just pass that in directly like so.

And because we declared the const as href, we can actually just pass that in directly like so. Let's see if that works. So I'll enter laracasts as text. I'll select it. I'll click this link icon. https://laracasts.com. Click OK, and that didn't do anything. Let's take a look if there was any errors. Were there any errors?

Let's take a look if there was any errors. Were there any errors? Oh, I never run it. .run. Okay, let's see if that works. So we'll come back, laracasts, and let's select that text, add a link, https://laracasts.com, and boom, it's been linked.

https://laracasts.com, and boom, it's been linked, and we should have that syntax in Markdown. Yep, using the square brackets for the text and then normal brackets, parentheses, whatever you want to call them, for our link itself. That is perfect. What happens if we select this? We get taken straight to Laracast.

What happens if we select this? We get taken straight to Laracasts. That's brilliant. If I select this, okay, our active state isn't working. Let's fix that first of all. So where we have our link up here, which is, where are you, prompt user for href. I want to check if a link is active.

prompt user for href. I want to check if a link is active. Will that be enough for this to work? Yes, it will. But obviously, if I click this again, it isn't going to work. So what should happen is it should detect if it is active, and if it is, it should actually unset the link.

and if it is, it should actually unset the link. That should be pretty straightforward, actually. So inside our little promptUser for href check, we could say, well, if editor.value.isActive, and we're going to check for link, so if it's already active, what we'll actually do is return editor.value, and I'm going to say chain.remove,

what we'll actually do is return editor.value, and I'm going to say chain.remove, or unset link, maybe? Yeah, unset link.run. If I select this and click it again, the link is gone, and the output is once again plain text. So there we go. Pretty easy to actually add all of this functionality to our toolbar.

Pretty easy to actually add all of this functionality to our toolbar. TipTap takes all the hard work out of it. All we have to do is come up with the UI and then configure it the way we'd like. Talking about configuration, one last thing I'd like to do, I'm going to come to the editor up here, where we have starterKit. There are a few plugins I'd actually like to disable.

where we have starter kit. There are a few plugins I'd actually like to disable. So I don't want to support code blocks because this is a movie forum, so I'll remove code and code block. But perhaps you do want to support code blocks. If yours is for developers, for example. And one cool thing to play with is, can you get your button for turning something into a code block

can you get your button for turning something into a code block not only to render the code block, but also to have correct highlighting inside that code block? You could use something like highlight.js to accomplish that. It's not something for this video, it's not something for this series, but I'd be interested to hear

it's not something for this series, but I'd be interested to hear if you can get that working. And I'm pretty sure you can because of how extensible TipTap is. So, we have a working Markdown editor. An editor that is friendly for advanced Markdown users, but it's also just as friendly for those who have no clue what Markdown is. They can use this toolbar.

for those who have no clue what Markdown is. They can use this toolbar and they can easily format their text when describing movies. So in the next episode, let's go ahead and wire this up so that we're actually able to see formatted HTML, formatted Markdown in the posts and comments that we create.

in the Post and Comment that we create.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟