Lexical React - Headings, Lists, Toolbar
Summary
TLDRこの動画のスクリプトでは、React製のリッチテキストエディターツールキットであるLexicalを使って、ヘディングやリストを追加するツールバーの作り方を解説しています。最初にヘディングプラグインを作成し、選択したテキストをヘディングに変換する方法を示しています。次に、さまざまなヘディングレベルをサポートするよう機能を拡張します。さらに、リストプラグインを追加し、順序付きリストや順序なしリストを作成する方法を説明しています。このチュートリアルを通して、Lexicalを使ったリッチテキストエディターのカスタマイズ方法を実践的に学ぶことができます。
Takeaways
- 🚀 この動画では、React の lexical エディターでツールバーを構築する方法を学びます。
- 📝 前回の動画では、ヘッディングノードを挿入するだけでしたが、今回はテキストの選択とヘッディングへの変換、リストの追加など、より実践的な機能を追加しています。
- 🔧 setBlocksType ヘルパーを使用して、選択したテキストをヘッディングに変換することができます。
- ⌨️ ツールバーにボタンを追加し、タグに基づいてヘッディングの種類 (h1、h2、h3) を切り替えることができます。
- 📖 スタイルシートを使ってヘッディングの見た目をカスタマイズできます。
- ✅ lexical-list パッケージをインストールして、ordered list と unordered list のコマンドをツールバーに追加できます。
- 🧩 プラグインを作成してツールバーの機能を構築し、ヘッディングとリストのプラグインを合成することができます。
- 🔄 インストール時に node_modules の問題が発生した場合は、キャッシュをクリアしてパッケージを再インストールする必要があります。
- 🧭 lexical のプレイグラウンドでさまざまなツールバーの実装例を確認できます。
- 🏗 ツールバーの構築方法は多様で、開発者ごとにカスタマイズできるため、自分なりのソリューションを模索することが推奨されています。
Q & A
1. Lexical Reactとは何ですか?
-Lexical Reactは、Facebookが開発したリッチテキストエディタの構築を支援するライブラリです。ReactベースのコンポーネントとAPIを提供し、高度にカスタマイズ可能なテキストエディタの作成を可能にします。
2. 前回の動画では何を行いましたか?
-前回の動画では、基本的なボタンを作成し、そのボタンをクリックするとヘッディングノードが挿入されるようにしました。これは、ツールバーとエディターとのUIの対話の基礎となるものでした。
3. 今回のゴールは何ですか?
-今回の目標は、ツールバーをさらに拡張し、ヘッディングの使用をより実用的なものにすること、そしてリストのサポートを追加することです。つまり、UIとの対話をより深く行うことが目的です。
4. セレクトされたテキストをヘッディングに変換するにはどうすればよいですか?
-setBlocksTypeヘルパー関数を使用します。これにより、選択範囲とノード作成関数を渡すことで、その選択範囲をヘッディングノードで囲むことができます。
5. 様々なヘッディングレベル(H1、H2、H3など)をどのように実装しますか?
-各ヘッディングレベルに対応するボタンを作成し、そのボタンをクリックしたときに適切なヘッディングノードを作成するようにします。ボタンには独自のスタイルを適用することもできます。
6. リストのサポートを追加するにはどうすればよいですか?
-lexical-reactパッケージから提供されているlistPluginを使用します。このプラグインにより、ordered listやunordered listを挿入したり削除したりするコマンドが利用可能になります。
7. リストノードをエディターに登録する必要があるのはなぜですか?
-現在のLexicalの制約上、リストノードをエディターに明示的に登録する必要があります。プラグイン自体でノードを登録することはできないためです。この点は将来的に改善される可能性があります。
8. ツールバーの構造をどのように構築しますか?
-ツールバーの構造は、Reactコンポーネントを構成することで作成できます。ツールバー全体をひとつのプラグインとしてラップし、その中で個々のボタンを別のコンポーネントとして作成するのが一般的なアプローチです。
9. Lexicalでツールバーを実装する方法は決まっていますか?
-いいえ、ツールバーの実装方法は決まっていません。Lexicalはフレキシブルなので、Reactコンポーネントの構成方法は自由に選択できます。プレイグラウンドなどのサンプルを参考にしつつ、自分なりの実装方法を探ることが推奨されています。
10. 動画の最後に言及されている「より興味深い使用例」とは何を指していますか?
-動画の最後では、今後の動画でより高度でエキサイティングな機能の実装を行うことを示唆しています。たとえば、リッチテキストフォーマッティングの追加や、カスタムノードの作成などが考えられます。
Outlines
🔩 ヘッダープラグインの拡張
この段落では、前回のビデオで作成した基本的な見出しボタンをさらに拡張し、テキスト選択に応じて見出しを作成できるように変更しています。さらに、H1、H2、H3の見出しタイプに対応するボタンを作成し、スタイルを適用しています。
🛠️ ツールバーの作成
この段落では、見出しボタンをツールバーに統合するために、新しいツールバーコンポーネントを作成しています。見出しの各レベルに対応するボタンを作成し、それぞれにスタイルを適用しています。また、H1にはアンダーラインを、H2には太字を、H3には通常のスタイルを適用しています。
📝 リストサポートの追加
この段落では、リストのサポートを追加するために、lexical-listパッケージをインストールしています。次に、順序付きリストと順序なしリストに対応するボタンを作成し、リストプラグインをインポートしてエディタに登録しています。
✨ リストコマンドの実装
この段落では、リストコマンドの実装について説明しています。リストコマンドを呼び出すためのクリックハンドラを作成し、EditorオブジェクトのdispatchCommandメソッドを使用してリストコマンドを発行しています。また、リストノードをエディタに登録する必要があることを説明しています。
🐞 バグの修正とデモ
この段落では、リストコマンドが正しく機能しない問題を解決するために、node_modulesをクリーンインストールしています。その後、ヘッディングとリストの機能を実際に使用してデモを行い、ツールバープラグインの拡張方法について言及しています。
🎬 まとめ
この最終段落では、Lexicalでのツールバーの作成方法について総括しています。プラグインの構成を説明し、さまざまな方法でツールバーを実装できることを示唆しています。最後に、今後の動画でより興味深い使用例を取り上げることを示唆しています。
Mindmap
Keywords
💡lexical
💡プラグイン
💡ツールバー
💡コマンド
💡ノード
💡コンポーザブル
💡セレクション
💡スタイリング
💡コンポーネント
💡アップデート
Highlights
The tutorial covers building an editor in Lexical React, expanding on the previous video where a basic button was created to insert a heading node.
The objective is to make the toolbar more versatile by allowing users to select text and convert it into different heading levels (H1, H2, H3), as well as add support for ordered and unordered lists.
The setBlocksType helper from Lexical is introduced, which allows wrapping a selected range in a specified block type by providing a creator function.
The tutorial walks through checking if the selection is a range selection, as setBlocksType only works with range selections.
The tutorial demonstrates creating multiple buttons for H1, H2, and H3 headings by mapping over an array of tags and rendering buttons with respective click handlers.
Styling is applied to the different heading levels using CSS to illustrate how custom styles can be added.
The tutorial introduces adding support for ordered and unordered lists by installing the @lexical/list package and registering the necessary list nodes.
A separate toolbar plugin is created for the list functionality, which dispatches the appropriate commands (insertOrderedList or insertUnorderedList) based on the button clicked.
The tutorial explains the importance of registering list nodes on the editor's initial configuration due to constraints in the current implementation.
The tutorial highlights the flexibility of Lexical React in building toolbars by composing plugins and components, encouraging exploration and finding custom solutions.
The setBlocksType helper allows wrapping a selected range in a specified block type by providing a creator function.
Styling is applied to the different heading levels using CSS to illustrate how custom styles can be added.
A separate toolbar plugin is created for the list functionality, which dispatches the appropriate commands (insertOrderedList or insertUnorderedList) based on the button clicked.
The tutorial explains the importance of registering list nodes on the editor's initial configuration due to constraints in the current implementation.
The tutorial highlights the flexibility of Lexical React in building toolbars by composing plugins and components, encouraging exploration and finding custom solutions.
Transcripts
hey everyone it's AC from the lexical
team uh today we're going to continue on
our tutorial for how to build an editor
in lexical react
um last time we left off with just kind
of a basic uh button that inserts a
heading node
um so it's kind of the beginnings of a
toolbar and a little bit of like how to
interact with the editor through the UI
So today we're going to expand the
toolbar a little more and try to like
work a little more with like a more I
guess interesting or like practical
application of something like headings
and then also maybe add support for
lists
um and and doing so kind of expand our
toolbar a bit and and work a little bit
with more with the UI so um let's Dive
In
so like I said last time we did set up
this uh heading plugin which does
something really it basically is
connected to this button
so we render a button here when we click
it we get the root element of the
lexical editor and we append um we
create a text node a pin that and create
a heading node a pin that to pin the
text onto the heading node and then
append the heading into the root so
that's a bit of a mouthful but that's
you know more or less we're just kind of
like creating a heading node so you
can't like
choose the text it's hard coded you
select the editor you click heading and
inserts hello world all right so that's
that's pretty much it and so what would
be like a more typical I suppose is um
for you to like highlight some text or
select some text and then be able to
convert that into a heading right or
convert that into uh change the block
type of it so to speak right
um and so uh let's try to do that first
right let's try to see if we can make it
so we can just instead of hard inserting
a hard-coded hello world heading we can
just select arbitrary text and convert
it into uh to a heading so that's step
one
um so
first of all let's get rid of this my
that's annoying me for some reason it's
just having plug-in for us
um so
what we can do first so let's go
explodes is a helper called set blocks
type
um and that basically is going to allow
you to pass in a selection and a Creator
function and it's going to try to wrap
your selection in whatever block type is
created by the function you provide and
I'll show you how that works in a sec
um so first let's get rid of this piece
we don't need this anymore instead of
getting the root we'll need to get this
selection
so the way you do that in lexical is
just with something similar to get root
it's called get selection
second thing you want to do is check if
it's a range selection there are three
types of selection in lexical right now
um grid selection
range selection and node selection and
for purposes of this we're just going to
want to deal with range selection that's
when you're selecting like a range of
nodes usually some text is involved and
it's kind of the most common one and you
usually have to special case the other
ones so we'll we'll just deal with the
range selection for now and if you want
to layer in node selection and grid
selection in your own editor that's
something that may be the subject of
future videos
so um check if it's a range selection
and by the way set blocks type only
works with range selection or maybe grid
selection so we have to do this check
anyway
um so set block stop experimental comes
from lexical selection
um I
didn't think I had that installed but I
interesting
it must be cash from earlier
um but anyway so yeah we have set block
types experiments so we're probably
going to want to like
actually go and make sure we have this
so obviously PM install
that
you'd have to do this typically to get
access to this utility
um
I was playing around earlier and I think
I
installed it and didn't clear out the
module cache
so that's probably why I'm able to
import it here
um
and what's actually happening is that
probably installed
I actually don't want it to be a
hard dependency let's make it a Dev
dependency
um
and then we need to all these versions
to match so I'm just going to downgrade
it to eight zero
like the rest of my electrical packages
for now
and then we'll just run that one more
time to make sure we have the right
version
uh what did I do
oh trailing comma
okay let's get rid of that
cool so that should work fine and then
we have it's still called in actually
0.9 is called set blocks type and we
dropped the experimental tag so this is
a stable API but on 0.8 which is the
version I'm using in this series so far
um it's just called set block type
experimental so that's why we have to do
that for now
um so we won't need these anymore
and we'll still need to create heading
notes what we're going to do with this
is the API here it takes you can see two
parameters selection element Creator as
I mentioned before so we can pass as
parameter one the selection which we got
from git selection and then we'll pass
the Creator function
which to mirror the functionality we
have now we just do create heading node
and we would say it's an H1
cool and so why are we complaining
indentation okay
better
what have I done
okay cool
um really need to work on my editor
environment this is like not
having to fix indentation manually is
not ideal anyway so um now what we
should expect to see let's refresh this
to make sure we got all the changes um
we're just going to see like this when
you click heading should be converted
into
and so that's kind of similar behaviors
before so let's change the text so my
name is AC
oops and then
Okay cool so that works so now instead
of like just inserting hard-coded text
we're just converting whatever is we
select into a heading so that's a good
step in the right direction
so if we're going to expand this out a
little more
um we'd probably want to support like
different heading types and this kind of
like leads us into how you could like
maybe start building a toolbar right so
right now we just have a single button
called heading
but like there are different types of
headings right so we could call this
instead like
H1 we could say well like that's now
gonna just insert an H1 and that would
imply that we'll add h2h3h4 whatever
right so let's add support for
um through H3 let's say
um so you can see here that we're
passing in the tag we want to create to
create heading node
so we just need like maybe for now a
couple more we could do a drop down or
something theoretically just to keep it
simple let's add a couple more buttons
We'll add H1 H2 and H3 and we'll kind of
continue to expand our toolbar here so
um one way we can do that is probably by
just saying like
let's add a wrapper around this
and then
let's have a
um
keeping it simple for now hardcoding
some things
we're just going to map these uh tags
to buttons
whoops did I like drop that button for
my clipboard somehow
I did great
um
so it's okay not hard to rewrite
um
so we have our click Handler
and then uh we had we need a key
actually
since we're
using map and then we'll have in here
tag Dot
two uppercase just like that for now
um and so then let's see we're not
really returning this are we let's do
this instead to make it return the
actual react element
um
and then we should be good yeah so we
have H1 h2h3 there but that's not really
doing it it's all going to insert it's
all going to call the same click Handler
right now right so it's all gonna um
to insert an H1 so we need to kind of
adapt that click Handler a bit so what
we can do here instead is maybe say like
um
we have a tag
which is
um
something like this that
and then we'll pass the tag in here
and then we'll have to change this
because this will just typically take
the event
instead of doing that
we'll call it on click
and we'll pass in the tech
cool
um
that should work
fine unless I'm missing something
so H1 gives us that H2 H3 and this is
just like user agent Styles right so we
have additional styling applied to H1
from the last video
where we added we're just kind of
playing around with classes so we added
a weird background color to H1
um so we could do
um you know we could have
instead of relying on user agent Styles
we can add our own here right we can say
uh glyph editor H2 and probably
H3 would be glyph
editor H3
and then
make sure we have a comma
go to the styles
so this will be H2 this would be h
three
um let's go to something like let's say
we'll set we can control the font sizes
right
so maybe this font size is going to be
like
um I don't know
let's say
48 pixels would be hard
and you might want to use like
em or something in real life so don't
don't take this as like
best practices this is just me
giving a few examples
um so
uh now we have that maybe let's say just
to make it interesting let's say like we
want to
at an underline to our H1s
and our H2S would be like what's
something we could do to this like maybe
these are
uh italic
which
um
it's escaping me how you do that now uh
hmm why am I forgetting how to add
italic
uh
styles to text anyway
um so let's say like maybe instead of
that
um we'll do font weight
people
um and maybe we want that here too so
this will be bold and underlined
this will be just bold and this will be
just like a
regular regular text so now H1 becomes
underlined
H2 becomes just bold
H3 becomes
so wait a second
hmm
so let's go H1 H2 H3 so that that's like
can't really tell that it's applying rh2
Styles so we can add a color of like
[Music]
um
let's go green
so
we should see that update
unless I missed something somewhere
of course but that's not the problem
um so
why is there two not
with editor H2 oh
you're not allowed to just say green I
suppose
right no quotes
not bad
there we go much better
now we have green green H2S and uh
regular size regular colored h3s
underlines on our H1s
so yeah you can kind of add whatever
Styles you want to your headings you can
make them do whatever you want that's
just kind of a just a quick example
um so let's talk about how we could do
something like let me fix this type
script error first so I probably wanted
to do something like
uh
uh
so
heading tag and then I would just do
like
um
probably
could even make this configurable right
um
through a prop but I'll say constant
heading tags
this type
whoops
hang tag array
cool so that'll make task group quiet
um
right so now we have these buttons so
now let's try to see if we can add lists
really quick and so we'll just expand
our toolbar out and add a couple more
buttons
um owner to listen order list
um
conversion I guess that'll be a little
bit different we can't use set block
types for that because lists are a
little more complicated that's why we
have the list the list package
and plug in for that
um and so the first thing we'll need to
do there is ADD
um add that package so we can just
improve install
um I think that's how you do that oh
it's just dead now right and then
um at let's go
slash list and this is just going to
give us access to some of the thing like
the nodes and things that we need in
order to actually use a list plugin so
we expose the list plugin from lexical
react but you still need to register the
nodes and things like that to support it
um
and so we'll need to
um and you have to access the commands
and things all that against is Alex list
package
and so once we have that
um we will need to oh we will need to
make sure we have the same
why I thought I said Dev
okay
so let's go here
and let's go to eight
and then
just run and install and then I'm gonna
restart this Dev server because
just to make sure we get all the
new node modules picked up
cool
um so that looks
right now that we have a list so we can
add we now we can try adding
list plugin here
and that comes from the lexical react
package as you can see uh and so let's
look at what that does for a sec
so if we go to list plugin
Maybe
here we go we can look at the so we
actually ship like Source Maps or source
files with the npm package so you can
actually see
in development in the node modules like
what's going on but uh here you can see
this is the list plugin right
so there's subtracting over a lot of
things but like the public API here is
we're just
um we're calling use list in here
um and so
that is just registering a bunch of
commands effectively so what that does
is it makes it so you can dispatch these
commands in the editor and it'll do
certain things like insert a number list
insert a bullet list you know remove a
list whatever right so all you have to
do is dispatch those commands and that's
sort of the what the plugin provides out
of the box
so um
if we go here
uh let's see how do we want to do this
we're going to need a button to like
dispatch the commands right we have our
heading plugin we don't want to add
lists to that right because then it
wouldn't be having plug-in anymore so
I'm thinking what we do is we add
a toolbar plugin
and then from here we would say like we
probably still want to have the editor
um and then we would want to return so
you can kind of compose plugins which I
think is
kind of cool but let's just do maybe
let's make this a div so we can style it
because I got a few of them we're gonna
need to
and then
um we can return heading plugin here
and then maybe
um we'll make a list
toolbar plugin
let's rename this so it's consistent
so now we have heading toolbar plugin
you could probably rename this
later but so we won't need this in here
anymore we just need toolbar plugin
and then we can have list toolbar plugin
probably just copy this
so let's make this list toolbar plugin
and so
um
we're gonna need the click Handler still
um
actually we could still use tags right
because
uh list
still
the list uh
still uh plugins sorry
the list yeah list plugins still
distinguish between those two right you
can have or UL so we can kind of use
tags to distinguish between those two
um we'll need to do something different
in here right we can't use set blocks
type we don't really care about
selection
we don't even need to care about
editor.update because when you dispatch
a command it's implicitly wrapped in an
update
um and if there's not already one
ongoing so we can uh kind of we don't
even have to worry about that really and
we can say I was hoping that we could
we'll come back to this
um piece for now let's not do that
um I think we might export the tag types
from the list package
um let me see list tag type no
um for now let's go uh just oh well
UL like that and we'll fix it later so
um we'll get to the interesting part
so if uh tag
triple equals ol
then we want to do editor dot dispatch
commands insert order list
and you do have to like add the second
argument even if you um
even if you don't uh plan to
even if it isn't it's just undefined
which it is in this case right with this
touch dispatch command with the install
order list command um the payload type
is void
um but you have to add it anyway we
should probably do something about that
but
not right now so return
and then um if you if it's not then we
would do dispatch command we would do
insert unorderless command
like that so that becomes our click and
then we just have
list tags.map
and then
um
I guess we don't need
that
this returns a jsx element
um
and I'm missing something
um
list tags oh we have to do the same
thing we did before
so that's fine let's do type
list tag
s below
or UL
and this would be a list tag array
and this would be a list tag right
um
oh sorry not an array just a list tag
um and that should
work for some reason we still see
console areas where we have
list plugin list node and or List item
node not registered on editor okay good
point so we need to register those on
the editor because we're using them in
list plugin it's not super obvious that
you have to do that that's why we have
this console error in here ideally
there'd be a way for the plugin to
register the node itself but there are
some constraints we have right now that
we haven't been able to work through yet
that don't allow you to register the
node on the plugin itself you have to do
it when you create the editor in the
initial config and so that kind of is a
little bit weird it mixes some concerns
um once again would love to fix that but
for now you need to
import these and register them on the
editor so you have a list noted on this
side of the node right
and then that should fix our errors I
think
um but we have a weird thing here where
our styling's messed up
so let's see if we can sort that out
real quick
um
let's go with this probably just needs
to be
a fragment
for now and then this can have a class
name
of a toolbar
wrapper
cannot type this morning
toolbar wrapper and then
um
from there you can do
um
two of our wrapper and then you can have
[Music]
um
just display Flex
cool and so that fixes us up now there
so now we just have like a toolbar with
display Flex that's kind of
accommodating the width of all of the
child elements
um so that's pretty straightforward
there so now what we would expect is
that when we
um you know now that we have these
commands wired up we can change print
all these different formats right so I
would say hello we have H1 h2h3 working
ol
UL
still not quite working
um
I think I might know why that is
sometimes when you install these
packages
so like we basically rely on object
references for the command creation
I'd like to like detect commands so like
you're not you're not like
um matching a string when we decide
which command listeners to trigger
you're actually matching an object
reference so it's like a shared object
reference across these modules and that
can actually be a little bit weird
um sometimes when you're installing
packages because you can end up having
different object references for the
command identity and so like this insert
ordered list command is actually an
object and so when we decide you know
when we dispatch this command and let's
go is like okay we need to trigger you
know any command list associated with
this command it's looking for this exact
object reference in the command listener
map that we have and so if it's two
different references obviously they
won't match and you won't run any
listeners and so I suspect that's what's
happening here because I've done some
weird things with my um
with my node modules
um and it's like installing different
versions and things so I'm going to just
get rid of that
um let's get rid of package lock as well
and let's just do a fresh install
Okay cool so we have that package
installed now
so let's
start the dev server back up
so on a fresh install this should work
okay so we're good now so we're able to
convert between these different types at
this point and so you should also be
able to like if we have separate
paragraphs
making both headings convert them both
to list right so all this should work
just out of the box and it does work
pretty well so um that's pretty much it
there I'm not sure why am I still seeing
type errors probably because
the ID hasn't caught up with the with
the reinstall
um
so let me try reopening this file
still nothing
she's a bit weird
so I might have to restart vs code to
get that to work probably but anyway
that's in a nutshell like how you would
set these things up
um in lexical and um
you know you have like your headings
list you have kind of the beginnings of
a toolbar here and I mean there are many
many different ways to do a toolbar in
lexical it's just as flexible as
reactive is really like all you're doing
is um
you know accessing the editor and like
dispatching commands or like calling
editor updates or whatever with some
buttons so how you do it's really up to
you like the one way to do it is just to
have a toolbar plug-in in the composer
and then have like you know kind of
compose little pieces of that UI through
other plugins plugins are totally
composable and then just like react
components and so that's a good way to
go at it but like I said there are many
ways to do this you can check out the
playground
um on you know our GitHub uh repo for
examples for our example of how to do it
but they're like once again many many
ways to do this so encourage you to uh
you know explore and find your own
Solutions
um but anyways I hope this was helpful
and we'll continue in a future video and
maybe work on some more interesting uh
and exciting use cases for like school
thank you
5.0 / 5 (0 votes)