Skip to content

Commit 7d2022f

Browse files
authored
feat(richtext-lexical)!: dropdown menu disabled status (#8177)
**Breaking change**: ToolbarDropdown no longer receives `groupKey={group.key}` and `items={group.items}` as props, but instead `group={group}` ___ Similar to #8159, but in this case it allows you to disable an entire dropdown menu, not just individual items in the dropdown. This adds a new property to `ToolbarGroup` when used with `type: 'dropdown'`. For example, if you add `isEnabled: () => false,` inside `packages/richtext-lexical/src/features/shared/toolbar/textDropdownGroup.ts` and run `pnpm dev fields`, this is what you'll see in the Lexical editor: ![image](https://github.com/user-attachments/assets/4efe2e92-2e78-473f-8c97-0995e3d44671)
1 parent 493b121 commit 7d2022f

File tree

6 files changed

+71
-60
lines changed

6 files changed

+71
-60
lines changed

packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,8 @@ function ToolbarGroupComponent({
110110
<ToolbarDropdown
111111
anchorElem={anchorElem}
112112
editor={editor}
113-
groupKey={group.key}
113+
group={group}
114114
Icon={DropdownIcon}
115-
items={group.items}
116115
itemsContainerClassNames={['fixed-toolbar__dropdown-items']}
117116
label={dropdownLabel}
118117
maxActiveItems={1}
@@ -122,8 +121,7 @@ function ToolbarGroupComponent({
122121
<ToolbarDropdown
123122
anchorElem={anchorElem}
124123
editor={editor}
125-
groupKey={group.key}
126-
items={group.items}
124+
group={group}
127125
itemsContainerClassNames={['fixed-toolbar__dropdown-items']}
128126
label={dropdownLabel}
129127
maxActiveItems={1}

packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,16 @@ function ToolbarGroupComponent({
101101
<ToolbarDropdown
102102
anchorElem={anchorElem}
103103
editor={editor}
104-
groupKey={group.key}
104+
group={group}
105105
Icon={DropdownIcon}
106-
items={group.items}
107106
maxActiveItems={1}
108107
onActiveChange={onActiveChange}
109108
/>
110109
) : (
111110
<ToolbarDropdown
112111
anchorElem={anchorElem}
113112
editor={editor}
114-
groupKey={group.key}
115-
items={group.items}
113+
group={group}
116114
maxActiveItems={1}
117115
onActiveChange={onActiveChange}
118116
/>

packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
margin-right: 2px;
1919
}
2020

21-
&:hover:not([disabled]) {
21+
&:hover:not(.disabled) {
2222
background-color: var(--theme-elevation-100);
2323
}
2424

packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.scss

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323

2424
&:disabled {
2525
cursor: not-allowed;
26-
27-
.icon {
28-
opacity: 0.2;
29-
}
26+
opacity: 0.2;
3027
}
3128

3229
&:hover:not([disabled]) {

packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { mergeRegister } from '@lexical/utils'
99
import { useTranslation } from '@payloadcms/ui'
1010
import { $getSelection } from 'lexical'
1111

12-
import type { ToolbarGroupItem } from '../../types.js'
12+
import type { ToolbarDropdownGroup, ToolbarGroupItem } from '../../types.js'
1313

1414
import { useEditorConfigContext } from '../../../../lexical/config/client/EditorConfigProvider.js'
1515
import { DropDown, DropDownItem } from './DropDown.js'
@@ -70,9 +70,8 @@ export const ToolbarDropdown = ({
7070
anchorElem,
7171
classNames,
7272
editor,
73-
groupKey,
73+
group,
7474
Icon,
75-
items,
7675
itemsContainerClassNames,
7776
label,
7877
maxActiveItems,
@@ -81,9 +80,8 @@ export const ToolbarDropdown = ({
8180
anchorElem: HTMLElement
8281
classNames?: string[]
8382
editor: LexicalEditor
84-
groupKey: string
83+
group: ToolbarDropdownGroup
8584
Icon?: React.FC
86-
items: ToolbarGroupItem[]
8785
itemsContainerClassNames?: string[]
8886
label?: string
8987
/**
@@ -95,7 +93,9 @@ export const ToolbarDropdown = ({
9593
}) => {
9694
const [activeItemKeys, setActiveItemKeys] = React.useState<string[]>([])
9795
const [enabledItemKeys, setEnabledItemKeys] = React.useState<string[]>([])
96+
const [enabledGroup, setEnabledGroup] = React.useState<boolean>(true)
9897
const editorConfigContext = useEditorConfigContext()
98+
const { items, key: groupKey } = group
9999

100100
const updateStates = useCallback(() => {
101101
editor.getEditorState().read(() => {
@@ -125,14 +125,17 @@ export const ToolbarDropdown = ({
125125
_enabledItemKeys.push(item.key)
126126
}
127127
}
128+
if (group.isEnabled) {
129+
setEnabledGroup(group.isEnabled({ editor, editorConfigContext, selection }))
130+
}
128131
setActiveItemKeys(_activeItemKeys)
129132
setEnabledItemKeys(_enabledItemKeys)
130133

131134
if (onActiveChange) {
132135
onActiveChange({ activeItems: _activeItems })
133136
}
134137
})
135-
}, [editor, editorConfigContext, items, maxActiveItems, onActiveChange])
138+
}, [editor, editorConfigContext, group, items, maxActiveItems, onActiveChange])
136139

137140
useEffect(() => {
138141
updateStates()
@@ -152,6 +155,7 @@ export const ToolbarDropdown = ({
152155
buttonClassName={[baseClass, `${baseClass}-${groupKey}`, ...(classNames || [])]
153156
.filter(Boolean)
154157
.join(' ')}
158+
disabled={!enabledGroup}
155159
Icon={Icon}
156160
itemsContainerClassNames={[`${baseClass}-items`, ...(itemsContainerClassNames || [])]}
157161
key={groupKey}

packages/richtext-lexical/src/features/toolbars/types.ts

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,61 @@ import type React from 'react'
44

55
import type { EditorConfigContextType } from '../../lexical/config/client/EditorConfigProvider.js'
66

7-
export type ToolbarGroup =
8-
| {
9-
/**
10-
* All toolbar items part of this toolbar group need to be added here.
11-
*/
12-
items: Array<ToolbarGroupItem>
13-
/**
14-
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
15-
*/
16-
key: string
17-
/**
18-
* Determines where the toolbar group will be.
19-
*/
20-
order?: number
21-
/**
22-
* Controls the toolbar group type. Set to `buttons` to create a buttons toolbar group, which displays toolbar items horizontally using only their icons.
23-
*/
24-
type: 'buttons'
25-
}
26-
| {
27-
/**
28-
* The dropdown toolbar ChildComponent allows you to pass in a React Component which will be displayed within the dropdown button.
29-
*/
30-
ChildComponent?: React.FC
31-
/**
32-
* All toolbar items part of this toolbar group need to be added here.
33-
*/
34-
items: Array<ToolbarGroupItem>
35-
/**
36-
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
37-
*/
38-
key: string
39-
/**
40-
* Determines where the toolbar group will be.
41-
*/
42-
order?: number
43-
/**
44-
* Controls the toolbar group type. Set to `dropdown` to create a buttons toolbar group, which displays toolbar items vertically using their icons and labels, if the dropdown is open.
45-
*/
46-
type: 'dropdown'
47-
}
7+
export type ToolbarGroup = ToolbarButtonsGroup | ToolbarDropdownGroup
8+
9+
export type ToolbarDropdownGroup = {
10+
/**
11+
* The dropdown toolbar ChildComponent allows you to pass in a React Component which will be displayed within the dropdown button.
12+
*/
13+
ChildComponent?: React.FC
14+
/**
15+
* This is optional and controls if the toolbar group is highlighted or not.
16+
*/
17+
isEnabled?: ({
18+
editor,
19+
editorConfigContext,
20+
selection,
21+
}: {
22+
editor: LexicalEditor
23+
editorConfigContext: EditorConfigContextType
24+
selection: BaseSelection
25+
}) => boolean
26+
/**
27+
* All toolbar items part of this toolbar group need to be added here.
28+
*/
29+
items: Array<ToolbarGroupItem>
30+
/**
31+
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
32+
*/
33+
key: string
34+
/**
35+
* Determines where the toolbar group will be.
36+
*/
37+
order?: number
38+
/**
39+
* Controls the toolbar group type. Set to `dropdown` to create a buttons toolbar group, which displays toolbar items vertically using their icons and labels, if the dropdown is open.
40+
*/
41+
type: 'dropdown'
42+
}
43+
44+
export type ToolbarButtonsGroup = {
45+
/**
46+
* All toolbar items part of this toolbar group need to be added here.
47+
*/
48+
items: Array<ToolbarGroupItem>
49+
/**
50+
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
51+
*/
52+
key: string
53+
/**
54+
* Determines where the toolbar group will be.
55+
*/
56+
order?: number
57+
/**
58+
* Controls the toolbar group type. Set to `buttons` to create a buttons toolbar group, which displays toolbar items horizontally using only their icons.
59+
*/
60+
type: 'buttons'
61+
}
4862

4963
export type ToolbarGroupItem = {
5064
/** A React component which is rendered within your toolbar item's default button component. Usually, you want this to be an icon. */

0 commit comments

Comments
 (0)