@webiny/lexical-editor 6.6.0-alpha.0 → 6.6.0-alpha.1

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.
Files changed (113) hide show
  1. package/components/Editor/EditorPlaceholder.d.ts +15 -0
  2. package/components/Editor/EditorPlaceholder.js +41 -0
  3. package/components/Editor/EditorPlaceholder.js.map +1 -0
  4. package/components/Editor/RichTextEditor.js +7 -5
  5. package/components/Editor/RichTextEditor.js.map +1 -1
  6. package/components/Toolbar/StaticToolbar.css +95 -191
  7. package/components/ToolbarActions/BoldAction.js +7 -3
  8. package/components/ToolbarActions/BoldAction.js.map +1 -1
  9. package/components/ToolbarActions/BulletListAction.js +7 -3
  10. package/components/ToolbarActions/BulletListAction.js.map +1 -1
  11. package/components/ToolbarActions/CodeHighlightAction.js +7 -3
  12. package/components/ToolbarActions/CodeHighlightAction.js.map +1 -1
  13. package/components/ToolbarActions/FontSizeAction.js +2 -1
  14. package/components/ToolbarActions/FontSizeAction.js.map +1 -1
  15. package/components/ToolbarActions/ImageAction.js +3 -2
  16. package/components/ToolbarActions/ImageAction.js.map +1 -1
  17. package/components/ToolbarActions/ItalicAction.js +7 -3
  18. package/components/ToolbarActions/ItalicAction.js.map +1 -1
  19. package/components/ToolbarActions/LinkAction.js +7 -3
  20. package/components/ToolbarActions/LinkAction.js.map +1 -1
  21. package/components/ToolbarActions/NumberedListAction.js +7 -3
  22. package/components/ToolbarActions/NumberedListAction.js.map +1 -1
  23. package/components/ToolbarActions/QuoteAction.js +7 -3
  24. package/components/ToolbarActions/QuoteAction.js.map +1 -1
  25. package/components/ToolbarActions/UnderlineAction.js +7 -3
  26. package/components/ToolbarActions/UnderlineAction.js.map +1 -1
  27. package/context/RichTextEditorContext.js +2 -1
  28. package/context/RichTextEditorContext.js.map +1 -1
  29. package/hooks/useCurrentElement.d.ts +2 -2
  30. package/package.json +16 -14
  31. package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorController.js +1 -1
  32. package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorController.js.map +1 -1
  33. package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorPlugin.css +4 -3
  34. package/ui/DropDown.d.ts +4 -2
  35. package/ui/DropDown.js +13 -5
  36. package/ui/DropDown.js.map +1 -1
  37. package/ui/Placeholder.css +2 -1
  38. package/ui/Placeholder.js +1 -1
  39. package/ui/Placeholder.js.map +1 -1
  40. package/ui/ToolbarActionDialog.js +2 -1
  41. package/ui/ToolbarActionDialog.js.map +1 -1
  42. package/images/icons/chat-square-quote.js +0 -19
  43. package/images/icons/chat-square-quote.js.map +0 -1
  44. package/images/icons/chat-square-quote.svg +0 -1
  45. package/images/icons/chevron-down.js +0 -18
  46. package/images/icons/chevron-down.js.map +0 -1
  47. package/images/icons/chevron-down.svg +0 -1
  48. package/images/icons/code.js +0 -17
  49. package/images/icons/code.js.map +0 -1
  50. package/images/icons/code.svg +0 -1
  51. package/images/icons/font-color.js +0 -17
  52. package/images/icons/font-color.js.map +0 -1
  53. package/images/icons/font-color.svg +0 -1
  54. package/images/icons/indent.js +0 -18
  55. package/images/icons/indent.js.map +0 -1
  56. package/images/icons/indent.svg +0 -3
  57. package/images/icons/insert-image.js +0 -20
  58. package/images/icons/insert-image.js.map +0 -1
  59. package/images/icons/insert-image.svg +0 -4
  60. package/images/icons/justify.js +0 -19
  61. package/images/icons/justify.js.map +0 -1
  62. package/images/icons/justify.svg +0 -3
  63. package/images/icons/link.js +0 -19
  64. package/images/icons/link.js.map +0 -1
  65. package/images/icons/link.svg +0 -1
  66. package/images/icons/list-ol.js +0 -20
  67. package/images/icons/list-ol.js.map +0 -1
  68. package/images/icons/list-ol.svg +0 -1
  69. package/images/icons/list-ul.js +0 -18
  70. package/images/icons/list-ul.js.map +0 -1
  71. package/images/icons/list-ul.svg +0 -1
  72. package/images/icons/outdent.js +0 -18
  73. package/images/icons/outdent.js.map +0 -1
  74. package/images/icons/outdent.svg +0 -3
  75. package/images/icons/text-center.js +0 -18
  76. package/images/icons/text-center.js.map +0 -1
  77. package/images/icons/text-center.svg +0 -1
  78. package/images/icons/text-left.js +0 -18
  79. package/images/icons/text-left.js.map +0 -1
  80. package/images/icons/text-left.svg +0 -1
  81. package/images/icons/text-right.js +0 -18
  82. package/images/icons/text-right.js.map +0 -1
  83. package/images/icons/text-right.svg +0 -1
  84. package/images/icons/type-bold.js +0 -17
  85. package/images/icons/type-bold.js.map +0 -1
  86. package/images/icons/type-bold.svg +0 -1
  87. package/images/icons/type-italic.js +0 -17
  88. package/images/icons/type-italic.js.map +0 -1
  89. package/images/icons/type-italic.svg +0 -1
  90. package/images/icons/type-strikethrough.js +0 -17
  91. package/images/icons/type-strikethrough.js.map +0 -1
  92. package/images/icons/type-strikethrough.svg +0 -1
  93. package/images/icons/type-underline.js +0 -17
  94. package/images/icons/type-underline.js.map +0 -1
  95. package/images/icons/type-underline.svg +0 -1
  96. package/static/svg/chat-square-quote.123cfa24.svg +0 -1
  97. package/static/svg/chevron-down.d9636921.svg +0 -1
  98. package/static/svg/code.912b1f4d.svg +0 -1
  99. package/static/svg/font-color.4f0c0de5.svg +0 -1
  100. package/static/svg/indent.4d78e483.svg +0 -3
  101. package/static/svg/insert-image.354465f1.svg +0 -4
  102. package/static/svg/justify.dab42aec.svg +0 -3
  103. package/static/svg/link.b774de25.svg +0 -1
  104. package/static/svg/list-ol.d64946f3.svg +0 -1
  105. package/static/svg/list-ul.1d54da3f.svg +0 -1
  106. package/static/svg/outdent.5c13ff16.svg +0 -3
  107. package/static/svg/text-center.a411e780.svg +0 -1
  108. package/static/svg/text-left.54f41f4e.svg +0 -1
  109. package/static/svg/text-right.9288b7a2.svg +0 -1
  110. package/static/svg/type-bold.7e3e270b.svg +0 -1
  111. package/static/svg/type-italic.d8e45748.svg +0 -1
  112. package/static/svg/type-strikethrough.2694a816.svg +0 -1
  113. package/static/svg/type-underline.104a0ed5.svg +0 -1
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ interface EditorPlaceholderProps {
3
+ text: React.ReactNode;
4
+ styles?: React.CSSProperties;
5
+ fallbackClassName?: string;
6
+ }
7
+ /**
8
+ * Renders our own placeholder, shown whenever the editor's text content is empty —
9
+ * regardless of the current block type. Lexical's built-in placeholder only shows for an
10
+ * empty paragraph, so it disappears when the block is a heading, quote, etc. The placeholder
11
+ * also picks up the current block's typography (via its style id), falling back to the
12
+ * default paragraph style, so its size/font matches whatever the user is about to type.
13
+ */
14
+ export declare const EditorPlaceholder: ({ text, styles, fallbackClassName }: EditorPlaceholderProps) => React.JSX.Element | null;
15
+ export {};
@@ -0,0 +1,41 @@
1
+ import react, { useEffect, useState } from "react";
2
+ import { $getRoot } from "lexical";
3
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
+ import { $isRootTextContentEmpty } from "@lexical/text";
5
+ import { Placeholder } from "../../ui/Placeholder.js";
6
+ import { useRichTextEditor } from "../../hooks/index.js";
7
+ const hasStyleId = (node)=>!!node && "function" == typeof node.getStyleId;
8
+ const EditorPlaceholder = ({ text, styles, fallbackClassName })=>{
9
+ const [editor] = useLexicalComposerContext();
10
+ const { theme } = useRichTextEditor();
11
+ const [state, setState] = useState({
12
+ isEmpty: true
13
+ });
14
+ useEffect(()=>{
15
+ const update = ()=>{
16
+ editor.getEditorState().read(()=>{
17
+ const isEmpty = $isRootTextContentEmpty(editor.isComposing());
18
+ const firstBlock = $getRoot().getFirstChild();
19
+ const styleId = hasStyleId(firstBlock) ? firstBlock.getStyleId() : void 0;
20
+ const className = styleId ? theme.getTypographyById(styleId)?.className : void 0;
21
+ setState({
22
+ isEmpty,
23
+ className
24
+ });
25
+ });
26
+ };
27
+ update();
28
+ return editor.registerUpdateListener(update);
29
+ }, [
30
+ editor,
31
+ theme
32
+ ]);
33
+ if (!state.isEmpty) return null;
34
+ return /*#__PURE__*/ react.createElement(Placeholder, {
35
+ styles: styles,
36
+ className: state.className ?? fallbackClassName
37
+ }, text);
38
+ };
39
+ export { EditorPlaceholder };
40
+
41
+ //# sourceMappingURL=EditorPlaceholder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components/Editor/EditorPlaceholder.js","sources":["../../../src/components/Editor/EditorPlaceholder.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport type { LexicalNode } from \"lexical\";\nimport { $getRoot } from \"lexical\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { $isRootTextContentEmpty } from \"@lexical/text\";\nimport { Placeholder } from \"~/ui/Placeholder.js\";\nimport { useRichTextEditor } from \"~/hooks/index.js\";\n\ninterface EditorPlaceholderProps {\n text: React.ReactNode;\n styles?: React.CSSProperties;\n fallbackClassName?: string;\n}\n\ninterface WithStyleId {\n getStyleId(): string | undefined;\n}\n\nconst hasStyleId = (node: LexicalNode | null): node is LexicalNode & WithStyleId => {\n return !!node && typeof (node as Partial<WithStyleId>).getStyleId === \"function\";\n};\n\n/**\n * Renders our own placeholder, shown whenever the editor's text content is empty —\n * regardless of the current block type. Lexical's built-in placeholder only shows for an\n * empty paragraph, so it disappears when the block is a heading, quote, etc. The placeholder\n * also picks up the current block's typography (via its style id), falling back to the\n * default paragraph style, so its size/font matches whatever the user is about to type.\n */\nexport const EditorPlaceholder = ({ text, styles, fallbackClassName }: EditorPlaceholderProps) => {\n const [editor] = useLexicalComposerContext();\n const { theme } = useRichTextEditor();\n const [state, setState] = useState<{ isEmpty: boolean; className?: string }>({\n isEmpty: true\n });\n\n useEffect(() => {\n const update = () => {\n editor.getEditorState().read(() => {\n const isEmpty = $isRootTextContentEmpty(editor.isComposing());\n const firstBlock = $getRoot().getFirstChild();\n const styleId = hasStyleId(firstBlock) ? firstBlock.getStyleId() : undefined;\n const className = styleId ? theme.getTypographyById(styleId)?.className : undefined;\n setState({ isEmpty, className });\n });\n };\n update();\n return editor.registerUpdateListener(update);\n }, [editor, theme]);\n\n if (!state.isEmpty) {\n return null;\n }\n\n return (\n <Placeholder styles={styles} className={state.className ?? fallbackClassName}>\n {text}\n </Placeholder>\n );\n};\n"],"names":["hasStyleId","node","EditorPlaceholder","text","styles","fallbackClassName","editor","useLexicalComposerContext","theme","useRichTextEditor","state","setState","useState","useEffect","update","isEmpty","$isRootTextContentEmpty","firstBlock","$getRoot","styleId","undefined","className","Placeholder"],"mappings":";;;;;;AAkBA,MAAMA,aAAa,CAACC,OACT,CAAC,CAACA,QAAQ,AAAqD,cAArD,OAAQA,KAA8B,UAAU;AAU9D,MAAMC,oBAAoB,CAAC,EAAEC,IAAI,EAAEC,MAAM,EAAEC,iBAAiB,EAA0B;IACzF,MAAM,CAACC,OAAO,GAAGC;IACjB,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClB,MAAM,CAACC,OAAOC,SAAS,GAAGC,SAAmD;QACzE,SAAS;IACb;IAEAC,UAAU;QACN,MAAMC,SAAS;YACXR,OAAO,cAAc,GAAG,IAAI,CAAC;gBACzB,MAAMS,UAAUC,wBAAwBV,OAAO,WAAW;gBAC1D,MAAMW,aAAaC,WAAW,aAAa;gBAC3C,MAAMC,UAAUnB,WAAWiB,cAAcA,WAAW,UAAU,KAAKG;gBACnE,MAAMC,YAAYF,UAAUX,MAAM,iBAAiB,CAACW,UAAU,YAAYC;gBAC1ET,SAAS;oBAAEI;oBAASM;gBAAU;YAClC;QACJ;QACAP;QACA,OAAOR,OAAO,sBAAsB,CAACQ;IACzC,GAAG;QAACR;QAAQE;KAAM;IAElB,IAAI,CAACE,MAAM,OAAO,EACd,OAAO;IAGX,OAAO,WAAP,GACI,oBAACY,aAAWA;QAAC,QAAQlB;QAAQ,WAAWM,MAAM,SAAS,IAAIL;OACtDF;AAGb"}
@@ -10,7 +10,7 @@ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
10
10
  import { allNodes } from "@webiny/lexical-nodes";
11
11
  import { RichTextEditorProvider } from "../../context/RichTextEditorContext.js";
12
12
  import { BlurEventPlugin } from "../../plugins/BlurEventPlugin/BlurEventPlugin.js";
13
- import { Placeholder } from "../../ui/Placeholder.js";
13
+ import { EditorPlaceholder } from "./EditorPlaceholder.js";
14
14
  import { SharedHistoryContext, useSharedHistoryContext } from "../../context/SharedHistoryContext.js";
15
15
  import { LexicalEditorWithConfig, useLexicalEditorConfig } from "../LexicalEditorConfig/LexicalEditorConfig.js";
16
16
  import { StateHandlingPlugin } from "../../plugins/StateHandlingPlugin.js";
@@ -18,9 +18,7 @@ const BaseRichTextEditor = ({ onChange, toolbar, staticToolbar, nodes, placehold
18
18
  const editorTheme = useRef(props.theme);
19
19
  const config = useLexicalEditorConfig();
20
20
  const { historyState } = useSharedHistoryContext();
21
- const placeholderElem = /*#__PURE__*/ react.createElement(Placeholder, {
22
- styles: placeholderStyles
23
- }, placeholder || "Enter text...");
21
+ const defaultParagraphClassName = props.theme?.typography?.paragraphs?.[0]?.className;
24
22
  const scrollRef = useRef(null);
25
23
  const [floatingAnchorElem, setFloatingAnchorElem] = useState(void 0);
26
24
  const onRef = (_floatingAnchorElem)=>{
@@ -93,8 +91,12 @@ const BaseRichTextEditor = ({ onChange, toolbar, staticToolbar, nodes, placehold
93
91
  ...contentEditableStyles
94
92
  }
95
93
  }))),
96
- placeholder: placeholderElem,
94
+ placeholder: null,
97
95
  ErrorBoundary: LexicalErrorBoundary
96
+ }), /*#__PURE__*/ react.createElement(EditorPlaceholder, {
97
+ text: placeholder || "Enter text...",
98
+ styles: placeholderStyles,
99
+ fallbackClassName: defaultParagraphClassName
98
100
  }), disabled ? null : floatingAnchorElem && toolbar))));
99
101
  };
100
102
  const RichTextEditor = makeDecoratable("RichTextEditor", (props)=>/*#__PURE__*/ react.createElement(LexicalEditorWithConfig, null, /*#__PURE__*/ react.createElement(BaseRichTextEditor, props)));
@@ -1 +1 @@
1
- {"version":3,"file":"components/Editor/RichTextEditor.js","sources":["../../../src/components/Editor/RichTextEditor.tsx"],"sourcesContent":["import React, { Fragment, useId, useRef, useState } from \"react\";\nimport type { Klass, LexicalNode } from \"lexical\";\nimport { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { AutoFocusPlugin } from \"@lexical/react/LexicalAutoFocusPlugin\";\nimport { ClearEditorPlugin } from \"@lexical/react/LexicalClearEditorPlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport type { EditorTheme } from \"@webiny/lexical-theme\";\nimport { allNodes } from \"@webiny/lexical-nodes\";\nimport { RichTextEditorProvider } from \"~/context/RichTextEditorContext.js\";\nimport { BlurEventPlugin } from \"~/plugins/BlurEventPlugin/BlurEventPlugin.js\";\nimport type { LexicalValue, ToolbarActionPlugin } from \"~/types.js\";\nimport { Placeholder } from \"~/ui/Placeholder.js\";\nimport { SharedHistoryContext, useSharedHistoryContext } from \"~/context/SharedHistoryContext.js\";\nimport {\n LexicalEditorWithConfig,\n useLexicalEditorConfig\n} from \"~/components/LexicalEditorConfig/LexicalEditorConfig.js\";\nimport { StateHandlingPlugin } from \"~/plugins/StateHandlingPlugin.js\";\n\nexport type InitialEditorConfig = React.ComponentProps<typeof LexicalComposer>[\"initialConfig\"] & {\n editorId: string;\n};\n\nexport interface RichTextEditorProps {\n children?: React.ReactNode | React.ReactNode[];\n classes?: string;\n disabled?: boolean;\n contentEditableStyles?: React.CSSProperties;\n focus?: boolean;\n height?: number | string;\n nodes?: Klass<LexicalNode>[];\n onBlur?: (editorState: LexicalValue) => void;\n onChange?: (value: LexicalValue) => void;\n placeholder?: string;\n placeholderStyles?: React.CSSProperties;\n staticToolbar?: React.ReactNode;\n styles?: React.CSSProperties;\n tag?: string;\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n toolbar?: React.ReactNode;\n value: LexicalValue | null | undefined;\n configRef?: React.MutableRefObject<InitialEditorConfig | undefined>;\n width?: number | string;\n}\n\nconst BaseRichTextEditor = ({\n onChange,\n toolbar,\n staticToolbar,\n nodes,\n placeholder,\n children,\n onBlur,\n focus,\n styles,\n width,\n disabled = false,\n height,\n contentEditableStyles,\n placeholderStyles,\n ...props\n}: RichTextEditorProps) => {\n const editorTheme = useRef(props.theme);\n const config = useLexicalEditorConfig();\n const { historyState } = useSharedHistoryContext();\n const placeholderElem = (\n <Placeholder styles={placeholderStyles}>{placeholder || \"Enter text...\"}</Placeholder>\n );\n const scrollRef = useRef(null);\n\n const [floatingAnchorElem, setFloatingAnchorElem] = useState<HTMLElement | undefined>(\n undefined\n );\n const onRef = (_floatingAnchorElem: HTMLDivElement) => {\n if (_floatingAnchorElem !== null) {\n setFloatingAnchorElem(_floatingAnchorElem);\n }\n };\n\n const sizeStyle = {\n height: height || \"\",\n width: width || \"\"\n };\n\n const configNodes = config.nodes.map(node => node.node);\n const configPlugins = config.plugins.map(plugin => (\n <Fragment key={plugin.name}>{plugin.element}</Fragment>\n ));\n\n const initialConfig = {\n editorId: useId(),\n editable: !disabled,\n namespace: \"webiny\",\n onError: () => {\n // Ignore errors. We don't want to break the app because of errors caused by config/value updates.\n // These are usually resolved in the next component render cycle.\n },\n nodes: [...allNodes, ...configNodes, ...(nodes || [])],\n theme: {\n ...editorTheme.current.tokens,\n // I'm not aware of a better way to pass custom data to nodes.\n // For now, we're using Lexical's theme to pass colors and typography.\n $colors: editorTheme.current.colors,\n $typography: editorTheme.current.typography,\n $cacheKey: JSON.stringify(editorTheme.current)\n }\n };\n\n if (props.configRef) {\n props.configRef.current = initialConfig;\n }\n\n return (\n /**\n * Once the LexicalComposer is mounted, it caches the `initialConfig` internally, and all future\n * updates to the config will be ignored. This is a problem because we pull in Nodes from our config,\n * and initially, there can be multiple re-renders, while the config object is settled.\n *\n * To bypass this issue, we generate a naive `key` based on the number of Nodes.\n */\n <SharedHistoryContext>\n <LexicalComposer initialConfig={initialConfig} key={initialConfig.nodes.length}>\n <RichTextEditorProvider\n theme={props.theme}\n toolbarActionPlugins={props.toolbarActionPlugins}\n >\n {staticToolbar && !disabled ? staticToolbar : null}\n <div data-role={\"overlays\"} className={\"relative\"}></div>\n <div\n /* This className is necessary for targeting of editor container from CSS files. */\n className={\"editor-shell\"}\n ref={scrollRef}\n style={{\n ...styles,\n ...sizeStyle,\n overflow: \"auto\",\n position: \"relative\"\n }}\n >\n {/* State plugins. */}\n <StateHandlingPlugin\n value={props.value}\n onChange={disabled ? undefined : onChange}\n />\n <ClearEditorPlugin />\n <HistoryPlugin externalHistoryState={historyState} />\n {/* Event plugins. */}\n {onBlur && <BlurEventPlugin onBlur={onBlur} />}\n {focus && <AutoFocusPlugin />}\n {/* External plugins and components. */}\n {configPlugins}\n {children}\n <RichTextPlugin\n contentEditable={\n <div className=\"editor-scroller\" style={{ ...sizeStyle }}>\n <div className=\"editor\" ref={onRef}>\n <ContentEditable\n disabled={disabled}\n style={{ outline: 0, ...contentEditableStyles }}\n />\n </div>\n </div>\n }\n placeholder={placeholderElem}\n ErrorBoundary={LexicalErrorBoundary}\n />\n {/* Toolbar. */}\n {disabled ? null : floatingAnchorElem && toolbar}\n </div>\n </RichTextEditorProvider>\n </LexicalComposer>\n </SharedHistoryContext>\n );\n};\n\n/**\n * @description Main editor container\n */\nexport const RichTextEditor = makeDecoratable(\"RichTextEditor\", (props: RichTextEditorProps) => {\n return (\n <LexicalEditorWithConfig>\n <BaseRichTextEditor {...props} />\n </LexicalEditorWithConfig>\n );\n});\n\nexport namespace RichTextEditor {\n export type InitialConfig = InitialEditorConfig;\n}\n"],"names":["BaseRichTextEditor","onChange","toolbar","staticToolbar","nodes","placeholder","children","onBlur","focus","styles","width","disabled","height","contentEditableStyles","placeholderStyles","props","editorTheme","useRef","config","useLexicalEditorConfig","historyState","useSharedHistoryContext","placeholderElem","Placeholder","scrollRef","floatingAnchorElem","setFloatingAnchorElem","useState","undefined","onRef","_floatingAnchorElem","sizeStyle","configNodes","node","configPlugins","plugin","Fragment","initialConfig","useId","allNodes","JSON","SharedHistoryContext","LexicalComposer","RichTextEditorProvider","StateHandlingPlugin","ClearEditorPlugin","HistoryPlugin","BlurEventPlugin","AutoFocusPlugin","RichTextPlugin","ContentEditable","LexicalErrorBoundary","RichTextEditor","makeDecoratable","LexicalEditorWithConfig"],"mappings":"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"}
1
+ {"version":3,"file":"components/Editor/RichTextEditor.js","sources":["../../../src/components/Editor/RichTextEditor.tsx"],"sourcesContent":["import React, { Fragment, useId, useRef, useState } from \"react\";\nimport type { Klass, LexicalNode } from \"lexical\";\nimport { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { AutoFocusPlugin } from \"@lexical/react/LexicalAutoFocusPlugin\";\nimport { ClearEditorPlugin } from \"@lexical/react/LexicalClearEditorPlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport type { EditorTheme } from \"@webiny/lexical-theme\";\nimport { allNodes } from \"@webiny/lexical-nodes\";\nimport { RichTextEditorProvider } from \"~/context/RichTextEditorContext.js\";\nimport { BlurEventPlugin } from \"~/plugins/BlurEventPlugin/BlurEventPlugin.js\";\nimport type { LexicalValue, ToolbarActionPlugin } from \"~/types.js\";\nimport { EditorPlaceholder } from \"~/components/Editor/EditorPlaceholder.js\";\nimport { SharedHistoryContext, useSharedHistoryContext } from \"~/context/SharedHistoryContext.js\";\nimport {\n LexicalEditorWithConfig,\n useLexicalEditorConfig\n} from \"~/components/LexicalEditorConfig/LexicalEditorConfig.js\";\nimport { StateHandlingPlugin } from \"~/plugins/StateHandlingPlugin.js\";\n\nexport type InitialEditorConfig = React.ComponentProps<typeof LexicalComposer>[\"initialConfig\"] & {\n editorId: string;\n};\n\nexport interface RichTextEditorProps {\n children?: React.ReactNode | React.ReactNode[];\n classes?: string;\n disabled?: boolean;\n contentEditableStyles?: React.CSSProperties;\n focus?: boolean;\n height?: number | string;\n nodes?: Klass<LexicalNode>[];\n onBlur?: (editorState: LexicalValue) => void;\n onChange?: (value: LexicalValue) => void;\n placeholder?: string;\n placeholderStyles?: React.CSSProperties;\n staticToolbar?: React.ReactNode;\n styles?: React.CSSProperties;\n tag?: string;\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n toolbar?: React.ReactNode;\n value: LexicalValue | null | undefined;\n configRef?: React.MutableRefObject<InitialEditorConfig | undefined>;\n width?: number | string;\n}\n\nconst BaseRichTextEditor = ({\n onChange,\n toolbar,\n staticToolbar,\n nodes,\n placeholder,\n children,\n onBlur,\n focus,\n styles,\n width,\n disabled = false,\n height,\n contentEditableStyles,\n placeholderStyles,\n ...props\n}: RichTextEditorProps) => {\n const editorTheme = useRef(props.theme);\n const config = useLexicalEditorConfig();\n const { historyState } = useSharedHistoryContext();\n // Apply the default paragraph's typography class so the placeholder matches the real\n // text's size/line-height/font (the muted color is kept via placeholderStyles).\n const defaultParagraphClassName = props.theme?.typography?.paragraphs?.[0]?.className;\n const scrollRef = useRef(null);\n\n const [floatingAnchorElem, setFloatingAnchorElem] = useState<HTMLElement | undefined>(\n undefined\n );\n const onRef = (_floatingAnchorElem: HTMLDivElement) => {\n if (_floatingAnchorElem !== null) {\n setFloatingAnchorElem(_floatingAnchorElem);\n }\n };\n\n const sizeStyle = {\n height: height || \"\",\n width: width || \"\"\n };\n\n const configNodes = config.nodes.map(node => node.node);\n const configPlugins = config.plugins.map(plugin => (\n <Fragment key={plugin.name}>{plugin.element}</Fragment>\n ));\n\n const initialConfig = {\n editorId: useId(),\n editable: !disabled,\n namespace: \"webiny\",\n onError: () => {\n // Ignore errors. We don't want to break the app because of errors caused by config/value updates.\n // These are usually resolved in the next component render cycle.\n },\n nodes: [...allNodes, ...configNodes, ...(nodes || [])],\n theme: {\n ...editorTheme.current.tokens,\n // I'm not aware of a better way to pass custom data to nodes.\n // For now, we're using Lexical's theme to pass colors and typography.\n $colors: editorTheme.current.colors,\n $typography: editorTheme.current.typography,\n $cacheKey: JSON.stringify(editorTheme.current)\n }\n };\n\n if (props.configRef) {\n props.configRef.current = initialConfig;\n }\n\n return (\n /**\n * Once the LexicalComposer is mounted, it caches the `initialConfig` internally, and all future\n * updates to the config will be ignored. This is a problem because we pull in Nodes from our config,\n * and initially, there can be multiple re-renders, while the config object is settled.\n *\n * To bypass this issue, we generate a naive `key` based on the number of Nodes.\n */\n <SharedHistoryContext>\n <LexicalComposer initialConfig={initialConfig} key={initialConfig.nodes.length}>\n <RichTextEditorProvider\n theme={props.theme}\n toolbarActionPlugins={props.toolbarActionPlugins}\n >\n {staticToolbar && !disabled ? staticToolbar : null}\n <div data-role={\"overlays\"} className={\"relative\"}></div>\n <div\n /* This className is necessary for targeting of editor container from CSS files. */\n className={\"editor-shell\"}\n ref={scrollRef}\n style={{\n ...styles,\n ...sizeStyle,\n overflow: \"auto\",\n position: \"relative\"\n }}\n >\n {/* State plugins. */}\n <StateHandlingPlugin\n value={props.value}\n onChange={disabled ? undefined : onChange}\n />\n <ClearEditorPlugin />\n <HistoryPlugin externalHistoryState={historyState} />\n {/* Event plugins. */}\n {onBlur && <BlurEventPlugin onBlur={onBlur} />}\n {focus && <AutoFocusPlugin />}\n {/* External plugins and components. */}\n {configPlugins}\n {children}\n <RichTextPlugin\n contentEditable={\n <div className=\"editor-scroller\" style={{ ...sizeStyle }}>\n <div className=\"editor\" ref={onRef}>\n <ContentEditable\n disabled={disabled}\n style={{ outline: 0, ...contentEditableStyles }}\n />\n </div>\n </div>\n }\n placeholder={null}\n ErrorBoundary={LexicalErrorBoundary}\n />\n <EditorPlaceholder\n text={placeholder || \"Enter text...\"}\n styles={placeholderStyles}\n fallbackClassName={defaultParagraphClassName}\n />\n {/* Toolbar. */}\n {disabled ? null : floatingAnchorElem && toolbar}\n </div>\n </RichTextEditorProvider>\n </LexicalComposer>\n </SharedHistoryContext>\n );\n};\n\n/**\n * @description Main editor container\n */\nexport const RichTextEditor = makeDecoratable(\"RichTextEditor\", (props: RichTextEditorProps) => {\n return (\n <LexicalEditorWithConfig>\n <BaseRichTextEditor {...props} />\n </LexicalEditorWithConfig>\n );\n});\n\nexport namespace RichTextEditor {\n export type InitialConfig = InitialEditorConfig;\n}\n"],"names":["BaseRichTextEditor","onChange","toolbar","staticToolbar","nodes","placeholder","children","onBlur","focus","styles","width","disabled","height","contentEditableStyles","placeholderStyles","props","editorTheme","useRef","config","useLexicalEditorConfig","historyState","useSharedHistoryContext","defaultParagraphClassName","scrollRef","floatingAnchorElem","setFloatingAnchorElem","useState","undefined","onRef","_floatingAnchorElem","sizeStyle","configNodes","node","configPlugins","plugin","Fragment","initialConfig","useId","allNodes","JSON","SharedHistoryContext","LexicalComposer","RichTextEditorProvider","StateHandlingPlugin","ClearEditorPlugin","HistoryPlugin","BlurEventPlugin","AutoFocusPlugin","RichTextPlugin","ContentEditable","LexicalErrorBoundary","EditorPlaceholder","RichTextEditor","makeDecoratable","LexicalEditorWithConfig"],"mappings":"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"}
@@ -3,9 +3,13 @@
3
3
  position: relative;
4
4
  padding: 4px;
5
5
  vertical-align: middle;
6
- border: 1px solid #eee;
6
+ border: 1px solid var(--border-color-neutral-muted);
7
+ /* No divider between toolbar and body (matches Figma): the body carries the
8
+ bottom border, the toolbar the top — together one seamless rounded box. */
9
+ border-bottom: none;
10
+ border-radius: var(--radius-md) var(--radius-md) 0 0;
7
11
  will-change: transform;
8
- background: #fff;
12
+ background: var(--color-neutral-base);
9
13
  flex-wrap: wrap;
10
14
  z-index: 1;
11
15
  }
@@ -13,8 +17,10 @@
13
17
  .static-toolbar button.popup-item {
14
18
  border: 0;
15
19
  display: flex;
16
- background: #fff;
17
- border-radius: 10px;
20
+ align-items: center;
21
+ justify-content: center;
22
+ background: transparent;
23
+ border-radius: var(--radius-md);
18
24
  padding: 8px;
19
25
  cursor: pointer;
20
26
  vertical-align: middle;
@@ -28,14 +34,11 @@
28
34
  margin-right: 2px;
29
35
  }
30
36
 
31
- .static-toolbar button.popup-item i.format {
32
- background-size: contain;
33
- display: inline-block;
34
- height: 18px;
35
- width: 18px;
36
- margin-top: 2px;
37
- vertical-align: -0.25em;
37
+ .static-toolbar button.popup-item .format {
38
38
  display: flex;
39
+ height: 16px;
40
+ width: 16px;
41
+ flex-shrink: 0;
39
42
  }
40
43
 
41
44
  .static-toolbar button.popup-item:disabled i.format {
@@ -43,7 +46,7 @@
43
46
  }
44
47
 
45
48
  .static-toolbar button.popup-item.active {
46
- background-color: rgba(223, 232, 250, 0.3);
49
+ background-color: var(--color-primary-subtle);
47
50
  }
48
51
 
49
52
  .static-toolbar button.popup-item.active i {
@@ -51,20 +54,21 @@
51
54
  }
52
55
 
53
56
  .static-toolbar .popup-item:hover:not([disabled]) {
54
- background-color: #eee;
57
+ /* Matches admin-ui ghost IconButton hover (bg-neutral-dark/5). */
58
+ background-color: color-mix(in oklab, var(--color-neutral-dark) 5%, transparent);
55
59
  }
56
60
 
57
61
  .static-toolbar select.popup-item {
58
62
  border: 0;
59
63
  display: flex;
60
- border-radius: 10px;
64
+ border-radius: var(--radius-md);
61
65
  padding: 8px;
62
66
  vertical-align: middle;
63
67
  -webkit-appearance: none;
64
68
  -moz-appearance: none;
65
69
  width: 70px;
66
70
  font-size: 14px;
67
- color: #777;
71
+ color: var(--text-color-neutral-strong);
68
72
  text-overflow: ellipsis;
69
73
  }
70
74
 
@@ -79,7 +83,7 @@
79
83
  width: 200px;
80
84
  vertical-align: middle;
81
85
  font-size: 14px;
82
- color: #777;
86
+ color: var(--text-color-neutral-strong);
83
87
  text-overflow: ellipsis;
84
88
  width: 70px;
85
89
  overflow: hidden;
@@ -89,14 +93,14 @@
89
93
 
90
94
  .static-toolbar .popup-item .icon {
91
95
  display: flex;
92
- width: 20px;
93
- height: 20px;
96
+ width: 16px;
97
+ height: 16px;
94
98
  user-select: none;
95
99
  line-height: 16px;
96
100
  background-size: contain;
97
101
  }
98
102
 
99
- .static-toolbar i.chevron-down {
103
+ .static-toolbar .chevron-down {
100
104
  margin-top: 3px;
101
105
  width: 16px;
102
106
  height: 16px;
@@ -104,7 +108,7 @@
104
108
  user-select: none;
105
109
  }
106
110
 
107
- .static-toolbar i.chevron-down.inside {
111
+ .static-toolbar .chevron-down.inside {
108
112
  width: 16px;
109
113
  height: 16px;
110
114
  display: flex;
@@ -116,30 +120,20 @@
116
120
 
117
121
  .static-toolbar .divider {
118
122
  width: 1px;
119
- background-color: #eee;
123
+ background-color: var(--border-color-neutral-dimmed);
120
124
  margin: 0 4px;
121
125
  }
122
126
 
123
- .static-toolbar .icon.bullet-list,
124
- .static-toolbar .icon.bullet {
125
- background-image: url("../../images/icons/list-ul.svg");
126
- }
127
-
128
- .static-toolbar .icon.numbered-list,
129
- .static-toolbar .icon.number {
130
- background-image: url("../../images/icons/list-ol.svg");
131
- }
132
-
133
127
  .static-toolbar .link-editor .button.active,
134
128
  .static-toolbar .button.active {
135
- background-color: rgb(223, 232, 250);
129
+ background-color: var(--color-primary-subtle);
136
130
  }
137
131
 
138
132
  .static-toolbar button.toolbar-item {
139
133
  border: 0;
140
134
  display: flex;
141
135
  background: none;
142
- border-radius: 10px;
136
+ border-radius: var(--radius-md);
143
137
  padding: 8px;
144
138
  cursor: pointer;
145
139
  vertical-align: middle;
@@ -156,14 +150,11 @@
156
150
  margin-right: 2px;
157
151
  }
158
152
 
159
- .static-toolbar button.toolbar-item i.format {
160
- background-size: contain;
161
- display: inline-block;
162
- height: 18px;
163
- width: 18px;
164
- vertical-align: -0.25em;
153
+ .static-toolbar button.toolbar-item .format {
165
154
  display: flex;
166
- opacity: 0.6;
155
+ height: 16px;
156
+ width: 16px;
157
+ flex-shrink: 0;
167
158
  }
168
159
 
169
160
  .static-toolbar button.toolbar-item:disabled .icon,
@@ -174,7 +165,7 @@
174
165
  }
175
166
 
176
167
  .static-toolbar button.toolbar-item.active {
177
- background-color: rgba(223, 232, 250, 0.3);
168
+ background-color: var(--color-primary-subtle);
178
169
  }
179
170
 
180
171
  .static-toolbar button.toolbar-item.active i {
@@ -182,7 +173,8 @@
182
173
  }
183
174
 
184
175
  .static-toolbar .toolbar-item:hover:not([disabled]) {
185
- background-color: #eee;
176
+ /* Matches admin-ui ghost IconButton hover (bg-neutral-dark/5). */
177
+ background-color: color-mix(in oklab, var(--color-neutral-dark) 5%, transparent);
186
178
  }
187
179
 
188
180
  .static-toolbar .toolbar-item.font-family .text {
@@ -194,12 +186,20 @@
194
186
  width: 150px;
195
187
  }
196
188
 
189
+ /* Fixed-width label so the trigger doesn't resize as the selected heading changes. */
190
+ .static-toolbar .block-type-dropdown .dropdown-button-text {
191
+ width: 92px;
192
+ overflow: hidden;
193
+ white-space: nowrap;
194
+ text-overflow: ellipsis;
195
+ }
196
+
197
197
  .static-toolbar .toolbar-item .text {
198
198
  display: flex;
199
199
  line-height: 20px;
200
200
  vertical-align: middle;
201
201
  font-size: 14px;
202
- color: #777;
202
+ color: var(--text-color-neutral-strong);
203
203
  text-overflow: ellipsis;
204
204
  overflow: hidden;
205
205
  height: 20px;
@@ -209,15 +209,15 @@
209
209
 
210
210
  .static-toolbar .toolbar-item .icon {
211
211
  display: flex;
212
- width: 20px;
213
- height: 20px;
212
+ width: 16px;
213
+ height: 16px;
214
214
  user-select: none;
215
215
  margin-right: 8px;
216
216
  line-height: 16px;
217
217
  background-size: contain;
218
218
  }
219
219
 
220
- .static-toolbar i.chevron-down {
220
+ .static-toolbar .chevron-down {
221
221
  margin-top: 3px;
222
222
  width: 16px;
223
223
  height: 16px;
@@ -225,7 +225,7 @@
225
225
  user-select: none;
226
226
  }
227
227
 
228
- .static-toolbar i.chevron-down.inside {
228
+ .static-toolbar .chevron-down.inside {
229
229
  width: 16px;
230
230
  height: 16px;
231
231
  display: flex;
@@ -237,129 +237,28 @@
237
237
 
238
238
  .static-toolbar .divider {
239
239
  width: 1px;
240
- background-color: #eee;
240
+ background-color: var(--border-color-neutral-dimmed);
241
241
  margin: 0 4px;
242
242
  }
243
243
 
244
- .static-toolbar button.item i {
245
- opacity: 0.6;
246
- }
247
-
248
- .static-toolbar button.item.dropdown-item-active {
249
- background-color: rgba(223, 232, 250, 0.3);
250
- }
251
-
252
- .static-toolbar button.item.dropdown-item-active i {
253
- opacity: 1;
254
- }
255
-
256
- i.bold {
257
- background-image: url("../../images/icons/type-bold.svg");
258
- }
259
-
260
- i.italic {
261
- background-image: url("../../images/icons/type-italic.svg");
262
- }
263
-
264
- i.code {
265
- background-image: url("../../images/icons/code.svg");
266
- }
267
-
268
- i.underline {
269
- background-image: url("../../images/icons/type-underline.svg");
270
- }
271
-
272
- i.strikethrough {
273
- background-image: url("../../images/icons/type-strikethrough.svg");
274
- }
275
-
276
- i.link {
277
- background-image: url("../../images/icons/link.svg");
278
- }
279
-
280
- i.quote {
281
- background-image: url("../../images/icons/chat-square-quote.svg");
282
- }
283
-
284
- .icon.left-align,
285
- i.left-align {
286
- background-image: url("../../images/icons/text-left.svg");
287
- }
288
-
289
- i.center-align {
290
- background-image: url("../../images/icons/text-center.svg");
291
- }
292
-
293
- i.right-align {
294
- background-image: url("../../images/icons/text-right.svg");
295
- }
296
-
297
- i.indent {
298
- background-image: url("../../images/icons/indent.svg");
299
- }
300
-
301
- i.outdent {
302
- background-image: url("../../images/icons/outdent.svg");
303
- }
304
-
305
- i.justify-align {
306
- background-image: url("../../images/icons/justify.svg");
307
- }
308
-
309
- i.chevron-down {
310
- background-color: transparent;
311
- background-size: contain;
312
- display: inline-block;
313
- height: 8px;
314
- width: 8px;
315
- background-image: url("../../images/icons/chevron-down.svg");
316
- }
317
-
318
- i.insert-image,
319
- .icon.insert-image {
320
- background-color: transparent;
321
- background-size: contain;
322
- display: inline-block;
323
- height: 8px;
324
- width: 8px;
325
- background-image: url("../../images/icons/insert-image.svg");
326
- }
327
-
328
- .icon.bullet-list,
329
- .icon.bullet {
330
- background-image: url("../../images/icons/list-ul.svg");
331
- }
332
-
333
- .icon.numbered-list,
334
- .icon.number {
335
- background-image: url("../../images/icons/list-ol.svg");
336
- }
337
-
338
- i.font-color,
339
- .icon.font-color {
340
- background-image: url("../../images/icons/font-color.svg");
341
- }
342
-
343
244
  .link-editor .button.active,
344
245
  .toolbar .button.active {
345
- background-color: rgb(223, 232, 250);
246
+ background-color: var(--color-primary-subtle);
346
247
  }
347
248
 
348
249
  .toolbar .divider {
349
250
  width: 1px;
350
- background-color: #eee;
251
+ background-color: var(--border-color-neutral-dimmed);
351
252
  margin: 0 4px;
352
253
  }
353
254
 
354
255
  .lexical-dropdown {
256
+ /* Matches admin-ui DropdownMenu: border-defined (no heavy shadow), rounded-md. */
355
257
  display: block;
356
- box-shadow:
357
- 0 12px 28px 0 rgba(0, 0, 0, 0.2),
358
- 0 2px 4px 0 rgba(0, 0, 0, 0.1),
359
- inset 0 0 0 1px rgba(255, 255, 255, 0.5);
360
- border-radius: 8px;
361
- min-height: 40px;
362
- background-color: #fff;
258
+ border: 1px solid var(--border-color-neutral-muted);
259
+ border-radius: var(--radius-md);
260
+ background-color: var(--color-neutral-base);
261
+ color: var(--text-color-neutral-primary);
363
262
  max-height: 250px;
364
263
  overflow: hidden;
365
264
  overflow-y: auto;
@@ -373,21 +272,20 @@ i.font-color,
373
272
 
374
273
  .lexical-dropdown .item {
375
274
  margin: 0 8px 0 8px;
376
- padding: 8px;
377
- color: #050505;
275
+ padding: var(--padding-xs-plus) var(--padding-sm);
378
276
  cursor: pointer;
379
- line-height: 16px;
380
- font-size: 15px;
277
+ line-height: 20px;
278
+ font-size: 14px;
381
279
  display: flex;
382
- align-content: center;
280
+ align-items: center;
383
281
  flex-direction: row;
384
282
  flex-shrink: 0;
385
- justify-content: space-between;
386
- background-color: #fff;
387
- border-radius: 8px;
283
+ gap: var(--spacing-sm);
284
+ background-color: transparent;
285
+ color: var(--text-color-neutral-primary);
286
+ border-radius: var(--radius-sm);
388
287
  border: 0;
389
288
  max-width: 250px;
390
- min-width: 100px;
391
289
  }
392
290
 
393
291
  .lexical-dropdown .item.fontsize-item,
@@ -395,11 +293,13 @@ i.font-color,
395
293
  min-width: unset;
396
294
  }
397
295
 
398
- .lexical-dropdown .item .active {
296
+ /* Selected-item check indicator (admin-ui style): a check, no background tint. */
297
+ .lexical-dropdown .item-check {
399
298
  display: flex;
400
- width: 20px;
401
- height: 20px;
402
- background-size: contain;
299
+ width: 16px;
300
+ height: 16px;
301
+ flex-shrink: 0;
302
+ fill: var(--fill-neutral-xstrong);
403
303
  }
404
304
 
405
305
  .lexical-dropdown .item:first-child {
@@ -411,31 +311,27 @@ i.font-color,
411
311
  }
412
312
 
413
313
  .lexical-dropdown .item:hover {
414
- background-color: #eee;
314
+ background-color: var(--color-neutral-dimmed);
415
315
  }
416
316
 
417
317
  .lexical-dropdown .item .text {
418
318
  display: flex;
419
319
  line-height: 20px;
420
320
  flex-grow: 1;
421
- min-width: 150px;
422
321
  }
423
322
 
424
323
  .lexical-dropdown .item .icon {
425
324
  display: flex;
426
- width: 20px;
427
- height: 20px;
325
+ width: 16px;
326
+ height: 16px;
327
+ flex-shrink: 0;
428
328
  user-select: none;
429
- margin-right: 12px;
430
- line-height: 16px;
431
- background-size: contain;
432
- background-position: center;
433
- background-repeat: no-repeat;
329
+ fill: var(--fill-neutral-xstrong);
434
330
  }
435
331
 
436
332
  .lexical-dropdown .divider {
437
333
  width: auto;
438
- background-color: #eee;
334
+ background-color: var(--border-color-neutral-dimmed);
439
335
  margin: 4px 8px;
440
336
  height: 1px;
441
337
  }
@@ -452,18 +348,6 @@ i.font-color,
452
348
  }
453
349
  }
454
350
 
455
- button.item i {
456
- opacity: 0.6;
457
- }
458
-
459
- button.item.dropdown-item-active {
460
- background-color: rgba(223, 232, 250, 0.3);
461
- }
462
-
463
- button.item.dropdown-item-active i {
464
- opacity: 1;
465
- }
466
-
467
351
  .editor-shell span.editor-image {
468
352
  cursor: default;
469
353
  display: inline-block;
@@ -660,3 +544,23 @@ button.item.dropdown-item-active i {
660
544
  color: #000;
661
545
  overflow: hidden;
662
546
  }
547
+
548
+ /*
549
+ * Toolbar icons are inline SVGs from @webiny/icons (Material Symbols), replacing the
550
+ * legacy background-image glyphs. Sizing is handled per-button above; color via `fill`.
551
+ */
552
+ .static-toolbar .format,
553
+ .static-toolbar .icon,
554
+ .lexical-dropdown .item .icon {
555
+ fill: var(--fill-neutral-xstrong);
556
+ }
557
+
558
+ .static-toolbar .chevron-down {
559
+ fill: var(--fill-neutral-strong);
560
+ }
561
+
562
+ .static-toolbar button:disabled .format,
563
+ .static-toolbar button:disabled .icon,
564
+ .static-toolbar button:disabled .chevron-down {
565
+ fill: var(--fill-neutral-disabled);
566
+ }
@@ -2,6 +2,8 @@ import react from "react";
2
2
  import { FORMAT_TEXT_COMMAND } from "lexical";
3
3
  import { useCurrentSelection } from "../../hooks/useCurrentSelection.js";
4
4
  import { useRichTextEditor } from "../../hooks/index.js";
5
+ import { ReactComponent } from "@webiny/icons/format_bold.svg";
6
+ import clsx from "clsx";
5
7
  const BoldAction = ()=>{
6
8
  const { editor } = useRichTextEditor();
7
9
  const { rangeSelection } = useCurrentSelection();
@@ -11,10 +13,12 @@ const BoldAction = ()=>{
11
13
  };
12
14
  return /*#__PURE__*/ react.createElement("button", {
13
15
  onClick: handleClick,
14
- className: "popup-item spaced " + (isBoldSelected ? "active" : ""),
16
+ className: clsx("popup-item", "spaced", {
17
+ active: isBoldSelected
18
+ }),
15
19
  "aria-label": "Format text as bold"
16
- }, /*#__PURE__*/ react.createElement("i", {
17
- className: "format bold"
20
+ }, /*#__PURE__*/ react.createElement(ReactComponent, {
21
+ className: "format"
18
22
  }));
19
23
  };
20
24
  export { BoldAction };