Skip to content
This repository was archived by the owner on Mar 21, 2025. It is now read-only.

Commit 5bd37ed

Browse files
author
Andy Wilson
committed
fix: use dropdown ui element for align plugin dropdown
1 parent 94f139e commit 5bd37ed

File tree

2 files changed

+94
-76
lines changed

2 files changed

+94
-76
lines changed

packages/lexical/src/plugins/AlignPlugin.tsx

+80-66
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import {
1717
} from '@techstack/react-feather';
1818
import { $findMatchingParent, mergeRegister } from '@lexical/utils';
1919
import { $isLinkNode } from '@lexical/link';
20+
import { $isParentElementRTL } from '@lexical/selection';
2021

2122
import { LowPriority } from '../utils/priorities';
2223
import getSelectedNode from '../utils/getSelectedNode';
24+
import DropDown, { DropDownItem } from '../ui/Dropdown';
2325

2426
const ELEMENT_FORMAT_OPTIONS: {
2527
[key in Exclude<ElementFormatType, ''>]: {
@@ -62,9 +64,8 @@ const ELEMENT_FORMAT_OPTIONS: {
6264

6365
const ListPlugin = () => {
6466
const [editor] = useLexicalComposerContext();
65-
const dropDownRef = useRef<HTMLDivElement>(null);
67+
const [isRTL, setIsRTL] = useState(false);
6668

67-
const [showListOptionsDropdown, setShowListOptionsDropdown] = useState(false);
6869
const [elementFormat, setElementFormat] = useState<ElementFormatType>('left');
6970

7071
const formatOption = ELEMENT_FORMAT_OPTIONS[elementFormat || 'left'];
@@ -84,6 +85,7 @@ const ListPlugin = () => {
8485
);
8586
}
8687

88+
setIsRTL($isParentElementRTL(selection));
8789
setElementFormat(
8890
$isElementNode(matchingParent)
8991
? matchingParent.getFormatType()
@@ -115,72 +117,84 @@ const ListPlugin = () => {
115117
);
116118

117119
return (
118-
<>
119-
<button
120-
className='toolbar-item block-controls'
121-
type='button'
122-
onClick={() => setShowListOptionsDropdown(prevState => !prevState)}
123-
aria-label='Formatting Options'
120+
<DropDown buttonIcon={formatOption.icon} buttonLabel={formatOption.name}>
121+
<DropDownItem
122+
onClick={() => {
123+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left');
124+
}}
124125
>
125-
<span className={`icon`}>{formatOption.icon}</span>
126-
<span className='text'>{formatOption.name}</span>
127-
<i className='chevron-down'>
128-
<ChevronDown />
126+
<i className='icon'>
127+
<AlignLeft />
129128
</i>
130-
</button>
131-
{showListOptionsDropdown && (
132-
<div className='toolbar-dropdown' ref={dropDownRef}>
133-
<button
134-
type='button'
135-
onClick={() => {
136-
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left');
137-
}}
138-
className='toolbar-item spaced'
139-
aria-label='Left Align'
140-
>
141-
<i className='format left-align'>
142-
<AlignLeft size={14} />
143-
</i>
144-
</button>
145-
<button
146-
type='button'
147-
onClick={() => {
148-
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center');
149-
}}
150-
className='toolbar-item spaced'
151-
aria-label='Center Align'
152-
>
153-
<i className='format center-align'>
154-
<AlignCenter size={14} />
155-
</i>
156-
</button>
157-
<button
158-
type='button'
159-
onClick={() => {
160-
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right');
161-
}}
162-
className='toolbar-item spaced'
163-
aria-label='Right Align'
164-
>
165-
<i className='format right-align'>
166-
<AlignRight size={14} />
167-
</i>
168-
</button>
169-
<button
170-
type='button'
171-
onClick={() => {
172-
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify');
173-
}}
174-
className='toolbar-item'
175-
aria-label='Justify Align'
176-
>
177-
<i className='format justify-align'>
178-
<AlignJustify size={14} />
179-
</i>
180-
</button>
181-
</div>
182-
)}
183-
</>
129+
<span className="text">
130+
Align Left
131+
</span>
132+
</DropDownItem>
133+
<DropDownItem
134+
onClick={() => {
135+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center');
136+
}}
137+
>
138+
<i className='icon'>
139+
<AlignCenter />
140+
</i>
141+
<span className="text">
142+
Align Center
143+
</span>
144+
</DropDownItem>
145+
<DropDownItem
146+
onClick={() => {
147+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right');
148+
}}
149+
>
150+
<i className='icon'>
151+
<AlignRight />
152+
</i>
153+
<span className="text">
154+
Align Right
155+
</span>
156+
</DropDownItem>
157+
<DropDownItem
158+
onClick={() => {
159+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify');
160+
}}
161+
>
162+
<i className='icon'>
163+
<AlignJustify />
164+
</i>
165+
<span className="text">
166+
Align Justify
167+
</span>
168+
</DropDownItem>
169+
<DropDownItem
170+
onClick={() => {
171+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'start');
172+
}}
173+
>
174+
<i className='icon'>
175+
{isRTL
176+
? ELEMENT_FORMAT_OPTIONS.start.iconRTL
177+
: ELEMENT_FORMAT_OPTIONS.start.icon}
178+
</i>
179+
<span className="text">
180+
Align Start
181+
</span>
182+
</DropDownItem>
183+
<DropDownItem
184+
onClick={() => {
185+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'end');
186+
}}
187+
>
188+
<i className='icon'>
189+
{isRTL
190+
? ELEMENT_FORMAT_OPTIONS.end.iconRTL
191+
: ELEMENT_FORMAT_OPTIONS.end.icon}
192+
</i>
193+
<span className="text">
194+
Align End
195+
</span>
196+
</DropDownItem>
197+
</DropDown>
184198
);
185199
};
186200
export default ListPlugin;

packages/lexical/src/ui/Dropdown.tsx

+14-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
*/
88

9-
import React, {
9+
import {
1010
createContext,
1111
ReactNode,
1212
RefObject,
@@ -16,6 +16,10 @@ import React, {
1616
useMemo,
1717
useRef,
1818
useState,
19+
JSX,
20+
MouseEvent as ReactMouseEvent,
21+
Ref,
22+
KeyboardEvent,
1923
} from 'react';
2024
import { ChevronDown } from '@techstack/react-feather';
2125

@@ -29,13 +33,13 @@ const dropDownPadding = 4;
2933

3034
export function DropDownItem({
3135
children,
32-
className,
36+
className = 'item',
3337
onClick,
3438
title,
3539
}: {
3640
children: ReactNode;
37-
className: string;
38-
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
41+
className?: string;
42+
onClick: (event: ReactMouseEvent<HTMLButtonElement>) => void;
3943
title?: string;
4044
}) {
4145
const ref = useRef<HTMLButtonElement>(null);
@@ -72,22 +76,22 @@ function DropDownItems({
7276
dropDownRef,
7377
onClose,
7478
}: {
75-
children: React.ReactNode;
76-
dropDownRef: React.Ref<HTMLDivElement>;
79+
children: ReactNode;
80+
dropDownRef: Ref<HTMLDivElement>;
7781
onClose: () => void;
7882
}) {
7983
const [items, setItems] = useState<React.RefObject<HTMLButtonElement>[]>();
8084
const [highlightedItem, setHighlightedItem] =
81-
useState<React.RefObject<HTMLButtonElement>>();
85+
useState<RefObject<HTMLButtonElement>>();
8286

8387
const registerItem = useCallback(
84-
(itemRef: React.RefObject<HTMLButtonElement>) => {
88+
(itemRef: RefObject<HTMLButtonElement>) => {
8589
setItems(prev => (prev ? [...prev, itemRef] : [itemRef]));
8690
},
8791
[setItems]
8892
);
8993

90-
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
94+
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
9195
if (!items) return;
9296

9397
const { key } = event;
@@ -155,7 +159,7 @@ export default function DropDown({
155159
}: {
156160
disabled?: boolean;
157161
buttonAriaLabel?: string;
158-
buttonClassName: string;
162+
buttonClassName?: string;
159163
buttonIconClassName?: string;
160164
buttonLabel?: string;
161165
children: ReactNode;

0 commit comments

Comments
 (0)