@webiny/lexical-editor 6.4.11 → 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.
- package/components/Editor/EditorPlaceholder.d.ts +15 -0
- package/components/Editor/EditorPlaceholder.js +41 -0
- package/components/Editor/EditorPlaceholder.js.map +1 -0
- package/components/Editor/RichTextEditor.js +7 -5
- package/components/Editor/RichTextEditor.js.map +1 -1
- package/components/LexicalEditorConfig/LexicalEditorConfig.d.ts +6 -3
- package/components/Toolbar/StaticToolbar.css +95 -191
- package/components/ToolbarActions/BoldAction.js +7 -3
- package/components/ToolbarActions/BoldAction.js.map +1 -1
- package/components/ToolbarActions/BulletListAction.js +7 -3
- package/components/ToolbarActions/BulletListAction.js.map +1 -1
- package/components/ToolbarActions/CodeHighlightAction.js +7 -3
- package/components/ToolbarActions/CodeHighlightAction.js.map +1 -1
- package/components/ToolbarActions/FontSizeAction.js +2 -1
- package/components/ToolbarActions/FontSizeAction.js.map +1 -1
- package/components/ToolbarActions/ImageAction.js +3 -2
- package/components/ToolbarActions/ImageAction.js.map +1 -1
- package/components/ToolbarActions/ItalicAction.js +7 -3
- package/components/ToolbarActions/ItalicAction.js.map +1 -1
- package/components/ToolbarActions/LinkAction.js +7 -3
- package/components/ToolbarActions/LinkAction.js.map +1 -1
- package/components/ToolbarActions/NumberedListAction.js +7 -3
- package/components/ToolbarActions/NumberedListAction.js.map +1 -1
- package/components/ToolbarActions/QuoteAction.js +7 -3
- package/components/ToolbarActions/QuoteAction.js.map +1 -1
- package/components/ToolbarActions/UnderlineAction.js +7 -3
- package/components/ToolbarActions/UnderlineAction.js.map +1 -1
- package/hooks/useCurrentElement.d.ts +2 -2
- package/package.json +17 -15
- package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorController.js +1 -1
- package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorController.js.map +1 -1
- package/plugins/FloatingLinkEditorPlugin/FloatingLinkEditorPlugin.css +4 -3
- package/ui/DropDown.d.ts +4 -2
- package/ui/DropDown.js +13 -5
- package/ui/DropDown.js.map +1 -1
- package/ui/Placeholder.css +2 -1
- package/ui/Placeholder.js +1 -1
- package/ui/Placeholder.js.map +1 -1
- package/ui/ToolbarActionDialog.js +2 -1
- package/ui/ToolbarActionDialog.js.map +1 -1
- package/utils/rect.d.ts +2 -2
- package/images/icons/chat-square-quote.js +0 -19
- package/images/icons/chat-square-quote.js.map +0 -1
- package/images/icons/chat-square-quote.svg +0 -1
- package/images/icons/chevron-down.js +0 -18
- package/images/icons/chevron-down.js.map +0 -1
- package/images/icons/chevron-down.svg +0 -1
- package/images/icons/code.js +0 -17
- package/images/icons/code.js.map +0 -1
- package/images/icons/code.svg +0 -1
- package/images/icons/font-color.js +0 -17
- package/images/icons/font-color.js.map +0 -1
- package/images/icons/font-color.svg +0 -1
- package/images/icons/indent.js +0 -18
- package/images/icons/indent.js.map +0 -1
- package/images/icons/indent.svg +0 -3
- package/images/icons/insert-image.js +0 -20
- package/images/icons/insert-image.js.map +0 -1
- package/images/icons/insert-image.svg +0 -4
- package/images/icons/justify.js +0 -19
- package/images/icons/justify.js.map +0 -1
- package/images/icons/justify.svg +0 -3
- package/images/icons/link.js +0 -19
- package/images/icons/link.js.map +0 -1
- package/images/icons/link.svg +0 -1
- package/images/icons/list-ol.js +0 -20
- package/images/icons/list-ol.js.map +0 -1
- package/images/icons/list-ol.svg +0 -1
- package/images/icons/list-ul.js +0 -18
- package/images/icons/list-ul.js.map +0 -1
- package/images/icons/list-ul.svg +0 -1
- package/images/icons/outdent.js +0 -18
- package/images/icons/outdent.js.map +0 -1
- package/images/icons/outdent.svg +0 -3
- package/images/icons/text-center.js +0 -18
- package/images/icons/text-center.js.map +0 -1
- package/images/icons/text-center.svg +0 -1
- package/images/icons/text-left.js +0 -18
- package/images/icons/text-left.js.map +0 -1
- package/images/icons/text-left.svg +0 -1
- package/images/icons/text-right.js +0 -18
- package/images/icons/text-right.js.map +0 -1
- package/images/icons/text-right.svg +0 -1
- package/images/icons/type-bold.js +0 -17
- package/images/icons/type-bold.js.map +0 -1
- package/images/icons/type-bold.svg +0 -1
- package/images/icons/type-italic.js +0 -17
- package/images/icons/type-italic.js.map +0 -1
- package/images/icons/type-italic.svg +0 -1
- package/images/icons/type-strikethrough.js +0 -17
- package/images/icons/type-strikethrough.js.map +0 -1
- package/images/icons/type-strikethrough.svg +0 -1
- package/images/icons/type-underline.js +0 -17
- package/images/icons/type-underline.js.map +0 -1
- package/images/icons/type-underline.svg +0 -1
- package/static/svg/chat-square-quote.123cfa24.svg +0 -1
- package/static/svg/chevron-down.d9636921.svg +0 -1
- package/static/svg/code.912b1f4d.svg +0 -1
- package/static/svg/font-color.4f0c0de5.svg +0 -1
- package/static/svg/indent.4d78e483.svg +0 -3
- package/static/svg/insert-image.354465f1.svg +0 -4
- package/static/svg/justify.dab42aec.svg +0 -3
- package/static/svg/link.b774de25.svg +0 -1
- package/static/svg/list-ol.d64946f3.svg +0 -1
- package/static/svg/list-ul.1d54da3f.svg +0 -1
- package/static/svg/outdent.5c13ff16.svg +0 -3
- package/static/svg/text-center.a411e780.svg +0 -1
- package/static/svg/text-left.54f41f4e.svg +0 -1
- package/static/svg/text-right.9288b7a2.svg +0 -1
- package/static/svg/type-bold.7e3e270b.svg +0 -1
- package/static/svg/type-italic.d8e45748.svg +0 -1
- package/static/svg/type-strikethrough.2694a816.svg +0 -1
- 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 {
|
|
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
|
|
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:
|
|
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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"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { ToolbarElementConfig } from "./components/ToolbarElement.js";
|
|
2
|
+
import { ToolbarElement } from "./components/ToolbarElement.js";
|
|
2
3
|
import type { PluginConfig } from "./components/Plugin.js";
|
|
4
|
+
import { Plugin } from "./components/Plugin.js";
|
|
3
5
|
import type { NodeConfig } from "./components/Node.js";
|
|
6
|
+
import { Node } from "./components/Node.js";
|
|
4
7
|
export type { ToolbarElementConfig } from "./components/ToolbarElement.js";
|
|
5
8
|
export type { PluginConfig } from "./components/Plugin.js";
|
|
6
9
|
export type { NodeConfig } from "./components/Node.js";
|
|
7
10
|
export declare const LexicalEditorConfig: (({ priority, children }: import("@webiny/react-properties").ConfigProps) => React.JSX.Element) & {
|
|
8
|
-
ToolbarElement:
|
|
9
|
-
Plugin:
|
|
10
|
-
Node:
|
|
11
|
+
ToolbarElement: typeof ToolbarElement;
|
|
12
|
+
Plugin: typeof Plugin;
|
|
13
|
+
Node: typeof Node;
|
|
11
14
|
};
|
|
12
15
|
export declare const LexicalEditorWithConfig: ({ onProperties, children }: import("@webiny/react-properties").WithConfigProps) => React.JSX.Element;
|
|
13
16
|
interface LexicalEditorConfigData {
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
position: relative;
|
|
4
4
|
padding: 4px;
|
|
5
5
|
vertical-align: middle;
|
|
6
|
-
border: 1px solid
|
|
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:
|
|
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
|
-
|
|
17
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
93
|
-
height:
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
213
|
-
height:
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
246
|
+
background-color: var(--color-primary-subtle);
|
|
346
247
|
}
|
|
347
248
|
|
|
348
249
|
.toolbar .divider {
|
|
349
250
|
width: 1px;
|
|
350
|
-
background-color:
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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:
|
|
377
|
-
color: #050505;
|
|
275
|
+
padding: var(--padding-xs-plus) var(--padding-sm);
|
|
378
276
|
cursor: pointer;
|
|
379
|
-
line-height:
|
|
380
|
-
font-size:
|
|
277
|
+
line-height: 20px;
|
|
278
|
+
font-size: 14px;
|
|
381
279
|
display: flex;
|
|
382
|
-
align-
|
|
280
|
+
align-items: center;
|
|
383
281
|
flex-direction: row;
|
|
384
282
|
flex-shrink: 0;
|
|
385
|
-
|
|
386
|
-
background-color:
|
|
387
|
-
|
|
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
|
-
|
|
296
|
+
/* Selected-item check indicator (admin-ui style): a check, no background tint. */
|
|
297
|
+
.lexical-dropdown .item-check {
|
|
399
298
|
display: flex;
|
|
400
|
-
width:
|
|
401
|
-
height:
|
|
402
|
-
|
|
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:
|
|
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:
|
|
427
|
-
height:
|
|
325
|
+
width: 16px;
|
|
326
|
+
height: 16px;
|
|
327
|
+
flex-shrink: 0;
|
|
428
328
|
user-select: none;
|
|
429
|
-
|
|
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:
|
|
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
|
+
}
|