@strapi/content-manager 5.45.0 → 5.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/content-manager.js +26 -2
- package/dist/admin/content-manager.js.map +1 -1
- package/dist/admin/content-manager.mjs +26 -2
- package/dist/admin/content-manager.mjs.map +1 -1
- package/dist/admin/hooks/useContentTypeSchema.js +4 -1
- package/dist/admin/hooks/useContentTypeSchema.js.map +1 -1
- package/dist/admin/hooks/useContentTypeSchema.mjs +4 -1
- package/dist/admin/hooks/useContentTypeSchema.mjs.map +1 -1
- package/dist/admin/hooks/useDocumentLayout.js +67 -45
- package/dist/admin/hooks/useDocumentLayout.js.map +1 -1
- package/dist/admin/hooks/useDocumentLayout.mjs +67 -45
- package/dist/admin/hooks/useDocumentLayout.mjs.map +1 -1
- package/dist/admin/hooks/usePersistentQueryParams.js +4 -1
- package/dist/admin/hooks/usePersistentQueryParams.js.map +1 -1
- package/dist/admin/hooks/usePersistentQueryParams.mjs +4 -1
- package/dist/admin/hooks/usePersistentQueryParams.mjs.map +1 -1
- package/dist/admin/pages/ComponentConfigurationPage.js +7 -3
- package/dist/admin/pages/ComponentConfigurationPage.js.map +1 -1
- package/dist/admin/pages/ComponentConfigurationPage.mjs +7 -3
- package/dist/admin/pages/ComponentConfigurationPage.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.js +21 -4
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.mjs +19 -2
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.mjs +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.js +9 -6
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.mjs +10 -7
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksContent.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.js +1 -34
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.mjs +3 -35
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js +33 -18
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs +34 -19
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore.js +22 -0
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore.js.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore.mjs +20 -0
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore.mjs.map +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js +15 -4
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs +16 -5
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js +26 -4
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs +26 -4
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js +31 -0
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs +31 -0
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs.map +1 -1
- package/dist/admin/preview/components/InputPopover.js +3 -0
- package/dist/admin/preview/components/InputPopover.js.map +1 -1
- package/dist/admin/preview/components/InputPopover.mjs +3 -0
- package/dist/admin/preview/components/InputPopover.mjs.map +1 -1
- package/dist/admin/preview/hooks/usePreviewInputManager.js +24 -0
- package/dist/admin/preview/hooks/usePreviewInputManager.js.map +1 -1
- package/dist/admin/preview/hooks/usePreviewInputManager.mjs +24 -0
- package/dist/admin/preview/hooks/usePreviewInputManager.mjs.map +1 -1
- package/dist/admin/preview/utils/previewScript.js +616 -78
- package/dist/admin/preview/utils/previewScript.js.map +1 -1
- package/dist/admin/preview/utils/previewScript.mjs +616 -78
- package/dist/admin/preview/utils/previewScript.mjs.map +1 -1
- package/dist/admin/src/content-manager.d.ts +26 -0
- package/dist/admin/src/exports.d.ts +1 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.d.ts +14 -8
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore.d.ts +3 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCard.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +11 -1
- package/dist/server/homepage/services/homepage.js +12 -8
- package/dist/server/homepage/services/homepage.js.map +1 -1
- package/dist/server/homepage/services/homepage.mjs +12 -8
- package/dist/server/homepage/services/homepage.mjs.map +1 -1
- package/dist/server/services/metrics.js +1 -5
- package/dist/server/services/metrics.js.map +1 -1
- package/dist/server/services/metrics.mjs +1 -5
- package/dist/server/services/metrics.mjs.map +1 -1
- package/dist/server/src/homepage/services/homepage.d.ts.map +1 -1
- package/dist/server/src/services/metrics.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlocksEditor.js","sources":["../../../../../../../admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, type FieldValue, useIsMobile } from '@strapi/admin/strapi-admin';\nimport { IconButton, Divider, VisuallyHidden } from '@strapi/design-system';\nimport { Expand } from '@strapi/icons';\nimport { flushSync } from 'react-dom';\nimport { MessageDescriptor, useIntl } from 'react-intl';\nimport { Editor, type Descendant, createEditor, Transforms, Element } from 'slate';\nimport { withHistory } from 'slate-history';\nimport { type RenderElementProps, Slate, withReact, ReactEditor, useSlate } from 'slate-react';\nimport { styled, type CSSProperties } from 'styled-components';\n\nimport { getTranslation } from '../../../../../utils/translations';\n\nimport { codeBlocks } from './Blocks/Code';\nimport { headingBlocks } from './Blocks/Heading';\nimport { imageBlocks } from './Blocks/Image';\nimport { linkBlocks } from './Blocks/Link';\nimport { listBlocks } from './Blocks/List';\nimport { paragraphBlocks } from './Blocks/Paragraph';\nimport { quoteBlocks } from './Blocks/Quote';\nimport { BlocksContent, type BlocksContentProps } from './BlocksContent';\nimport { BlocksToolbar } from './BlocksToolbar';\nimport { EditorLayout } from './EditorLayout';\nimport { type ModifiersStore, modifiers } from './Modifiers';\nimport { withStrapiSchema } from './plugins/withStrapiSchema';\nimport { isNonNullable } from './utils/types';\n\nimport type { Schema } from '@strapi/types';\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditorProvider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface BaseBlock {\n renderElement: (props: RenderElementProps) => React.JSX.Element;\n /** Function to check if a given node is of this type of block */\n matchNode: (node: Schema.Attribute.BlocksNode) => boolean;\n handleConvert?: (editor: Editor) => void | (() => React.JSX.Element);\n handleEnterKey?: (editor: Editor) => void;\n handleBackspaceKey?: (editor: Editor, event: React.KeyboardEvent<HTMLElement>) => void;\n handleTab?: (editor: Editor) => void;\n handleShiftTab?: (editor: Editor) => void;\n snippets?: string[];\n /** Adjust the vertical positioning of the drag-to-reorder grip icon */\n dragHandleTopMargin?: CSSProperties['marginTop'];\n /** A Slate plugin: function that will wrap the editor creation */\n plugin?: (editor: Editor) => Editor;\n /**\n * Function that checks if an element should be draggable\n * @default () => true */\n isDraggable?: (element: Element) => boolean;\n}\n\ninterface NonSelectorBlock extends BaseBlock {\n isInBlocksSelector: false;\n}\n\ninterface SelectorBlock extends BaseBlock {\n isInBlocksSelector: true;\n icon: React.ComponentType;\n label: MessageDescriptor;\n}\n\ntype NonSelectorBlockKey = 'list-item' | 'link';\n\nconst selectorBlockKeys = [\n 'paragraph',\n 'heading-one',\n 'heading-two',\n 'heading-three',\n 'heading-four',\n 'heading-five',\n 'heading-six',\n 'list-ordered',\n 'list-unordered',\n 'image',\n 'quote',\n 'code',\n] as const;\n\ntype SelectorBlockKey = (typeof selectorBlockKeys)[number];\n\nconst isSelectorBlockKey = (key: unknown): key is SelectorBlockKey => {\n return typeof key === 'string' && selectorBlockKeys.includes(key as SelectorBlockKey);\n};\n\ntype BlocksStore = {\n [K in SelectorBlockKey]: SelectorBlock;\n} & {\n [K in NonSelectorBlockKey]: NonSelectorBlock;\n};\n\ninterface BlocksEditorContextValue {\n blocks: BlocksStore;\n modifiers: ModifiersStore;\n disabled: boolean;\n name: string;\n setLiveText: (text: string) => void;\n isExpandedMode: boolean;\n /** Push debounced Slate → form sync immediately (e.g. on Editable blur before Save). */\n flushPendingFormSync: () => void;\n}\n\nconst [BlocksEditorProvider, usePartialBlocksEditorContext] =\n createContext<BlocksEditorContextValue>('BlocksEditor');\n\nfunction useBlocksEditorContext(\n consumerName: string\n): BlocksEditorContextValue & { editor: Editor } {\n const context = usePartialBlocksEditorContext(consumerName, (state) => state);\n const editor = useSlate();\n\n return {\n ...context,\n editor,\n };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditor\n * -----------------------------------------------------------------------------------------------*/\n\nconst EditorDivider = styled(Divider)`\n background: ${({ theme }) => theme.colors.neutral200};\n`;\n\n/**\n * Forces an update of the Slate editor when the value prop changes from outside of Slate.\n * The root cause is that Slate is not a controlled component: https://github.com/ianstormtaylor/slate/issues/4612\n * Why not use JSON.stringify(value) as the key?\n * Because it would force a rerender of the entire editor every time the user types a character.\n * Why not use the entity id as the key, since it's unique for each locale?\n * Because it would not solve the problem when using the \"fill in from other locale\" feature\n */\nfunction useResetKey(value?: Schema.Attribute.BlocksValue): {\n key: number;\n incrementSlateUpdatesCount: () => void;\n} {\n // Keep track how many times Slate detected a change from a user interaction in the editor\n const slateUpdatesCount = React.useRef(0);\n // Keep track of how many times the value prop was updated, whether from within editor or from outside\n const valueUpdatesCount = React.useRef(0);\n // Use a key to force a rerender of the Slate editor when needed\n const [key, setKey] = React.useState(0);\n\n React.useEffect(() => {\n valueUpdatesCount.current += 1;\n\n // If the 2 refs are not equal, it means the value was updated from outside\n if (valueUpdatesCount.current !== slateUpdatesCount.current) {\n // So we change the key to force a rerender of the Slate editor,\n // which will pick up the new value through its initialValue prop\n setKey((previousKey) => previousKey + 1);\n\n // Then bring the 2 refs back in sync\n slateUpdatesCount.current = valueUpdatesCount.current;\n }\n }, [value]);\n\n const incrementSlateUpdatesCount = React.useCallback(() => {\n slateUpdatesCount.current += 1;\n }, []);\n\n return { key, incrementSlateUpdatesCount };\n}\n\nconst pipe =\n (...fns: ((baseEditor: Editor) => Editor)[]) =>\n (value: Editor) =>\n fns.reduce<Editor>((prev, fn) => fn(prev), value);\n\n/**\n * Normalize the blocks state to null if the editor state is considered empty,\n * otherwise return the state\n */\nconst normalizeBlocksState = (\n editor: Editor,\n value: Schema.Attribute.BlocksValue | Descendant[]\n): Schema.Attribute.BlocksValue | Descendant[] | null => {\n const isEmpty =\n value.length === 1 && Editor.isEmpty(editor, value[0] as Schema.Attribute.BlocksNode);\n\n return isEmpty ? null : value;\n};\n\ninterface BlocksEditorProps\n extends Pick<FieldValue<Schema.Attribute.BlocksValue>, 'onChange' | 'value' | 'error'>,\n BlocksContentProps {\n disabled?: boolean;\n name: string;\n}\n\nconst BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(\n ({ disabled = false, name, onChange, value, error, ...contentProps }, forwardedRef) => {\n const { formatMessage } = useIntl();\n const isMobile = useIsMobile();\n\n const blocks = React.useMemo(\n () => ({\n ...paragraphBlocks,\n ...headingBlocks,\n ...listBlocks,\n ...linkBlocks,\n ...imageBlocks,\n ...quoteBlocks,\n ...codeBlocks,\n }),\n []\n ) satisfies BlocksStore;\n\n const blockRegisteredPlugins = Object.values(blocks)\n .map((block) => block.plugin)\n .filter(isNonNullable);\n\n const [editor] = React.useState(() =>\n pipe(withHistory, withStrapiSchema, withReact, ...blockRegisteredPlugins)(createEditor())\n );\n const [liveText, setLiveText] = React.useState('');\n const ariaDescriptionId = React.useId();\n const [isExpandedMode, handleToggleExpand] = React.useReducer((prev) => !prev, false);\n\n /**\n * Editable is not able to hold the ref, https://github.com/ianstormtaylor/slate/issues/4082\n * so with \"useImperativeHandle\" we can use ReactEditor methods to expose to the parent above\n * also not passing forwarded ref here, gives console warning.\n */\n React.useImperativeHandle(\n forwardedRef,\n () => ({\n focus() {\n ReactEditor.focus(editor);\n },\n }),\n [editor]\n );\n\n const { key, incrementSlateUpdatesCount } = useResetKey(value);\n\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const flushPendingFormSync = React.useCallback(() => {\n if (!debounceTimeout.current) {\n return;\n }\n clearTimeout(debounceTimeout.current);\n debounceTimeout.current = null;\n incrementSlateUpdatesCount();\n // Ensure Strapi Form state updates before the next event (e.g. Save click) reads values.\n flushSync(() => {\n onChange(\n name,\n normalizeBlocksState(editor, editor.children) as Schema.Attribute.BlocksValue\n );\n });\n }, [editor, incrementSlateUpdatesCount, name, onChange]);\n\n const handleSlateChange = React.useCallback(\n (state: Descendant[]) => {\n const isAstChange = editor.operations.some((op) => op.type !== 'set_selection');\n\n if (isAstChange) {\n /**\n * Slate handles the state of the editor internally. We just need to keep Strapi's form\n * state in sync with it in order to make sure that things like the \"modified\" state\n * isn't broken. Updating the whole state on every change is very expensive however,\n * so we debounce calls to onChange to mitigate input lag.\n */\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n\n // Set a new debounce timeout\n debounceTimeout.current = setTimeout(() => {\n incrementSlateUpdatesCount();\n\n // Normalize the state (empty editor becomes null)\n onChange(name, normalizeBlocksState(editor, state) as Schema.Attribute.BlocksValue);\n debounceTimeout.current = null;\n }, 300);\n }\n },\n [editor, incrementSlateUpdatesCount, name, onChange]\n );\n\n // Clean up the timeout on unmount\n React.useEffect(() => {\n return () => {\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n };\n }, []);\n\n // Ensure the editor is in sync after discard\n React.useEffect(() => {\n // Never deselect while the editor is actively focused (typing / editing),\n if (ReactEditor.isFocused(editor)) {\n return;\n }\n\n // Normalize empty states for comparison to avoid losing focus on the editor when content is deleted\n const normalizedValue = value?.length ? value : null;\n const normalizedEditorState = normalizeBlocksState(editor, editor.children);\n\n // Compare the field value with the editor state to check for a stale selection\n if (\n normalizedValue &&\n normalizedEditorState &&\n JSON.stringify(normalizedEditorState) !== JSON.stringify(normalizedValue)\n ) {\n // When there is a diff, unset selection to avoid an invalid state\n Transforms.deselect(editor);\n }\n }, [editor, value]);\n\n return (\n <>\n <VisuallyHidden id={ariaDescriptionId}>\n {formatMessage({\n id: getTranslation('components.Blocks.dnd.instruction'),\n defaultMessage: `To reorder blocks, press Command or Control along with Shift and the Up or Down arrow keys`,\n })}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{liveText}</VisuallyHidden>\n <Slate\n editor={editor}\n initialValue={\n value?.length ? value : [{ type: 'paragraph', children: [{ type: 'text', text: '' }] }]\n }\n onChange={handleSlateChange}\n key={key}\n >\n <BlocksEditorProvider\n blocks={blocks}\n modifiers={modifiers}\n disabled={disabled}\n name={name}\n setLiveText={setLiveText}\n isExpandedMode={isExpandedMode}\n flushPendingFormSync={flushPendingFormSync}\n >\n <EditorLayout\n error={error}\n disabled={disabled}\n onToggleExpand={handleToggleExpand}\n ariaDescriptionId={ariaDescriptionId}\n >\n <BlocksToolbar />\n <EditorDivider width=\"100%\" />\n <BlocksContent {...contentProps} />\n {!isExpandedMode && !isMobile && (\n <IconButton\n position=\"absolute\"\n bottom=\"1.2rem\"\n right=\"1.2rem\"\n shadow=\"filterShadow\"\n label={formatMessage({\n id: getTranslation('components.Blocks.expand'),\n defaultMessage: 'Expand',\n })}\n onClick={handleToggleExpand}\n >\n <Expand />\n </IconButton>\n )}\n </EditorLayout>\n </BlocksEditorProvider>\n </Slate>\n </>\n );\n }\n);\n\nexport {\n type BlocksStore,\n type SelectorBlockKey,\n BlocksEditor,\n BlocksEditorProvider,\n useBlocksEditorContext,\n isSelectorBlockKey,\n normalizeBlocksState,\n};\n"],"names":["selectorBlockKeys","isSelectorBlockKey","key","includes","BlocksEditorProvider","usePartialBlocksEditorContext","createContext","useBlocksEditorContext","consumerName","context","state","editor","useSlate","EditorDivider","styled","Divider","theme","colors","neutral200","useResetKey","value","slateUpdatesCount","React","useRef","valueUpdatesCount","setKey","useState","useEffect","current","previousKey","incrementSlateUpdatesCount","useCallback","pipe","fns","reduce","prev","fn","normalizeBlocksState","isEmpty","length","Editor","BlocksEditor","forwardRef","disabled","name","onChange","error","contentProps","forwardedRef","formatMessage","useIntl","isMobile","useIsMobile","blocks","useMemo","paragraphBlocks","headingBlocks","listBlocks","linkBlocks","imageBlocks","quoteBlocks","codeBlocks","blockRegisteredPlugins","Object","values","map","block","plugin","filter","isNonNullable","withHistory","withStrapiSchema","withReact","createEditor","liveText","setLiveText","ariaDescriptionId","useId","isExpandedMode","handleToggleExpand","useReducer","useImperativeHandle","focus","ReactEditor","debounceTimeout","flushPendingFormSync","clearTimeout","flushSync","children","handleSlateChange","isAstChange","operations","some","op","type","setTimeout","isFocused","normalizedValue","normalizedEditorState","JSON","stringify","Transforms","deselect","_jsxs","_Fragment","_jsx","VisuallyHidden","id","getTranslation","defaultMessage","aria-live","Slate","initialValue","text","modifiers","EditorLayout","onToggleExpand","BlocksToolbar","width","BlocksContent","IconButton","position","bottom","right","shadow","label","onClick","Expand"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlocksEditor.js","sources":["../../../../../../../admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n createContext,\n useStrapiApp,\n type FieldValue,\n useIsMobile,\n} from '@strapi/admin/strapi-admin';\nimport { IconButton, Divider, VisuallyHidden } from '@strapi/design-system';\nimport { Expand } from '@strapi/icons';\nimport { flushSync } from 'react-dom';\nimport { MessageDescriptor, useIntl } from 'react-intl';\nimport { Editor, type Descendant, createEditor, Transforms, Element } from 'slate';\nimport { withHistory } from 'slate-history';\nimport { type RenderElementProps, Slate, withReact, ReactEditor, useSlate } from 'slate-react';\nimport { styled, type CSSProperties } from 'styled-components';\n\nimport { ContentManagerPlugin } from '../../../../../content-manager';\nimport { getTranslation } from '../../../../../utils/translations';\n\nimport { BlocksContent, type BlocksContentProps } from './BlocksContent';\nimport { BlocksToolbar } from './BlocksToolbar';\nimport { EditorLayout } from './EditorLayout';\nimport { type ModifiersStore, modifiers } from './Modifiers';\nimport { withStrapiSchema } from './plugins/withStrapiSchema';\nimport { isNonNullable } from './utils/types';\n\nimport type { Schema } from '@strapi/types';\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditorProvider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface CustomNode extends Omit<Schema.Attribute.BlocksNode, 'type'> {\n type: Schema.Attribute.BlocksNode['type'] | string;\n level?: number;\n format?: string;\n}\n\ninterface BaseBlock {\n renderElement: (props: RenderElementProps) => React.JSX.Element;\n /** Function to check if a given node is of this type of block */\n matchNode: (node: Schema.Attribute.BlocksNode | CustomNode) => boolean;\n handleConvert?: (editor: Editor) => void | (() => React.JSX.Element);\n handleEnterKey?: (editor: Editor) => void;\n handleBackspaceKey?: (editor: Editor, event: React.KeyboardEvent<HTMLElement>) => void;\n handleTab?: (editor: Editor) => void;\n handleShiftTab?: (editor: Editor) => void;\n snippets?: string[];\n /** Adjust the vertical positioning of the drag-to-reorder grip icon */\n dragHandleTopMargin?: CSSProperties['marginTop'];\n /** A Slate plugin: function that will wrap the editor creation */\n plugin?: (editor: Editor) => Editor;\n /**\n * Function that checks if an element should be draggable\n * @default () => true */\n isDraggable?: (element: Element) => boolean;\n}\n\nexport interface NonSelectorBlock extends BaseBlock {\n isInBlocksSelector?: false;\n}\n\nexport interface SelectorBlock extends BaseBlock {\n isInBlocksSelector: true;\n icon?: React.ComponentType;\n label: MessageDescriptor;\n}\n\ntype NonSelectorBlockKey = 'list-item' | 'link';\n\nconst selectorBlockKeys = [\n 'paragraph',\n 'heading-one',\n 'heading-two',\n 'heading-three',\n 'heading-four',\n 'heading-five',\n 'heading-six',\n 'list-ordered',\n 'list-unordered',\n 'image',\n 'quote',\n 'code',\n] as const;\n\ntype SelectorBlockKey = (typeof selectorBlockKeys)[number];\n\nconst isSelectorBlockKey = (key: unknown): key is SelectorBlockKey => {\n return typeof key === 'string' && selectorBlockKeys.includes(key as SelectorBlockKey);\n};\n\ntype BlocksStore = {\n [K in SelectorBlockKey]: SelectorBlock;\n} & {\n [K in NonSelectorBlockKey]: NonSelectorBlock;\n};\n\ntype RichTextBlocksStore = Partial<BlocksStore> & Record<string, SelectorBlock | NonSelectorBlock>;\n\ninterface BlocksEditorContextValue {\n blocks: RichTextBlocksStore;\n modifiers: ModifiersStore;\n disabled: boolean;\n name: string;\n setLiveText: (text: string) => void;\n isExpandedMode: boolean;\n /** Push debounced Slate → form sync immediately (e.g. on Editable blur before Save). */\n flushPendingFormSync: () => void;\n}\n\nconst [BlocksEditorProvider, usePartialBlocksEditorContext] =\n createContext<BlocksEditorContextValue>('BlocksEditor');\n\nfunction useBlocksEditorContext(consumerName: string): BlocksEditorContextValue & {\n editor: Editor;\n} {\n const context = usePartialBlocksEditorContext(consumerName, (state) => state);\n const editor = useSlate();\n\n return {\n ...context,\n editor,\n };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditor\n * -----------------------------------------------------------------------------------------------*/\n\nconst EditorDivider = styled(Divider)`\n background: ${({ theme }) => theme.colors.neutral200};\n`;\n\n/**\n * Forces an update of the Slate editor when the value prop changes from outside of Slate.\n * The root cause is that Slate is not a controlled component: https://github.com/ianstormtaylor/slate/issues/4612\n * Why not use JSON.stringify(value) as the key?\n * Because it would force a rerender of the entire editor every time the user types a character.\n * Why not use the entity id as the key, since it's unique for each locale?\n * Because it would not solve the problem when using the \"fill in from other locale\" feature\n */\nfunction useResetKey(value?: Schema.Attribute.BlocksValue): {\n key: number;\n incrementSlateUpdatesCount: () => void;\n} {\n // Keep track how many times Slate detected a change from a user interaction in the editor\n const slateUpdatesCount = React.useRef(0);\n // Keep track of how many times the value prop was updated, whether from within editor or from outside\n const valueUpdatesCount = React.useRef(0);\n // Use a key to force a rerender of the Slate editor when needed\n const [key, setKey] = React.useState(0);\n\n React.useEffect(() => {\n valueUpdatesCount.current += 1;\n\n // If the 2 refs are not equal, it means the value was updated from outside\n if (valueUpdatesCount.current !== slateUpdatesCount.current) {\n // So we change the key to force a rerender of the Slate editor,\n // which will pick up the new value through its initialValue prop\n setKey((previousKey) => previousKey + 1);\n\n // Then bring the 2 refs back in sync\n slateUpdatesCount.current = valueUpdatesCount.current;\n }\n }, [value]);\n\n const incrementSlateUpdatesCount = React.useCallback(() => {\n slateUpdatesCount.current += 1;\n }, []);\n\n return { key, incrementSlateUpdatesCount };\n}\n\nconst pipe =\n (...fns: ((baseEditor: Editor) => Editor)[]) =>\n (value: Editor) =>\n fns.reduce<Editor>((prev, fn) => fn(prev), value);\n\n/**\n * Normalize the blocks state to null if the editor state is considered empty,\n * otherwise return the state\n */\nconst normalizeBlocksState = (\n editor: Editor,\n value: Schema.Attribute.BlocksValue | Descendant[]\n): Schema.Attribute.BlocksValue | Descendant[] | null => {\n const isEmpty =\n value.length === 1 && Editor.isEmpty(editor, value[0] as Schema.Attribute.BlocksNode);\n\n return isEmpty ? null : value;\n};\n\ninterface BlocksEditorProps\n extends Pick<FieldValue<Schema.Attribute.BlocksValue>, 'onChange' | 'value' | 'error'>,\n BlocksContentProps {\n disabled?: boolean;\n name: string;\n}\n\nconst BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(\n ({ disabled = false, name, onChange, value, error, ...contentProps }, forwardedRef) => {\n const { formatMessage } = useIntl();\n const isMobile = useIsMobile();\n\n const blocks = useStrapiApp(\n 'BlocksEditor',\n (state) =>\n (\n state.plugins['content-manager']?.apis as\n | ContentManagerPlugin['config']['apis']\n | undefined\n )?.getRichTextBlocks() ?? ({} as RichTextBlocksStore)\n );\n\n const blockRegisteredPlugins = Object.values(blocks)\n .map((block) => block.plugin)\n .filter(isNonNullable);\n\n const [editor] = React.useState(() =>\n pipe(withHistory, withStrapiSchema, withReact, ...blockRegisteredPlugins)(createEditor())\n );\n const [liveText, setLiveText] = React.useState('');\n const ariaDescriptionId = React.useId();\n const [isExpandedMode, handleToggleExpand] = React.useReducer((prev) => !prev, false);\n\n /**\n * Editable is not able to hold the ref, https://github.com/ianstormtaylor/slate/issues/4082\n * so with \"useImperativeHandle\" we can use ReactEditor methods to expose to the parent above\n * also not passing forwarded ref here, gives console warning.\n */\n React.useImperativeHandle(\n forwardedRef,\n () => ({\n focus() {\n ReactEditor.focus(editor);\n },\n }),\n [editor]\n );\n\n const { key, incrementSlateUpdatesCount } = useResetKey(value);\n\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const flushPendingFormSync = React.useCallback(() => {\n if (!debounceTimeout.current) {\n return;\n }\n clearTimeout(debounceTimeout.current);\n debounceTimeout.current = null;\n incrementSlateUpdatesCount();\n // Ensure Strapi Form state updates before the next event (e.g. Save click) reads values.\n flushSync(() => {\n onChange(\n name,\n normalizeBlocksState(editor, editor.children) as Schema.Attribute.BlocksValue\n );\n });\n }, [editor, incrementSlateUpdatesCount, name, onChange]);\n\n const handleSlateChange = React.useCallback(\n (state: Descendant[]) => {\n const isAstChange = editor.operations.some((op) => op.type !== 'set_selection');\n\n if (isAstChange) {\n /**\n * Slate handles the state of the editor internally. We just need to keep Strapi's form\n * state in sync with it in order to make sure that things like the \"modified\" state\n * isn't broken. Updating the whole state on every change is very expensive however,\n * so we debounce calls to onChange to mitigate input lag.\n */\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n\n // Set a new debounce timeout\n debounceTimeout.current = setTimeout(() => {\n incrementSlateUpdatesCount();\n\n // Normalize the state (empty editor becomes null)\n onChange(name, normalizeBlocksState(editor, state) as Schema.Attribute.BlocksValue);\n debounceTimeout.current = null;\n }, 300);\n }\n },\n [editor, incrementSlateUpdatesCount, name, onChange]\n );\n\n // Clean up the timeout on unmount\n React.useEffect(() => {\n return () => {\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n };\n }, []);\n\n // Ensure the editor is in sync after discard\n React.useEffect(() => {\n // Never deselect while the editor is actively focused (typing / editing),\n if (ReactEditor.isFocused(editor)) {\n return;\n }\n\n // Normalize empty states for comparison to avoid losing focus on the editor when content is deleted\n const normalizedValue = value?.length ? value : null;\n const normalizedEditorState = normalizeBlocksState(editor, editor.children);\n\n // Compare the field value with the editor state to check for a stale selection\n if (\n normalizedValue &&\n normalizedEditorState &&\n JSON.stringify(normalizedEditorState) !== JSON.stringify(normalizedValue)\n ) {\n // When there is a diff, unset selection to avoid an invalid state\n Transforms.deselect(editor);\n }\n }, [editor, value]);\n\n return (\n <>\n <VisuallyHidden id={ariaDescriptionId}>\n {formatMessage({\n id: getTranslation('components.Blocks.dnd.instruction'),\n defaultMessage: `To reorder blocks, press Command or Control along with Shift and the Up or Down arrow keys`,\n })}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{liveText}</VisuallyHidden>\n <Slate\n editor={editor}\n initialValue={\n value?.length ? value : [{ type: 'paragraph', children: [{ type: 'text', text: '' }] }]\n }\n onChange={handleSlateChange}\n key={key}\n >\n <BlocksEditorProvider\n blocks={blocks}\n modifiers={modifiers}\n disabled={disabled}\n name={name}\n setLiveText={setLiveText}\n isExpandedMode={isExpandedMode}\n flushPendingFormSync={flushPendingFormSync}\n >\n <EditorLayout\n error={error}\n disabled={disabled}\n onToggleExpand={handleToggleExpand}\n ariaDescriptionId={ariaDescriptionId}\n >\n <BlocksToolbar />\n <EditorDivider width=\"100%\" />\n <BlocksContent {...contentProps} />\n {!isExpandedMode && !isMobile && (\n <IconButton\n position=\"absolute\"\n bottom=\"1.2rem\"\n right=\"1.2rem\"\n shadow=\"filterShadow\"\n label={formatMessage({\n id: getTranslation('components.Blocks.expand'),\n defaultMessage: 'Expand',\n })}\n onClick={handleToggleExpand}\n >\n <Expand />\n </IconButton>\n )}\n </EditorLayout>\n </BlocksEditorProvider>\n </Slate>\n </>\n );\n }\n);\n\nexport {\n type BlocksStore,\n type RichTextBlocksStore,\n type SelectorBlockKey,\n BlocksEditor,\n BlocksEditorProvider,\n useBlocksEditorContext,\n isSelectorBlockKey,\n normalizeBlocksState,\n};\n"],"names":["BlocksEditorProvider","usePartialBlocksEditorContext","createContext","useBlocksEditorContext","consumerName","context","state","editor","useSlate","EditorDivider","styled","Divider","theme","colors","neutral200","useResetKey","value","slateUpdatesCount","React","useRef","valueUpdatesCount","key","setKey","useState","useEffect","current","previousKey","incrementSlateUpdatesCount","useCallback","pipe","fns","reduce","prev","fn","normalizeBlocksState","isEmpty","length","Editor","BlocksEditor","forwardRef","disabled","name","onChange","error","contentProps","forwardedRef","formatMessage","useIntl","isMobile","useIsMobile","blocks","useStrapiApp","plugins","apis","getRichTextBlocks","blockRegisteredPlugins","Object","values","map","block","plugin","filter","isNonNullable","withHistory","withStrapiSchema","withReact","createEditor","liveText","setLiveText","ariaDescriptionId","useId","isExpandedMode","handleToggleExpand","useReducer","useImperativeHandle","focus","ReactEditor","debounceTimeout","flushPendingFormSync","clearTimeout","flushSync","children","handleSlateChange","isAstChange","operations","some","op","type","setTimeout","isFocused","normalizedValue","normalizedEditorState","JSON","stringify","Transforms","deselect","_jsxs","_Fragment","_jsx","VisuallyHidden","id","getTranslation","defaultMessage","aria-live","Slate","initialValue","text","modifiers","EditorLayout","onToggleExpand","BlocksToolbar","width","BlocksContent","IconButton","position","bottom","right","shadow","label","onClick","Expand"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+GA,MAAM,CAACA,oBAAAA,EAAsBC,6BAAAA,CAA8B,GACzDC,yBAAAA,CAAwC,cAAA;AAE1C,SAASC,uBAAuBC,YAAoB,EAAA;AAGlD,IAAA,MAAMC,OAAAA,GAAUJ,6BAAAA,CAA8BG,YAAAA,EAAc,CAACE,KAAAA,GAAUA,KAAAA,CAAAA;AACvE,IAAA,MAAMC,MAAAA,GAASC,mBAAAA,EAAAA;IAEf,OAAO;AACL,QAAA,GAAGH,OAAO;AACVE,QAAAA;AACF,KAAA;AACF;AAEA;;AAEkG,qGAElG,MAAME,aAAAA,GAAgBC,uBAAAA,CAAOC,oBAAAA,CAAQ;cACvB,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAACC,UAAU,CAAC;AACvD,CAAC;AAED;;;;;;;IAQA,SAASC,YAAYC,KAAoC,EAAA;;IAKvD,MAAMC,iBAAAA,GAAoBC,gBAAAA,CAAMC,MAAM,CAAC,CAAA,CAAA;;IAEvC,MAAMC,iBAAAA,GAAoBF,gBAAAA,CAAMC,MAAM,CAAC,CAAA,CAAA;;AAEvC,IAAA,MAAM,CAACE,GAAAA,EAAKC,MAAAA,CAAO,GAAGJ,gBAAAA,CAAMK,QAAQ,CAAC,CAAA,CAAA;AAErCL,IAAAA,gBAAAA,CAAMM,SAAS,CAAC,IAAA;AACdJ,QAAAA,iBAAAA,CAAkBK,OAAO,IAAI,CAAA;;AAG7B,QAAA,IAAIL,iBAAAA,CAAkBK,OAAO,KAAKR,iBAAAA,CAAkBQ,OAAO,EAAE;;;YAG3DH,MAAAA,CAAO,CAACI,cAAgBA,WAAAA,GAAc,CAAA,CAAA;;YAGtCT,iBAAAA,CAAkBQ,OAAO,GAAGL,iBAAAA,CAAkBK,OAAO;AACvD,QAAA;IACF,CAAA,EAAG;AAACT,QAAAA;AAAM,KAAA,CAAA;IAEV,MAAMW,0BAAAA,GAA6BT,gBAAAA,CAAMU,WAAW,CAAC,IAAA;AACnDX,QAAAA,iBAAAA,CAAkBQ,OAAO,IAAI,CAAA;AAC/B,IAAA,CAAA,EAAG,EAAE,CAAA;IAEL,OAAO;AAAEJ,QAAAA,GAAAA;AAAKM,QAAAA;AAA2B,KAAA;AAC3C;AAEA,MAAME,IAAAA,GACJ,CAAC,GAAGC,GAAAA,GACJ,CAACd,KAAAA,GACCc,GAAAA,CAAIC,MAAM,CAAS,CAACC,IAAAA,EAAMC,EAAAA,GAAOA,GAAGD,IAAAA,CAAAA,EAAOhB,KAAAA,CAAAA;AAE/C;;;IAIA,MAAMkB,oBAAAA,GAAuB,CAC3B3B,MAAAA,EACAS,KAAAA,GAAAA;IAEA,MAAMmB,OAAAA,GACJnB,KAAAA,CAAMoB,MAAM,KAAK,CAAA,IAAKC,YAAAA,CAAOF,OAAO,CAAC5B,MAAAA,EAAQS,KAAK,CAAC,CAAA,CAAE,CAAA;AAEvD,IAAA,OAAOmB,UAAU,IAAA,GAAOnB,KAAAA;AAC1B;AASA,MAAMsB,6BAAepB,gBAAAA,CAAMqB,UAAU,CACnC,CAAC,EAAEC,WAAW,KAAK,EAAEC,IAAI,EAAEC,QAAQ,EAAE1B,KAAK,EAAE2B,KAAK,EAAE,GAAGC,cAAc,EAAEC,YAAAA,GAAAA;IACpE,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAMC,QAAAA,GAAWC,uBAAAA,EAAAA;AAEjB,IAAA,MAAMC,MAAAA,GAASC,wBAAAA,CACb,cAAA,EACA,CAAC7C,KAAAA,GAEGA,KAAAA,CAAM8C,OAAO,CAAC,iBAAA,CAAkB,EAAEC,IAAAA,EAGjCC,uBAAwB,EAAC,CAAA;AAGhC,IAAA,MAAMC,sBAAAA,GAAyBC,MAAAA,CAAOC,MAAM,CAACP,MAAAA,CAAAA,CAC1CQ,GAAG,CAAC,CAACC,KAAAA,GAAUA,KAAAA,CAAMC,MAAM,CAAA,CAC3BC,MAAM,CAACC,mBAAAA,CAAAA;IAEV,MAAM,CAACvD,MAAAA,CAAO,GAAGW,gBAAAA,CAAMK,QAAQ,CAAC,IAC9BM,IAAAA,CAAKkC,wBAAAA,EAAaC,iCAAAA,EAAkBC,oBAAAA,EAAAA,GAAcV,sBAAAA,CAAAA,CAAwBW,kBAAAA,EAAAA,CAAAA,CAAAA;AAE5E,IAAA,MAAM,CAACC,QAAAA,EAAUC,WAAAA,CAAY,GAAGlD,gBAAAA,CAAMK,QAAQ,CAAC,EAAA,CAAA;IAC/C,MAAM8C,iBAAAA,GAAoBnD,iBAAMoD,KAAK,EAAA;IACrC,MAAM,CAACC,cAAAA,EAAgBC,kBAAAA,CAAmB,GAAGtD,gBAAAA,CAAMuD,UAAU,CAAC,CAACzC,IAAAA,GAAS,CAACA,IAAAA,EAAM,KAAA,CAAA;AAE/E;;;;AAIC,QACDd,gBAAAA,CAAMwD,mBAAmB,CACvB7B,YAAAA,EACA,KAAO;AACL8B,YAAAA,KAAAA,CAAAA,GAAAA;AACEC,gBAAAA,sBAAAA,CAAYD,KAAK,CAACpE,MAAAA,CAAAA;AACpB,YAAA;AACF,SAAA,CAAA,EACA;AAACA,QAAAA;AAAO,KAAA,CAAA;AAGV,IAAA,MAAM,EAAEc,GAAG,EAAEM,0BAA0B,EAAE,GAAGZ,WAAAA,CAAYC,KAAAA,CAAAA;IAExD,MAAM6D,eAAAA,GAAkB3D,gBAAAA,CAAMC,MAAM,CAAwB,IAAA,CAAA;IAE5D,MAAM2D,oBAAAA,GAAuB5D,gBAAAA,CAAMU,WAAW,CAAC,IAAA;QAC7C,IAAI,CAACiD,eAAAA,CAAgBpD,OAAO,EAAE;AAC5B,YAAA;AACF,QAAA;AACAsD,QAAAA,YAAAA,CAAaF,gBAAgBpD,OAAO,CAAA;AACpCoD,QAAAA,eAAAA,CAAgBpD,OAAO,GAAG,IAAA;AAC1BE,QAAAA,0BAAAA,EAAAA;;QAEAqD,kBAAAA,CAAU,IAAA;AACRtC,YAAAA,QAAAA,CACED,IAAAA,EACAP,oBAAAA,CAAqB3B,MAAAA,EAAQA,MAAAA,CAAO0E,QAAQ,CAAA,CAAA;AAEhD,QAAA,CAAA,CAAA;IACF,CAAA,EAAG;AAAC1E,QAAAA,MAAAA;AAAQoB,QAAAA,0BAAAA;AAA4Bc,QAAAA,IAAAA;AAAMC,QAAAA;AAAS,KAAA,CAAA;AAEvD,IAAA,MAAMwC,iBAAAA,GAAoBhE,gBAAAA,CAAMU,WAAW,CACzC,CAACtB,KAAAA,GAAAA;QACC,MAAM6E,WAAAA,GAAc5E,MAAAA,CAAO6E,UAAU,CAACC,IAAI,CAAC,CAACC,EAAAA,GAAOA,EAAAA,CAAGC,IAAI,KAAK,eAAA,CAAA;AAE/D,QAAA,IAAIJ,WAAAA,EAAa;AACf;;;;;cAMA,IAAIN,eAAAA,CAAgBpD,OAAO,EAAE;AAC3BsD,gBAAAA,YAAAA,CAAaF,gBAAgBpD,OAAO,CAAA;AACtC,YAAA;;YAGAoD,eAAAA,CAAgBpD,OAAO,GAAG+D,UAAAA,CAAW,IAAA;AACnC7D,gBAAAA,0BAAAA,EAAAA;;gBAGAe,QAAAA,CAASD,IAAAA,EAAMP,qBAAqB3B,MAAAA,EAAQD,KAAAA,CAAAA,CAAAA;AAC5CuE,gBAAAA,eAAAA,CAAgBpD,OAAO,GAAG,IAAA;YAC5B,CAAA,EAAG,GAAA,CAAA;AACL,QAAA;IACF,CAAA,EACA;AAAClB,QAAAA,MAAAA;AAAQoB,QAAAA,0BAAAA;AAA4Bc,QAAAA,IAAAA;AAAMC,QAAAA;AAAS,KAAA,CAAA;;AAItDxB,IAAAA,gBAAAA,CAAMM,SAAS,CAAC,IAAA;QACd,OAAO,IAAA;YACL,IAAIqD,eAAAA,CAAgBpD,OAAO,EAAE;AAC3BsD,gBAAAA,YAAAA,CAAaF,gBAAgBpD,OAAO,CAAA;AACtC,YAAA;AACF,QAAA,CAAA;AACF,IAAA,CAAA,EAAG,EAAE,CAAA;;AAGLP,IAAAA,gBAAAA,CAAMM,SAAS,CAAC,IAAA;;QAEd,IAAIoD,sBAAAA,CAAYa,SAAS,CAAClF,MAAAA,CAAAA,EAAS;AACjC,YAAA;AACF,QAAA;;QAGA,MAAMmF,eAAAA,GAAkB1E,KAAAA,EAAOoB,MAAAA,GAASpB,KAAAA,GAAQ,IAAA;AAChD,QAAA,MAAM2E,qBAAAA,GAAwBzD,oBAAAA,CAAqB3B,MAAAA,EAAQA,MAAAA,CAAO0E,QAAQ,CAAA;;QAG1E,IACES,eAAAA,IACAC,yBACAC,IAAAA,CAAKC,SAAS,CAACF,qBAAAA,CAAAA,KAA2BC,IAAAA,CAAKC,SAAS,CAACH,eAAAA,CAAAA,EACzD;;AAEAI,YAAAA,gBAAAA,CAAWC,QAAQ,CAACxF,MAAAA,CAAAA;AACtB,QAAA;IACF,CAAA,EAAG;AAACA,QAAAA,MAAAA;AAAQS,QAAAA;AAAM,KAAA,CAAA;IAElB,qBACEgF,eAAA,CAAAC,mBAAA,EAAA;;0BACEC,cAAA,CAACC,2BAAAA,EAAAA;gBAAeC,EAAAA,EAAI/B,iBAAAA;0BACjBvB,aAAAA,CAAc;AACbsD,oBAAAA,EAAAA,EAAIC,2BAAAA,CAAe,mCAAA,CAAA;oBACnBC,cAAAA,EAAgB,CAAC,0FAA0F;AAC7G,iBAAA;;0BAEFJ,cAAA,CAACC,2BAAAA,EAAAA;gBAAeI,WAAAA,EAAU,WAAA;AAAapC,gBAAAA,QAAAA,EAAAA;;0BACvC+B,cAAA,CAACM,gBAAAA,EAAAA;gBACCjG,MAAAA,EAAQA,MAAAA;gBACRkG,YAAAA,EACEzF,KAAAA,EAAOoB,SAASpB,KAAAA,GAAQ;AAAC,oBAAA;wBAAEuE,IAAAA,EAAM,WAAA;wBAAaN,QAAAA,EAAU;AAAC,4BAAA;gCAAEM,IAAAA,EAAM,MAAA;gCAAQmB,IAAAA,EAAM;AAAG;AAAE;AAAC;AAAE,iBAAA;gBAEzFhE,QAAAA,EAAUwC,iBAAAA;AAGV,gBAAA,QAAA,gBAAAgB,cAAA,CAAClG,oBAAAA,EAAAA;oBACCkD,MAAAA,EAAQA,MAAAA;oBACRyD,SAAAA,EAAWA,mBAAAA;oBACXnE,QAAAA,EAAUA,QAAAA;oBACVC,IAAAA,EAAMA,IAAAA;oBACN2B,WAAAA,EAAaA,WAAAA;oBACbG,cAAAA,EAAgBA,cAAAA;oBAChBO,oBAAAA,EAAsBA,oBAAAA;AAEtB,oBAAA,QAAA,gBAAAkB,eAAA,CAACY,yBAAAA,EAAAA;wBACCjE,KAAAA,EAAOA,KAAAA;wBACPH,QAAAA,EAAUA,QAAAA;wBACVqE,cAAAA,EAAgBrC,kBAAAA;wBAChBH,iBAAAA,EAAmBA,iBAAAA;;0CAEnB6B,cAAA,CAACY,2BAAAA,EAAAA,EAAAA,CAAAA;0CACDZ,cAAA,CAACzF,aAAAA,EAAAA;gCAAcsG,KAAAA,EAAM;;0CACrBb,cAAA,CAACc,2BAAAA,EAAAA;AAAe,gCAAA,GAAGpE;;4BAClB,CAAC2B,cAAAA,IAAkB,CAACvB,QAAAA,kBACnBkD,cAAA,CAACe,uBAAAA,EAAAA;gCACCC,QAAAA,EAAS,UAAA;gCACTC,MAAAA,EAAO,QAAA;gCACPC,KAAAA,EAAM,QAAA;gCACNC,MAAAA,EAAO,cAAA;AACPC,gCAAAA,KAAAA,EAAOxE,aAAAA,CAAc;AACnBsD,oCAAAA,EAAAA,EAAIC,2BAAAA,CAAe,0BAAA,CAAA;oCACnBC,cAAAA,EAAgB;AAClB,iCAAA,CAAA;gCACAiB,OAAAA,EAAS/C,kBAAAA;AAET,gCAAA,QAAA,gBAAA0B,cAAA,CAACsB,YAAAA,EAAAA,EAAAA;;;;;AAhCJnG,aAAAA,EAAAA,GAAAA;;;AAwCb,CAAA;;;;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { createContext, useIsMobile } from '@strapi/admin/strapi-admin';
|
|
3
|
+
import { createContext, useIsMobile, useStrapiApp } from '@strapi/admin/strapi-admin';
|
|
4
4
|
import { Divider, VisuallyHidden, IconButton } from '@strapi/design-system';
|
|
5
5
|
import { Expand } from '@strapi/icons';
|
|
6
6
|
import { flushSync } from 'react-dom';
|
|
@@ -10,13 +10,6 @@ import { withHistory } from 'slate-history';
|
|
|
10
10
|
import { withReact, ReactEditor, Slate, useSlate } from 'slate-react';
|
|
11
11
|
import { styled } from 'styled-components';
|
|
12
12
|
import { getTranslation } from '../../../../../utils/translations.mjs';
|
|
13
|
-
import { codeBlocks } from './Blocks/Code.mjs';
|
|
14
|
-
import { headingBlocks } from './Blocks/Heading.mjs';
|
|
15
|
-
import { imageBlocks } from './Blocks/Image.mjs';
|
|
16
|
-
import { linkBlocks } from './Blocks/Link.mjs';
|
|
17
|
-
import { listBlocks } from './Blocks/List.mjs';
|
|
18
|
-
import { paragraphBlocks } from './Blocks/Paragraph.mjs';
|
|
19
|
-
import { quoteBlocks } from './Blocks/Quote.mjs';
|
|
20
13
|
import { BlocksContent } from './BlocksContent.mjs';
|
|
21
14
|
import { BlocksToolbar } from './BlocksToolbar.mjs';
|
|
22
15
|
import { EditorLayout } from './EditorLayout.mjs';
|
|
@@ -24,23 +17,6 @@ import { modifiers } from './Modifiers.mjs';
|
|
|
24
17
|
import { withStrapiSchema } from './plugins/withStrapiSchema.mjs';
|
|
25
18
|
import { isNonNullable } from './utils/types.mjs';
|
|
26
19
|
|
|
27
|
-
const selectorBlockKeys = [
|
|
28
|
-
'paragraph',
|
|
29
|
-
'heading-one',
|
|
30
|
-
'heading-two',
|
|
31
|
-
'heading-three',
|
|
32
|
-
'heading-four',
|
|
33
|
-
'heading-five',
|
|
34
|
-
'heading-six',
|
|
35
|
-
'list-ordered',
|
|
36
|
-
'list-unordered',
|
|
37
|
-
'image',
|
|
38
|
-
'quote',
|
|
39
|
-
'code'
|
|
40
|
-
];
|
|
41
|
-
const isSelectorBlockKey = (key)=>{
|
|
42
|
-
return typeof key === 'string' && selectorBlockKeys.includes(key);
|
|
43
|
-
};
|
|
44
20
|
const [BlocksEditorProvider, usePartialBlocksEditorContext] = createContext('BlocksEditor');
|
|
45
21
|
function useBlocksEditorContext(consumerName) {
|
|
46
22
|
const context = usePartialBlocksEditorContext(consumerName, (state)=>state);
|
|
@@ -101,15 +77,7 @@ const pipe = (...fns)=>(value)=>fns.reduce((prev, fn)=>fn(prev), value);
|
|
|
101
77
|
const BlocksEditor = /*#__PURE__*/ React.forwardRef(({ disabled = false, name, onChange, value, error, ...contentProps }, forwardedRef)=>{
|
|
102
78
|
const { formatMessage } = useIntl();
|
|
103
79
|
const isMobile = useIsMobile();
|
|
104
|
-
const blocks =
|
|
105
|
-
...paragraphBlocks,
|
|
106
|
-
...headingBlocks,
|
|
107
|
-
...listBlocks,
|
|
108
|
-
...linkBlocks,
|
|
109
|
-
...imageBlocks,
|
|
110
|
-
...quoteBlocks,
|
|
111
|
-
...codeBlocks
|
|
112
|
-
}), []);
|
|
80
|
+
const blocks = useStrapiApp('BlocksEditor', (state)=>state.plugins['content-manager']?.apis?.getRichTextBlocks() ?? {});
|
|
113
81
|
const blockRegisteredPlugins = Object.values(blocks).map((block)=>block.plugin).filter(isNonNullable);
|
|
114
82
|
const [editor] = React.useState(()=>pipe(withHistory, withStrapiSchema, withReact, ...blockRegisteredPlugins)(createEditor()));
|
|
115
83
|
const [liveText, setLiveText] = React.useState('');
|
|
@@ -264,5 +232,5 @@ const BlocksEditor = /*#__PURE__*/ React.forwardRef(({ disabled = false, name, o
|
|
|
264
232
|
});
|
|
265
233
|
});
|
|
266
234
|
|
|
267
|
-
export { BlocksEditor, BlocksEditorProvider,
|
|
235
|
+
export { BlocksEditor, BlocksEditorProvider, normalizeBlocksState, useBlocksEditorContext };
|
|
268
236
|
//# sourceMappingURL=BlocksEditor.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlocksEditor.mjs","sources":["../../../../../../../admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { createContext, type FieldValue, useIsMobile } from '@strapi/admin/strapi-admin';\nimport { IconButton, Divider, VisuallyHidden } from '@strapi/design-system';\nimport { Expand } from '@strapi/icons';\nimport { flushSync } from 'react-dom';\nimport { MessageDescriptor, useIntl } from 'react-intl';\nimport { Editor, type Descendant, createEditor, Transforms, Element } from 'slate';\nimport { withHistory } from 'slate-history';\nimport { type RenderElementProps, Slate, withReact, ReactEditor, useSlate } from 'slate-react';\nimport { styled, type CSSProperties } from 'styled-components';\n\nimport { getTranslation } from '../../../../../utils/translations';\n\nimport { codeBlocks } from './Blocks/Code';\nimport { headingBlocks } from './Blocks/Heading';\nimport { imageBlocks } from './Blocks/Image';\nimport { linkBlocks } from './Blocks/Link';\nimport { listBlocks } from './Blocks/List';\nimport { paragraphBlocks } from './Blocks/Paragraph';\nimport { quoteBlocks } from './Blocks/Quote';\nimport { BlocksContent, type BlocksContentProps } from './BlocksContent';\nimport { BlocksToolbar } from './BlocksToolbar';\nimport { EditorLayout } from './EditorLayout';\nimport { type ModifiersStore, modifiers } from './Modifiers';\nimport { withStrapiSchema } from './plugins/withStrapiSchema';\nimport { isNonNullable } from './utils/types';\n\nimport type { Schema } from '@strapi/types';\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditorProvider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface BaseBlock {\n renderElement: (props: RenderElementProps) => React.JSX.Element;\n /** Function to check if a given node is of this type of block */\n matchNode: (node: Schema.Attribute.BlocksNode) => boolean;\n handleConvert?: (editor: Editor) => void | (() => React.JSX.Element);\n handleEnterKey?: (editor: Editor) => void;\n handleBackspaceKey?: (editor: Editor, event: React.KeyboardEvent<HTMLElement>) => void;\n handleTab?: (editor: Editor) => void;\n handleShiftTab?: (editor: Editor) => void;\n snippets?: string[];\n /** Adjust the vertical positioning of the drag-to-reorder grip icon */\n dragHandleTopMargin?: CSSProperties['marginTop'];\n /** A Slate plugin: function that will wrap the editor creation */\n plugin?: (editor: Editor) => Editor;\n /**\n * Function that checks if an element should be draggable\n * @default () => true */\n isDraggable?: (element: Element) => boolean;\n}\n\ninterface NonSelectorBlock extends BaseBlock {\n isInBlocksSelector: false;\n}\n\ninterface SelectorBlock extends BaseBlock {\n isInBlocksSelector: true;\n icon: React.ComponentType;\n label: MessageDescriptor;\n}\n\ntype NonSelectorBlockKey = 'list-item' | 'link';\n\nconst selectorBlockKeys = [\n 'paragraph',\n 'heading-one',\n 'heading-two',\n 'heading-three',\n 'heading-four',\n 'heading-five',\n 'heading-six',\n 'list-ordered',\n 'list-unordered',\n 'image',\n 'quote',\n 'code',\n] as const;\n\ntype SelectorBlockKey = (typeof selectorBlockKeys)[number];\n\nconst isSelectorBlockKey = (key: unknown): key is SelectorBlockKey => {\n return typeof key === 'string' && selectorBlockKeys.includes(key as SelectorBlockKey);\n};\n\ntype BlocksStore = {\n [K in SelectorBlockKey]: SelectorBlock;\n} & {\n [K in NonSelectorBlockKey]: NonSelectorBlock;\n};\n\ninterface BlocksEditorContextValue {\n blocks: BlocksStore;\n modifiers: ModifiersStore;\n disabled: boolean;\n name: string;\n setLiveText: (text: string) => void;\n isExpandedMode: boolean;\n /** Push debounced Slate → form sync immediately (e.g. on Editable blur before Save). */\n flushPendingFormSync: () => void;\n}\n\nconst [BlocksEditorProvider, usePartialBlocksEditorContext] =\n createContext<BlocksEditorContextValue>('BlocksEditor');\n\nfunction useBlocksEditorContext(\n consumerName: string\n): BlocksEditorContextValue & { editor: Editor } {\n const context = usePartialBlocksEditorContext(consumerName, (state) => state);\n const editor = useSlate();\n\n return {\n ...context,\n editor,\n };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditor\n * -----------------------------------------------------------------------------------------------*/\n\nconst EditorDivider = styled(Divider)`\n background: ${({ theme }) => theme.colors.neutral200};\n`;\n\n/**\n * Forces an update of the Slate editor when the value prop changes from outside of Slate.\n * The root cause is that Slate is not a controlled component: https://github.com/ianstormtaylor/slate/issues/4612\n * Why not use JSON.stringify(value) as the key?\n * Because it would force a rerender of the entire editor every time the user types a character.\n * Why not use the entity id as the key, since it's unique for each locale?\n * Because it would not solve the problem when using the \"fill in from other locale\" feature\n */\nfunction useResetKey(value?: Schema.Attribute.BlocksValue): {\n key: number;\n incrementSlateUpdatesCount: () => void;\n} {\n // Keep track how many times Slate detected a change from a user interaction in the editor\n const slateUpdatesCount = React.useRef(0);\n // Keep track of how many times the value prop was updated, whether from within editor or from outside\n const valueUpdatesCount = React.useRef(0);\n // Use a key to force a rerender of the Slate editor when needed\n const [key, setKey] = React.useState(0);\n\n React.useEffect(() => {\n valueUpdatesCount.current += 1;\n\n // If the 2 refs are not equal, it means the value was updated from outside\n if (valueUpdatesCount.current !== slateUpdatesCount.current) {\n // So we change the key to force a rerender of the Slate editor,\n // which will pick up the new value through its initialValue prop\n setKey((previousKey) => previousKey + 1);\n\n // Then bring the 2 refs back in sync\n slateUpdatesCount.current = valueUpdatesCount.current;\n }\n }, [value]);\n\n const incrementSlateUpdatesCount = React.useCallback(() => {\n slateUpdatesCount.current += 1;\n }, []);\n\n return { key, incrementSlateUpdatesCount };\n}\n\nconst pipe =\n (...fns: ((baseEditor: Editor) => Editor)[]) =>\n (value: Editor) =>\n fns.reduce<Editor>((prev, fn) => fn(prev), value);\n\n/**\n * Normalize the blocks state to null if the editor state is considered empty,\n * otherwise return the state\n */\nconst normalizeBlocksState = (\n editor: Editor,\n value: Schema.Attribute.BlocksValue | Descendant[]\n): Schema.Attribute.BlocksValue | Descendant[] | null => {\n const isEmpty =\n value.length === 1 && Editor.isEmpty(editor, value[0] as Schema.Attribute.BlocksNode);\n\n return isEmpty ? null : value;\n};\n\ninterface BlocksEditorProps\n extends Pick<FieldValue<Schema.Attribute.BlocksValue>, 'onChange' | 'value' | 'error'>,\n BlocksContentProps {\n disabled?: boolean;\n name: string;\n}\n\nconst BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(\n ({ disabled = false, name, onChange, value, error, ...contentProps }, forwardedRef) => {\n const { formatMessage } = useIntl();\n const isMobile = useIsMobile();\n\n const blocks = React.useMemo(\n () => ({\n ...paragraphBlocks,\n ...headingBlocks,\n ...listBlocks,\n ...linkBlocks,\n ...imageBlocks,\n ...quoteBlocks,\n ...codeBlocks,\n }),\n []\n ) satisfies BlocksStore;\n\n const blockRegisteredPlugins = Object.values(blocks)\n .map((block) => block.plugin)\n .filter(isNonNullable);\n\n const [editor] = React.useState(() =>\n pipe(withHistory, withStrapiSchema, withReact, ...blockRegisteredPlugins)(createEditor())\n );\n const [liveText, setLiveText] = React.useState('');\n const ariaDescriptionId = React.useId();\n const [isExpandedMode, handleToggleExpand] = React.useReducer((prev) => !prev, false);\n\n /**\n * Editable is not able to hold the ref, https://github.com/ianstormtaylor/slate/issues/4082\n * so with \"useImperativeHandle\" we can use ReactEditor methods to expose to the parent above\n * also not passing forwarded ref here, gives console warning.\n */\n React.useImperativeHandle(\n forwardedRef,\n () => ({\n focus() {\n ReactEditor.focus(editor);\n },\n }),\n [editor]\n );\n\n const { key, incrementSlateUpdatesCount } = useResetKey(value);\n\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const flushPendingFormSync = React.useCallback(() => {\n if (!debounceTimeout.current) {\n return;\n }\n clearTimeout(debounceTimeout.current);\n debounceTimeout.current = null;\n incrementSlateUpdatesCount();\n // Ensure Strapi Form state updates before the next event (e.g. Save click) reads values.\n flushSync(() => {\n onChange(\n name,\n normalizeBlocksState(editor, editor.children) as Schema.Attribute.BlocksValue\n );\n });\n }, [editor, incrementSlateUpdatesCount, name, onChange]);\n\n const handleSlateChange = React.useCallback(\n (state: Descendant[]) => {\n const isAstChange = editor.operations.some((op) => op.type !== 'set_selection');\n\n if (isAstChange) {\n /**\n * Slate handles the state of the editor internally. We just need to keep Strapi's form\n * state in sync with it in order to make sure that things like the \"modified\" state\n * isn't broken. Updating the whole state on every change is very expensive however,\n * so we debounce calls to onChange to mitigate input lag.\n */\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n\n // Set a new debounce timeout\n debounceTimeout.current = setTimeout(() => {\n incrementSlateUpdatesCount();\n\n // Normalize the state (empty editor becomes null)\n onChange(name, normalizeBlocksState(editor, state) as Schema.Attribute.BlocksValue);\n debounceTimeout.current = null;\n }, 300);\n }\n },\n [editor, incrementSlateUpdatesCount, name, onChange]\n );\n\n // Clean up the timeout on unmount\n React.useEffect(() => {\n return () => {\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n };\n }, []);\n\n // Ensure the editor is in sync after discard\n React.useEffect(() => {\n // Never deselect while the editor is actively focused (typing / editing),\n if (ReactEditor.isFocused(editor)) {\n return;\n }\n\n // Normalize empty states for comparison to avoid losing focus on the editor when content is deleted\n const normalizedValue = value?.length ? value : null;\n const normalizedEditorState = normalizeBlocksState(editor, editor.children);\n\n // Compare the field value with the editor state to check for a stale selection\n if (\n normalizedValue &&\n normalizedEditorState &&\n JSON.stringify(normalizedEditorState) !== JSON.stringify(normalizedValue)\n ) {\n // When there is a diff, unset selection to avoid an invalid state\n Transforms.deselect(editor);\n }\n }, [editor, value]);\n\n return (\n <>\n <VisuallyHidden id={ariaDescriptionId}>\n {formatMessage({\n id: getTranslation('components.Blocks.dnd.instruction'),\n defaultMessage: `To reorder blocks, press Command or Control along with Shift and the Up or Down arrow keys`,\n })}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{liveText}</VisuallyHidden>\n <Slate\n editor={editor}\n initialValue={\n value?.length ? value : [{ type: 'paragraph', children: [{ type: 'text', text: '' }] }]\n }\n onChange={handleSlateChange}\n key={key}\n >\n <BlocksEditorProvider\n blocks={blocks}\n modifiers={modifiers}\n disabled={disabled}\n name={name}\n setLiveText={setLiveText}\n isExpandedMode={isExpandedMode}\n flushPendingFormSync={flushPendingFormSync}\n >\n <EditorLayout\n error={error}\n disabled={disabled}\n onToggleExpand={handleToggleExpand}\n ariaDescriptionId={ariaDescriptionId}\n >\n <BlocksToolbar />\n <EditorDivider width=\"100%\" />\n <BlocksContent {...contentProps} />\n {!isExpandedMode && !isMobile && (\n <IconButton\n position=\"absolute\"\n bottom=\"1.2rem\"\n right=\"1.2rem\"\n shadow=\"filterShadow\"\n label={formatMessage({\n id: getTranslation('components.Blocks.expand'),\n defaultMessage: 'Expand',\n })}\n onClick={handleToggleExpand}\n >\n <Expand />\n </IconButton>\n )}\n </EditorLayout>\n </BlocksEditorProvider>\n </Slate>\n </>\n );\n }\n);\n\nexport {\n type BlocksStore,\n type SelectorBlockKey,\n BlocksEditor,\n BlocksEditorProvider,\n useBlocksEditorContext,\n isSelectorBlockKey,\n normalizeBlocksState,\n};\n"],"names":["selectorBlockKeys","isSelectorBlockKey","key","includes","BlocksEditorProvider","usePartialBlocksEditorContext","createContext","useBlocksEditorContext","consumerName","context","state","editor","useSlate","EditorDivider","styled","Divider","theme","colors","neutral200","useResetKey","value","slateUpdatesCount","React","useRef","valueUpdatesCount","setKey","useState","useEffect","current","previousKey","incrementSlateUpdatesCount","useCallback","pipe","fns","reduce","prev","fn","normalizeBlocksState","isEmpty","length","Editor","BlocksEditor","forwardRef","disabled","name","onChange","error","contentProps","forwardedRef","formatMessage","useIntl","isMobile","useIsMobile","blocks","useMemo","paragraphBlocks","headingBlocks","listBlocks","linkBlocks","imageBlocks","quoteBlocks","codeBlocks","blockRegisteredPlugins","Object","values","map","block","plugin","filter","isNonNullable","withHistory","withStrapiSchema","withReact","createEditor","liveText","setLiveText","ariaDescriptionId","useId","isExpandedMode","handleToggleExpand","useReducer","useImperativeHandle","focus","ReactEditor","debounceTimeout","flushPendingFormSync","clearTimeout","flushSync","children","handleSlateChange","isAstChange","operations","some","op","type","setTimeout","isFocused","normalizedValue","normalizedEditorState","JSON","stringify","Transforms","deselect","_jsxs","_Fragment","_jsx","VisuallyHidden","id","getTranslation","defaultMessage","aria-live","Slate","initialValue","text","modifiers","EditorLayout","onToggleExpand","BlocksToolbar","width","BlocksContent","IconButton","position","bottom","right","shadow","label","onClick","Expand"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlocksEditor.mjs","sources":["../../../../../../../admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n createContext,\n useStrapiApp,\n type FieldValue,\n useIsMobile,\n} from '@strapi/admin/strapi-admin';\nimport { IconButton, Divider, VisuallyHidden } from '@strapi/design-system';\nimport { Expand } from '@strapi/icons';\nimport { flushSync } from 'react-dom';\nimport { MessageDescriptor, useIntl } from 'react-intl';\nimport { Editor, type Descendant, createEditor, Transforms, Element } from 'slate';\nimport { withHistory } from 'slate-history';\nimport { type RenderElementProps, Slate, withReact, ReactEditor, useSlate } from 'slate-react';\nimport { styled, type CSSProperties } from 'styled-components';\n\nimport { ContentManagerPlugin } from '../../../../../content-manager';\nimport { getTranslation } from '../../../../../utils/translations';\n\nimport { BlocksContent, type BlocksContentProps } from './BlocksContent';\nimport { BlocksToolbar } from './BlocksToolbar';\nimport { EditorLayout } from './EditorLayout';\nimport { type ModifiersStore, modifiers } from './Modifiers';\nimport { withStrapiSchema } from './plugins/withStrapiSchema';\nimport { isNonNullable } from './utils/types';\n\nimport type { Schema } from '@strapi/types';\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditorProvider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface CustomNode extends Omit<Schema.Attribute.BlocksNode, 'type'> {\n type: Schema.Attribute.BlocksNode['type'] | string;\n level?: number;\n format?: string;\n}\n\ninterface BaseBlock {\n renderElement: (props: RenderElementProps) => React.JSX.Element;\n /** Function to check if a given node is of this type of block */\n matchNode: (node: Schema.Attribute.BlocksNode | CustomNode) => boolean;\n handleConvert?: (editor: Editor) => void | (() => React.JSX.Element);\n handleEnterKey?: (editor: Editor) => void;\n handleBackspaceKey?: (editor: Editor, event: React.KeyboardEvent<HTMLElement>) => void;\n handleTab?: (editor: Editor) => void;\n handleShiftTab?: (editor: Editor) => void;\n snippets?: string[];\n /** Adjust the vertical positioning of the drag-to-reorder grip icon */\n dragHandleTopMargin?: CSSProperties['marginTop'];\n /** A Slate plugin: function that will wrap the editor creation */\n plugin?: (editor: Editor) => Editor;\n /**\n * Function that checks if an element should be draggable\n * @default () => true */\n isDraggable?: (element: Element) => boolean;\n}\n\nexport interface NonSelectorBlock extends BaseBlock {\n isInBlocksSelector?: false;\n}\n\nexport interface SelectorBlock extends BaseBlock {\n isInBlocksSelector: true;\n icon?: React.ComponentType;\n label: MessageDescriptor;\n}\n\ntype NonSelectorBlockKey = 'list-item' | 'link';\n\nconst selectorBlockKeys = [\n 'paragraph',\n 'heading-one',\n 'heading-two',\n 'heading-three',\n 'heading-four',\n 'heading-five',\n 'heading-six',\n 'list-ordered',\n 'list-unordered',\n 'image',\n 'quote',\n 'code',\n] as const;\n\ntype SelectorBlockKey = (typeof selectorBlockKeys)[number];\n\nconst isSelectorBlockKey = (key: unknown): key is SelectorBlockKey => {\n return typeof key === 'string' && selectorBlockKeys.includes(key as SelectorBlockKey);\n};\n\ntype BlocksStore = {\n [K in SelectorBlockKey]: SelectorBlock;\n} & {\n [K in NonSelectorBlockKey]: NonSelectorBlock;\n};\n\ntype RichTextBlocksStore = Partial<BlocksStore> & Record<string, SelectorBlock | NonSelectorBlock>;\n\ninterface BlocksEditorContextValue {\n blocks: RichTextBlocksStore;\n modifiers: ModifiersStore;\n disabled: boolean;\n name: string;\n setLiveText: (text: string) => void;\n isExpandedMode: boolean;\n /** Push debounced Slate → form sync immediately (e.g. on Editable blur before Save). */\n flushPendingFormSync: () => void;\n}\n\nconst [BlocksEditorProvider, usePartialBlocksEditorContext] =\n createContext<BlocksEditorContextValue>('BlocksEditor');\n\nfunction useBlocksEditorContext(consumerName: string): BlocksEditorContextValue & {\n editor: Editor;\n} {\n const context = usePartialBlocksEditorContext(consumerName, (state) => state);\n const editor = useSlate();\n\n return {\n ...context,\n editor,\n };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * BlocksEditor\n * -----------------------------------------------------------------------------------------------*/\n\nconst EditorDivider = styled(Divider)`\n background: ${({ theme }) => theme.colors.neutral200};\n`;\n\n/**\n * Forces an update of the Slate editor when the value prop changes from outside of Slate.\n * The root cause is that Slate is not a controlled component: https://github.com/ianstormtaylor/slate/issues/4612\n * Why not use JSON.stringify(value) as the key?\n * Because it would force a rerender of the entire editor every time the user types a character.\n * Why not use the entity id as the key, since it's unique for each locale?\n * Because it would not solve the problem when using the \"fill in from other locale\" feature\n */\nfunction useResetKey(value?: Schema.Attribute.BlocksValue): {\n key: number;\n incrementSlateUpdatesCount: () => void;\n} {\n // Keep track how many times Slate detected a change from a user interaction in the editor\n const slateUpdatesCount = React.useRef(0);\n // Keep track of how many times the value prop was updated, whether from within editor or from outside\n const valueUpdatesCount = React.useRef(0);\n // Use a key to force a rerender of the Slate editor when needed\n const [key, setKey] = React.useState(0);\n\n React.useEffect(() => {\n valueUpdatesCount.current += 1;\n\n // If the 2 refs are not equal, it means the value was updated from outside\n if (valueUpdatesCount.current !== slateUpdatesCount.current) {\n // So we change the key to force a rerender of the Slate editor,\n // which will pick up the new value through its initialValue prop\n setKey((previousKey) => previousKey + 1);\n\n // Then bring the 2 refs back in sync\n slateUpdatesCount.current = valueUpdatesCount.current;\n }\n }, [value]);\n\n const incrementSlateUpdatesCount = React.useCallback(() => {\n slateUpdatesCount.current += 1;\n }, []);\n\n return { key, incrementSlateUpdatesCount };\n}\n\nconst pipe =\n (...fns: ((baseEditor: Editor) => Editor)[]) =>\n (value: Editor) =>\n fns.reduce<Editor>((prev, fn) => fn(prev), value);\n\n/**\n * Normalize the blocks state to null if the editor state is considered empty,\n * otherwise return the state\n */\nconst normalizeBlocksState = (\n editor: Editor,\n value: Schema.Attribute.BlocksValue | Descendant[]\n): Schema.Attribute.BlocksValue | Descendant[] | null => {\n const isEmpty =\n value.length === 1 && Editor.isEmpty(editor, value[0] as Schema.Attribute.BlocksNode);\n\n return isEmpty ? null : value;\n};\n\ninterface BlocksEditorProps\n extends Pick<FieldValue<Schema.Attribute.BlocksValue>, 'onChange' | 'value' | 'error'>,\n BlocksContentProps {\n disabled?: boolean;\n name: string;\n}\n\nconst BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(\n ({ disabled = false, name, onChange, value, error, ...contentProps }, forwardedRef) => {\n const { formatMessage } = useIntl();\n const isMobile = useIsMobile();\n\n const blocks = useStrapiApp(\n 'BlocksEditor',\n (state) =>\n (\n state.plugins['content-manager']?.apis as\n | ContentManagerPlugin['config']['apis']\n | undefined\n )?.getRichTextBlocks() ?? ({} as RichTextBlocksStore)\n );\n\n const blockRegisteredPlugins = Object.values(blocks)\n .map((block) => block.plugin)\n .filter(isNonNullable);\n\n const [editor] = React.useState(() =>\n pipe(withHistory, withStrapiSchema, withReact, ...blockRegisteredPlugins)(createEditor())\n );\n const [liveText, setLiveText] = React.useState('');\n const ariaDescriptionId = React.useId();\n const [isExpandedMode, handleToggleExpand] = React.useReducer((prev) => !prev, false);\n\n /**\n * Editable is not able to hold the ref, https://github.com/ianstormtaylor/slate/issues/4082\n * so with \"useImperativeHandle\" we can use ReactEditor methods to expose to the parent above\n * also not passing forwarded ref here, gives console warning.\n */\n React.useImperativeHandle(\n forwardedRef,\n () => ({\n focus() {\n ReactEditor.focus(editor);\n },\n }),\n [editor]\n );\n\n const { key, incrementSlateUpdatesCount } = useResetKey(value);\n\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const flushPendingFormSync = React.useCallback(() => {\n if (!debounceTimeout.current) {\n return;\n }\n clearTimeout(debounceTimeout.current);\n debounceTimeout.current = null;\n incrementSlateUpdatesCount();\n // Ensure Strapi Form state updates before the next event (e.g. Save click) reads values.\n flushSync(() => {\n onChange(\n name,\n normalizeBlocksState(editor, editor.children) as Schema.Attribute.BlocksValue\n );\n });\n }, [editor, incrementSlateUpdatesCount, name, onChange]);\n\n const handleSlateChange = React.useCallback(\n (state: Descendant[]) => {\n const isAstChange = editor.operations.some((op) => op.type !== 'set_selection');\n\n if (isAstChange) {\n /**\n * Slate handles the state of the editor internally. We just need to keep Strapi's form\n * state in sync with it in order to make sure that things like the \"modified\" state\n * isn't broken. Updating the whole state on every change is very expensive however,\n * so we debounce calls to onChange to mitigate input lag.\n */\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n\n // Set a new debounce timeout\n debounceTimeout.current = setTimeout(() => {\n incrementSlateUpdatesCount();\n\n // Normalize the state (empty editor becomes null)\n onChange(name, normalizeBlocksState(editor, state) as Schema.Attribute.BlocksValue);\n debounceTimeout.current = null;\n }, 300);\n }\n },\n [editor, incrementSlateUpdatesCount, name, onChange]\n );\n\n // Clean up the timeout on unmount\n React.useEffect(() => {\n return () => {\n if (debounceTimeout.current) {\n clearTimeout(debounceTimeout.current);\n }\n };\n }, []);\n\n // Ensure the editor is in sync after discard\n React.useEffect(() => {\n // Never deselect while the editor is actively focused (typing / editing),\n if (ReactEditor.isFocused(editor)) {\n return;\n }\n\n // Normalize empty states for comparison to avoid losing focus on the editor when content is deleted\n const normalizedValue = value?.length ? value : null;\n const normalizedEditorState = normalizeBlocksState(editor, editor.children);\n\n // Compare the field value with the editor state to check for a stale selection\n if (\n normalizedValue &&\n normalizedEditorState &&\n JSON.stringify(normalizedEditorState) !== JSON.stringify(normalizedValue)\n ) {\n // When there is a diff, unset selection to avoid an invalid state\n Transforms.deselect(editor);\n }\n }, [editor, value]);\n\n return (\n <>\n <VisuallyHidden id={ariaDescriptionId}>\n {formatMessage({\n id: getTranslation('components.Blocks.dnd.instruction'),\n defaultMessage: `To reorder blocks, press Command or Control along with Shift and the Up or Down arrow keys`,\n })}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{liveText}</VisuallyHidden>\n <Slate\n editor={editor}\n initialValue={\n value?.length ? value : [{ type: 'paragraph', children: [{ type: 'text', text: '' }] }]\n }\n onChange={handleSlateChange}\n key={key}\n >\n <BlocksEditorProvider\n blocks={blocks}\n modifiers={modifiers}\n disabled={disabled}\n name={name}\n setLiveText={setLiveText}\n isExpandedMode={isExpandedMode}\n flushPendingFormSync={flushPendingFormSync}\n >\n <EditorLayout\n error={error}\n disabled={disabled}\n onToggleExpand={handleToggleExpand}\n ariaDescriptionId={ariaDescriptionId}\n >\n <BlocksToolbar />\n <EditorDivider width=\"100%\" />\n <BlocksContent {...contentProps} />\n {!isExpandedMode && !isMobile && (\n <IconButton\n position=\"absolute\"\n bottom=\"1.2rem\"\n right=\"1.2rem\"\n shadow=\"filterShadow\"\n label={formatMessage({\n id: getTranslation('components.Blocks.expand'),\n defaultMessage: 'Expand',\n })}\n onClick={handleToggleExpand}\n >\n <Expand />\n </IconButton>\n )}\n </EditorLayout>\n </BlocksEditorProvider>\n </Slate>\n </>\n );\n }\n);\n\nexport {\n type BlocksStore,\n type RichTextBlocksStore,\n type SelectorBlockKey,\n BlocksEditor,\n BlocksEditorProvider,\n useBlocksEditorContext,\n isSelectorBlockKey,\n normalizeBlocksState,\n};\n"],"names":["BlocksEditorProvider","usePartialBlocksEditorContext","createContext","useBlocksEditorContext","consumerName","context","state","editor","useSlate","EditorDivider","styled","Divider","theme","colors","neutral200","useResetKey","value","slateUpdatesCount","React","useRef","valueUpdatesCount","key","setKey","useState","useEffect","current","previousKey","incrementSlateUpdatesCount","useCallback","pipe","fns","reduce","prev","fn","normalizeBlocksState","isEmpty","length","Editor","BlocksEditor","forwardRef","disabled","name","onChange","error","contentProps","forwardedRef","formatMessage","useIntl","isMobile","useIsMobile","blocks","useStrapiApp","plugins","apis","getRichTextBlocks","blockRegisteredPlugins","Object","values","map","block","plugin","filter","isNonNullable","withHistory","withStrapiSchema","withReact","createEditor","liveText","setLiveText","ariaDescriptionId","useId","isExpandedMode","handleToggleExpand","useReducer","useImperativeHandle","focus","ReactEditor","debounceTimeout","flushPendingFormSync","clearTimeout","flushSync","children","handleSlateChange","isAstChange","operations","some","op","type","setTimeout","isFocused","normalizedValue","normalizedEditorState","JSON","stringify","Transforms","deselect","_jsxs","_Fragment","_jsx","VisuallyHidden","id","getTranslation","defaultMessage","aria-live","Slate","initialValue","text","modifiers","EditorLayout","onToggleExpand","BlocksToolbar","width","BlocksContent","IconButton","position","bottom","right","shadow","label","onClick","Expand"],"mappings":"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"}
|
|
@@ -159,7 +159,7 @@ const ToolbarButton = ({ icon: Icon, name, label, isActive, disabled, handleClic
|
|
|
159
159
|
height: 7,
|
|
160
160
|
hasRadius: true,
|
|
161
161
|
type: "button",
|
|
162
|
-
children: /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
162
|
+
children: Icon && /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
163
163
|
fill: disabled ? 'neutral300' : enabledColor
|
|
164
164
|
})
|
|
165
165
|
})
|
|
@@ -171,16 +171,15 @@ const BlocksDropdown = ()=>{
|
|
|
171
171
|
const { formatMessage } = reactIntl.useIntl();
|
|
172
172
|
const { modalElement, handleConversionResult } = useConversionModal();
|
|
173
173
|
const isMobile = strapiAdmin.useIsMobile();
|
|
174
|
-
const blockKeysToInclude = types.getEntries(blocks).reduce((currentKeys,
|
|
175
|
-
|
|
176
|
-
return block.isInBlocksSelector ? [
|
|
174
|
+
const blockKeysToInclude = types.getEntries(blocks).reduce((currentKeys, [key, block])=>{
|
|
175
|
+
return block?.isInBlocksSelector ? [
|
|
177
176
|
...currentKeys,
|
|
178
177
|
key
|
|
179
178
|
] : currentKeys;
|
|
180
179
|
}, []);
|
|
181
180
|
const [blockSelected, setBlockSelected] = React__namespace.useState('paragraph');
|
|
182
181
|
const handleSelect = (optionKey)=>{
|
|
183
|
-
if (!
|
|
182
|
+
if (typeof optionKey !== 'string' || !blocks[optionKey]) {
|
|
184
183
|
return;
|
|
185
184
|
}
|
|
186
185
|
const editorIsEmpty = editor.children.length === 1 && slate.Editor.isEmpty(editor, editor.children[0]);
|
|
@@ -229,7 +228,7 @@ const BlocksDropdown = ()=>{
|
|
|
229
228
|
return;
|
|
230
229
|
}
|
|
231
230
|
// Let the block handle the Slate conversion logic
|
|
232
|
-
const maybeRenderModal = blocks[optionKey]
|
|
231
|
+
const maybeRenderModal = blocks[optionKey]?.handleConvert?.(editor);
|
|
233
232
|
handleConversionResult(maybeRenderModal);
|
|
234
233
|
setBlockSelected(optionKey);
|
|
235
234
|
slateReact.ReactEditor.focus(editor);
|
|
@@ -278,7 +277,7 @@ const BlocksDropdown = ()=>{
|
|
|
278
277
|
}
|
|
279
278
|
}
|
|
280
279
|
// Find the block key that matches the anchor node
|
|
281
|
-
const anchorBlockKey = types.getKeys(blocks).find((blockKey)=>!slate.Editor.isEditor(selectedNode) && blocks[blockKey]
|
|
280
|
+
const anchorBlockKey = types.getKeys(blocks).find((blockKey)=>!slate.Editor.isEditor(selectedNode) && blocks[blockKey]?.matchNode(selectedNode));
|
|
282
281
|
// Change the value selected in the dropdown if it doesn't match the anchor block key
|
|
283
282
|
if (anchorBlockKey && anchorBlockKey !== blockSelected) {
|
|
284
283
|
setBlockSelected(anchorBlockKey);
|
|
@@ -290,14 +289,27 @@ const BlocksDropdown = ()=>{
|
|
|
290
289
|
blocks,
|
|
291
290
|
blockSelected
|
|
292
291
|
]);
|
|
293
|
-
|
|
292
|
+
React__namespace.useEffect(()=>{
|
|
293
|
+
// If the selected block is not in the list of blocks to include, change the selected block to the first one
|
|
294
|
+
if (blockKeysToInclude.length > 0 && !blockKeysToInclude.includes(blockSelected)) {
|
|
295
|
+
setBlockSelected(blockKeysToInclude[0]);
|
|
296
|
+
}
|
|
297
|
+
}, [
|
|
298
|
+
blockKeysToInclude,
|
|
299
|
+
blockSelected
|
|
300
|
+
]);
|
|
301
|
+
if (!blocks[blockSelected]) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
const selectedBlock = blocks[blockSelected];
|
|
305
|
+
const Icon = selectedBlock.icon;
|
|
294
306
|
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
295
307
|
children: [
|
|
296
308
|
/*#__PURE__*/ jsxRuntime.jsx(SelectWrapper, {
|
|
297
309
|
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
|
|
298
|
-
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icon, {}),
|
|
310
|
+
startIcon: Icon && /*#__PURE__*/ jsxRuntime.jsx(Icon, {}),
|
|
299
311
|
onChange: handleSelect,
|
|
300
|
-
customizeContent: ()=>isMobile ? '' : formatMessage(
|
|
312
|
+
customizeContent: ()=>isMobile ? '' : formatMessage(selectedBlock.label),
|
|
301
313
|
value: blockSelected,
|
|
302
314
|
onCloseAutoFocus: preventSelectFocus,
|
|
303
315
|
"aria-label": formatMessage({
|
|
@@ -305,12 +317,15 @@ const BlocksDropdown = ()=>{
|
|
|
305
317
|
defaultMessage: 'Select a block'
|
|
306
318
|
}),
|
|
307
319
|
disabled: disabled,
|
|
308
|
-
children: blockKeysToInclude.map((key)
|
|
320
|
+
children: blockKeysToInclude.map((key)=>{
|
|
321
|
+
const selectorBlock = blocks[key];
|
|
322
|
+
return /*#__PURE__*/ jsxRuntime.jsx(BlockOption, {
|
|
309
323
|
value: key,
|
|
310
|
-
label:
|
|
311
|
-
icon:
|
|
324
|
+
label: selectorBlock.label,
|
|
325
|
+
icon: selectorBlock.icon,
|
|
312
326
|
blockSelected: blockSelected
|
|
313
|
-
}, key)
|
|
327
|
+
}, key);
|
|
328
|
+
})
|
|
314
329
|
})
|
|
315
330
|
}),
|
|
316
331
|
modalElement
|
|
@@ -321,7 +336,7 @@ const BlockOption = ({ value, icon: Icon, label, blockSelected })=>{
|
|
|
321
336
|
const { formatMessage } = reactIntl.useIntl();
|
|
322
337
|
const isSelected = value === blockSelected;
|
|
323
338
|
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
|
|
324
|
-
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
339
|
+
startIcon: Icon && /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
325
340
|
fill: isSelected ? 'primary600' : 'neutral500'
|
|
326
341
|
}),
|
|
327
342
|
value: value,
|
|
@@ -390,7 +405,7 @@ const ListButton = ({ block, format, location = 'toolbar' })=>{
|
|
|
390
405
|
}
|
|
391
406
|
if (!currentListEntry) {
|
|
392
407
|
// If selection is not a list then convert it to list
|
|
393
|
-
blocks[`list-${format}`]
|
|
408
|
+
blocks[`list-${format}`]?.handleConvert(editor);
|
|
394
409
|
return;
|
|
395
410
|
}
|
|
396
411
|
// If selection is already a list then toggle format
|
|
@@ -405,14 +420,14 @@ const ListButton = ({ block, format, location = 'toolbar' })=>{
|
|
|
405
420
|
});
|
|
406
421
|
} else {
|
|
407
422
|
// Format is same, convert selected list-item to paragraph
|
|
408
|
-
blocks['paragraph']
|
|
423
|
+
blocks['paragraph']?.handleConvert(editor);
|
|
409
424
|
}
|
|
410
425
|
}
|
|
411
426
|
};
|
|
412
427
|
if (location === 'menu') {
|
|
413
428
|
const Icon = block.icon;
|
|
414
429
|
return /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
|
|
415
|
-
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icon, {}),
|
|
430
|
+
startIcon: Icon && /*#__PURE__*/ jsxRuntime.jsx(Icon, {}),
|
|
416
431
|
onSelect: ()=>toggleList(format),
|
|
417
432
|
isActive: isListActive(),
|
|
418
433
|
disabled: isListDisabled(),
|