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)
This commit is contained in:
Germán Jabloñski
2024-09-20 16:55:34 -03:00
committed by GitHub
parent 493b121ae8
commit 7d2022f28b
6 changed files with 71 additions and 60 deletions

View File

@@ -110,9 +110,8 @@ function ToolbarGroupComponent({
<ToolbarDropdown <ToolbarDropdown
anchorElem={anchorElem} anchorElem={anchorElem}
editor={editor} editor={editor}
groupKey={group.key} group={group}
Icon={DropdownIcon} Icon={DropdownIcon}
items={group.items}
itemsContainerClassNames={['fixed-toolbar__dropdown-items']} itemsContainerClassNames={['fixed-toolbar__dropdown-items']}
label={dropdownLabel} label={dropdownLabel}
maxActiveItems={1} maxActiveItems={1}
@@ -122,8 +121,7 @@ function ToolbarGroupComponent({
<ToolbarDropdown <ToolbarDropdown
anchorElem={anchorElem} anchorElem={anchorElem}
editor={editor} editor={editor}
groupKey={group.key} group={group}
items={group.items}
itemsContainerClassNames={['fixed-toolbar__dropdown-items']} itemsContainerClassNames={['fixed-toolbar__dropdown-items']}
label={dropdownLabel} label={dropdownLabel}
maxActiveItems={1} maxActiveItems={1}

View File

@@ -101,9 +101,8 @@ function ToolbarGroupComponent({
<ToolbarDropdown <ToolbarDropdown
anchorElem={anchorElem} anchorElem={anchorElem}
editor={editor} editor={editor}
groupKey={group.key} group={group}
Icon={DropdownIcon} Icon={DropdownIcon}
items={group.items}
maxActiveItems={1} maxActiveItems={1}
onActiveChange={onActiveChange} onActiveChange={onActiveChange}
/> />
@@ -111,8 +110,7 @@ function ToolbarGroupComponent({
<ToolbarDropdown <ToolbarDropdown
anchorElem={anchorElem} anchorElem={anchorElem}
editor={editor} editor={editor}
groupKey={group.key} group={group}
items={group.items}
maxActiveItems={1} maxActiveItems={1}
onActiveChange={onActiveChange} onActiveChange={onActiveChange}
/> />

View File

@@ -18,7 +18,7 @@
margin-right: 2px; margin-right: 2px;
} }
&:hover:not([disabled]) { &:hover:not(.disabled) {
background-color: var(--theme-elevation-100); background-color: var(--theme-elevation-100);
} }

View File

@@ -23,10 +23,7 @@
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.2;
.icon {
opacity: 0.2;
}
} }
&:hover:not([disabled]) { &:hover:not([disabled]) {

View File

@@ -9,7 +9,7 @@ import { mergeRegister } from '@lexical/utils'
import { useTranslation } from '@payloadcms/ui' import { useTranslation } from '@payloadcms/ui'
import { $getSelection } from 'lexical' import { $getSelection } from 'lexical'
import type { ToolbarGroupItem } from '../../types.js' import type { ToolbarDropdownGroup, ToolbarGroupItem } from '../../types.js'
import { useEditorConfigContext } from '../../../../lexical/config/client/EditorConfigProvider.js' import { useEditorConfigContext } from '../../../../lexical/config/client/EditorConfigProvider.js'
import { DropDown, DropDownItem } from './DropDown.js' import { DropDown, DropDownItem } from './DropDown.js'
@@ -70,9 +70,8 @@ export const ToolbarDropdown = ({
anchorElem, anchorElem,
classNames, classNames,
editor, editor,
groupKey, group,
Icon, Icon,
items,
itemsContainerClassNames, itemsContainerClassNames,
label, label,
maxActiveItems, maxActiveItems,
@@ -81,9 +80,8 @@ export const ToolbarDropdown = ({
anchorElem: HTMLElement anchorElem: HTMLElement
classNames?: string[] classNames?: string[]
editor: LexicalEditor editor: LexicalEditor
groupKey: string group: ToolbarDropdownGroup
Icon?: React.FC Icon?: React.FC
items: ToolbarGroupItem[]
itemsContainerClassNames?: string[] itemsContainerClassNames?: string[]
label?: string label?: string
/** /**
@@ -95,7 +93,9 @@ export const ToolbarDropdown = ({
}) => { }) => {
const [activeItemKeys, setActiveItemKeys] = React.useState<string[]>([]) const [activeItemKeys, setActiveItemKeys] = React.useState<string[]>([])
const [enabledItemKeys, setEnabledItemKeys] = React.useState<string[]>([]) const [enabledItemKeys, setEnabledItemKeys] = React.useState<string[]>([])
const [enabledGroup, setEnabledGroup] = React.useState<boolean>(true)
const editorConfigContext = useEditorConfigContext() const editorConfigContext = useEditorConfigContext()
const { items, key: groupKey } = group
const updateStates = useCallback(() => { const updateStates = useCallback(() => {
editor.getEditorState().read(() => { editor.getEditorState().read(() => {
@@ -125,6 +125,9 @@ export const ToolbarDropdown = ({
_enabledItemKeys.push(item.key) _enabledItemKeys.push(item.key)
} }
} }
if (group.isEnabled) {
setEnabledGroup(group.isEnabled({ editor, editorConfigContext, selection }))
}
setActiveItemKeys(_activeItemKeys) setActiveItemKeys(_activeItemKeys)
setEnabledItemKeys(_enabledItemKeys) setEnabledItemKeys(_enabledItemKeys)
@@ -132,7 +135,7 @@ export const ToolbarDropdown = ({
onActiveChange({ activeItems: _activeItems }) onActiveChange({ activeItems: _activeItems })
} }
}) })
}, [editor, editorConfigContext, items, maxActiveItems, onActiveChange]) }, [editor, editorConfigContext, group, items, maxActiveItems, onActiveChange])
useEffect(() => { useEffect(() => {
updateStates() updateStates()
@@ -152,6 +155,7 @@ export const ToolbarDropdown = ({
buttonClassName={[baseClass, `${baseClass}-${groupKey}`, ...(classNames || [])] buttonClassName={[baseClass, `${baseClass}-${groupKey}`, ...(classNames || [])]
.filter(Boolean) .filter(Boolean)
.join(' ')} .join(' ')}
disabled={!enabledGroup}
Icon={Icon} Icon={Icon}
itemsContainerClassNames={[`${baseClass}-items`, ...(itemsContainerClassNames || [])]} itemsContainerClassNames={[`${baseClass}-items`, ...(itemsContainerClassNames || [])]}
key={groupKey} key={groupKey}

View File

@@ -4,47 +4,61 @@ import type React from 'react'
import type { EditorConfigContextType } from '../../lexical/config/client/EditorConfigProvider.js' import type { EditorConfigContextType } from '../../lexical/config/client/EditorConfigProvider.js'
export type ToolbarGroup = export type ToolbarGroup = ToolbarButtonsGroup | ToolbarDropdownGroup
| {
/** export type ToolbarDropdownGroup = {
* All toolbar items part of this toolbar group need to be added here. /**
*/ * The dropdown toolbar ChildComponent allows you to pass in a React Component which will be displayed within the dropdown button.
items: Array<ToolbarGroupItem> */
/** ChildComponent?: React.FC
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together. /**
*/ * This is optional and controls if the toolbar group is highlighted or not.
key: string */
/** isEnabled?: ({
* Determines where the toolbar group will be. editor,
*/ editorConfigContext,
order?: number selection,
/** }: {
* Controls the toolbar group type. Set to `buttons` to create a buttons toolbar group, which displays toolbar items horizontally using only their icons. editor: LexicalEditor
*/ editorConfigContext: EditorConfigContextType
type: 'buttons' selection: BaseSelection
} }) => boolean
| { /**
/** * All toolbar items part of this toolbar group need to be added here.
* The dropdown toolbar ChildComponent allows you to pass in a React Component which will be displayed within the dropdown button. */
*/ items: Array<ToolbarGroupItem>
ChildComponent?: React.FC /**
/** * Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
* All toolbar items part of this toolbar group need to be added here. */
*/ key: string
items: Array<ToolbarGroupItem> /**
/** * Determines where the toolbar group will be.
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together. */
*/ order?: number
key: string /**
/** * 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.
* Determines where the toolbar group will be. */
*/ type: 'dropdown'
order?: number }
/**
* 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. export type ToolbarButtonsGroup = {
*/ /**
type: 'dropdown' * All toolbar items part of this toolbar group need to be added here.
} */
items: Array<ToolbarGroupItem>
/**
* Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together.
*/
key: string
/**
* Determines where the toolbar group will be.
*/
order?: number
/**
* Controls the toolbar group type. Set to `buttons` to create a buttons toolbar group, which displays toolbar items horizontally using only their icons.
*/
type: 'buttons'
}
export type ToolbarGroupItem = { export type ToolbarGroupItem = {
/** A React component which is rendered within your toolbar item's default button component. Usually, you want this to be an icon. */ /** A React component which is rendered within your toolbar item's default button component. Usually, you want this to be an icon. */