@inshapardaz/likhari-react 0.1.2-dev.20 → 0.1.3
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/EditorRoot.d.ts +0 -11
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +2 -2
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +29 -55
- package/dist/theme/editor.css +58 -29
- package/package.json +1 -1
package/dist/EditorRoot.d.ts
CHANGED
|
@@ -25,18 +25,7 @@ export interface EditorRootProps {
|
|
|
25
25
|
*/
|
|
26
26
|
height?: string | number;
|
|
27
27
|
onChange?: (state: SerializedEditorState) => void;
|
|
28
|
-
/**
|
|
29
|
-
* Handler for the toolbar's Save button. Receives the serialized content
|
|
30
|
-
* (plain text when that format is enabled, otherwise Lexical JSON).
|
|
31
|
-
*/
|
|
32
28
|
onSave?: (content: string, format: FormatId) => void;
|
|
33
|
-
/**
|
|
34
|
-
* Whether the Save button (icon-only) is shown. Defaults to `true` when
|
|
35
|
-
* `onSave` is provided and `false` otherwise, i.e. the previous behavior.
|
|
36
|
-
* Set explicitly to show the button without a handler (saving still clears
|
|
37
|
-
* the unsaved-changes state) or to hide it while keeping `onSave`.
|
|
38
|
-
*/
|
|
39
|
-
showSave?: boolean;
|
|
40
29
|
}
|
|
41
30
|
export interface EditorRef {
|
|
42
31
|
getContent(format: FormatId): string;
|
package/dist/EditorRoot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAS1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAMzH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD
|
|
1
|
+
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAS1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAMzH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAoHrB,CAAC"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -20,7 +20,7 @@ function initialEditorStateJson(initialContent) {
|
|
|
20
20
|
const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
|
|
21
21
|
return JSON.stringify(state);
|
|
22
22
|
}
|
|
23
|
-
export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, locale = 'en', placeholder = 'Start writing…', height = '480px', onChange, onSave,
|
|
23
|
+
export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, locale = 'en', placeholder = 'Start writing…', height = '480px', onChange, onSave, }, ref) {
|
|
24
24
|
const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
|
|
25
25
|
const editorStateRef = useRef(null);
|
|
26
26
|
const lastSavedJsonRef = useRef(null);
|
|
@@ -84,5 +84,5 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
84
84
|
onSave?.(content, format);
|
|
85
85
|
};
|
|
86
86
|
const dir = locale === 'en' ? 'ltr' : 'rtl';
|
|
87
|
-
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, children: _jsx("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, isDirty: isDirty, showSave:
|
|
87
|
+
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, children: _jsx("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: onSave ? handleSave : undefined, isDirty: isDirty, showSave: Boolean(onSave) }), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": "Editor content" }), placeholder: _jsx("div", { className: "likhari-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.links && _jsx(LinkPlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange })] }) }) }));
|
|
88
88
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAsH7E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,YAAY,+BAgY1E"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { Menu, Select } from '@mantine/core';
|
|
4
3
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
5
|
-
import { $getSelection, $isRangeSelection, $isElementNode,
|
|
4
|
+
import { $getSelection, $isRangeSelection, $isElementNode, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND, UNDO_COMMAND, REDO_COMMAND, SELECTION_CHANGE_COMMAND, CAN_UNDO_COMMAND, CAN_REDO_COMMAND, COMMAND_PRIORITY_LOW, } from 'lexical';
|
|
6
5
|
import { $setBlocksType } from '@lexical/selection';
|
|
7
6
|
import { $createParagraphNode } from 'lexical';
|
|
8
7
|
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode } from '@lexical/rich-text';
|
|
@@ -27,8 +26,8 @@ const ALIGN_ICONS = {
|
|
|
27
26
|
right: IconAlignRight,
|
|
28
27
|
justify: IconAlignJustified,
|
|
29
28
|
};
|
|
30
|
-
function ToolbarButton({ icon: Icon, title, active,
|
|
31
|
-
return (_jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": active ? 'true' : 'false',
|
|
29
|
+
function ToolbarButton({ icon: Icon, title, active, disabled, onClick, }) {
|
|
30
|
+
return (_jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": active ? 'true' : 'false', disabled: disabled, "aria-pressed": active, "aria-label": title, title: title, onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }) }));
|
|
32
31
|
}
|
|
33
32
|
/** A disabled placeholder for a feature whose config flag is on but that
|
|
34
33
|
* isn't implemented yet (link/image/poetry/font/language-tooling — see
|
|
@@ -37,21 +36,16 @@ function ToolbarButton({ icon: Icon, title, active, dirty, disabled, onClick, })
|
|
|
37
36
|
function StubButton({ icon, title }) {
|
|
38
37
|
return _jsx(ToolbarButton, { icon: icon, title: `${title} (coming soon)`, disabled: true, onClick: undefined });
|
|
39
38
|
}
|
|
40
|
-
/**
|
|
41
|
-
* labels the control itself
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function ToolbarSelect({ icon: Icon, label, value, data, width, disabled, placeholder, onChange }) {
|
|
45
|
-
return (_jsx(Select, { size: "xs", w: width, "aria-label": label, title: disabled ? `${label} (coming soon)` : label, data: data, value: value, placeholder: placeholder, disabled: disabled, allowDeselect: false, leftSection: _jsx(Icon, { size: 15, stroke: ICON_STROKE }), comboboxProps: { withinPortal: true, position: 'bottom-start', middlewares: { flip: true, shift: true } }, classNames: { input: 'likhari-toolbar-select-input', dropdown: 'likhari-toolbar-select-dropdown' }, onChange: (v) => v && onChange?.(v) }));
|
|
46
|
-
}
|
|
47
|
-
function OverflowItem({ icon: Icon, label, active, onClick, }) {
|
|
48
|
-
return (_jsx(Menu.Item, { leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), "data-active": active ? 'true' : 'false', className: "likhari-overflow-item",
|
|
49
|
-
// Keep the editor's selection: a mousedown here would blur the canvas.
|
|
50
|
-
onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: label }));
|
|
39
|
+
/** Icon shown to the left of a <select> — native selects can't render icons
|
|
40
|
+
* per-option, so this labels the control itself instead. */
|
|
41
|
+
function SelectIcon({ icon: Icon }) {
|
|
42
|
+
return (_jsx("span", { className: "likhari-toolbar-select-icon", "aria-hidden": "true", children: _jsx(Icon, { size: 15, stroke: ICON_STROKE }) }));
|
|
51
43
|
}
|
|
52
44
|
export function Toolbar({ config, onSave, isDirty, showSave }) {
|
|
53
45
|
const [editor] = useLexicalComposerContext();
|
|
54
46
|
const [state, setState] = useState(INITIAL_STATE);
|
|
47
|
+
const [overflowOpen, setOverflowOpen] = useState(false);
|
|
48
|
+
const overflowRef = useRef(null);
|
|
55
49
|
const updateToolbar = useCallback(() => {
|
|
56
50
|
editor.getEditorState().read(() => {
|
|
57
51
|
const selection = $getSelection();
|
|
@@ -105,6 +99,25 @@ export function Toolbar({ config, onSave, isDirty, showSave }) {
|
|
|
105
99
|
return false;
|
|
106
100
|
}, COMMAND_PRIORITY_LOW);
|
|
107
101
|
}, [editor]);
|
|
102
|
+
// Close the overflow panel on outside click or Escape.
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (!overflowOpen)
|
|
105
|
+
return;
|
|
106
|
+
const handlePointerDown = (e) => {
|
|
107
|
+
if (overflowRef.current && !overflowRef.current.contains(e.target))
|
|
108
|
+
setOverflowOpen(false);
|
|
109
|
+
};
|
|
110
|
+
const handleKeyDown = (e) => {
|
|
111
|
+
if (e.key === 'Escape')
|
|
112
|
+
setOverflowOpen(false);
|
|
113
|
+
};
|
|
114
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
115
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
116
|
+
return () => {
|
|
117
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
118
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
119
|
+
};
|
|
120
|
+
}, [overflowOpen]);
|
|
108
121
|
const setBlockType = (type) => {
|
|
109
122
|
editor.update(() => {
|
|
110
123
|
const selection = $getSelection();
|
|
@@ -138,29 +151,6 @@ export function Toolbar({ config, onSave, isDirty, showSave }) {
|
|
|
138
151
|
}
|
|
139
152
|
setBlockType(value);
|
|
140
153
|
};
|
|
141
|
-
/** Runs a toolbar-select action, then returns focus to the canvas — the
|
|
142
|
-
* combobox otherwise keeps it, so typing after picking would go nowhere. */
|
|
143
|
-
// Opening the overflow Menu moves focus into its dropdown, which makes
|
|
144
|
-
// Lexical drop the canvas selection — so snapshot it on open and restore it
|
|
145
|
-
// before an item's action runs.
|
|
146
|
-
const menuSelectionRef = useRef(null);
|
|
147
|
-
const snapshotSelection = () => {
|
|
148
|
-
editor.getEditorState().read(() => {
|
|
149
|
-
const selection = $getSelection();
|
|
150
|
-
menuSelectionRef.current = selection ? selection.clone() : null;
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
const runOverflowAction = (action) => () => {
|
|
154
|
-
const saved = menuSelectionRef.current;
|
|
155
|
-
if (saved)
|
|
156
|
-
editor.update(() => $setSelection(saved.clone()), { discrete: true });
|
|
157
|
-
action();
|
|
158
|
-
editor.focus();
|
|
159
|
-
};
|
|
160
|
-
const withRefocus = (fn) => (value) => {
|
|
161
|
-
fn(value);
|
|
162
|
-
editor.focus();
|
|
163
|
-
};
|
|
164
154
|
const formatText = (format) => editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
|
165
155
|
const formatElement = (format) => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, format);
|
|
166
156
|
const applyCaseTransform = (transform) => {
|
|
@@ -208,23 +198,7 @@ export function Toolbar({ config, onSave, isDirty, showSave }) {
|
|
|
208
198
|
const showInsertPoetryGroup = config.links || config.images.linked || config.images.embedded || config.poetry.enabled;
|
|
209
199
|
const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
|
|
210
200
|
const showOverflowMenu = fmt.strikethrough || fmt.superscript || fmt.subscript || fmt.caseTransforms || fmt.clearFormatting || config.indent;
|
|
211
|
-
const formattingOptions = [
|
|
212
|
-
{ value: 'paragraph', label: 'Paragraph' },
|
|
213
|
-
...headingLevels.map((level) => ({ value: `h${level}`, label: `Heading ${level}` })),
|
|
214
|
-
...(config.lists.numbered ? [{ value: 'number', label: 'Numbered list' }] : []),
|
|
215
|
-
...(config.lists.bullet ? [{ value: 'bullet', label: 'Bullet list' }] : []),
|
|
216
|
-
...(config.lists.check ? [{ value: 'check', label: 'Task list' }] : []),
|
|
217
|
-
...(config.blocks.quote ? [{ value: 'quote', label: 'Quote' }] : []),
|
|
218
|
-
];
|
|
219
|
-
const alignOptions = [
|
|
220
|
-
...(config.alignment.start ? [{ value: 'start', label: 'Align start' }] : []),
|
|
221
|
-
...(config.alignment.center ? [{ value: 'center', label: 'Align center' }] : []),
|
|
222
|
-
...(config.alignment.start ? [{ value: 'end', label: 'Align end' }] : []),
|
|
223
|
-
...(config.alignment.justify ? [{ value: 'justify', label: 'Justify' }] : []),
|
|
224
|
-
...(config.alignment.left ? [{ value: 'left', label: 'Align left' }] : []),
|
|
225
|
-
...(config.alignment.right ? [{ value: 'right', label: 'Align right' }] : []),
|
|
226
|
-
];
|
|
227
201
|
const formattingValue = state.listType ?? state.blockType;
|
|
228
202
|
const AlignIcon = ALIGN_ICONS[state.elementFormat] ?? IconAlignLeft;
|
|
229
|
-
return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": "Formatting", children: [showSave && (
|
|
203
|
+
return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": "Formatting", children: [showSave && (_jsxs("button", { type: "button", className: "likhari-save-button", "data-dirty": isDirty ? 'true' : 'false', onClick: onSave, children: [_jsx(IconDeviceFloppy, { size: 16, stroke: ICON_STROKE }), "Save"] })), config.history && (_jsxs("div", { className: "likhari-toolbar-group", children: [_jsx(ToolbarButton, { icon: IconArrowBackUp, title: "Undo", disabled: !state.canUndo, onClick: () => editor.dispatchCommand(UNDO_COMMAND, undefined) }), _jsx(ToolbarButton, { icon: IconArrowForwardUp, title: "Redo", disabled: !state.canRedo, onClick: () => editor.dispatchCommand(REDO_COMMAND, undefined) })] })), showFormattingGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-select-group", children: [_jsx(SelectIcon, { icon: IconPilcrow }), _jsxs("select", { className: "likhari-toolbar-select", "aria-label": "Formatting", value: formattingValue, onChange: (e) => applyFormatting(e.target.value), children: [_jsx("option", { value: "paragraph", children: "Paragraph" }), headingLevels.map((level) => (_jsxs("option", { value: `h${level}`, children: ["Heading ", level] }, level))), config.lists.numbered && _jsx("option", { value: "number", children: "Numbered list" }), config.lists.bullet && _jsx("option", { value: "bullet", children: "Bullet list" }), config.lists.check && _jsx("option", { value: "check", children: "Task list" }), config.blocks.quote && _jsx("option", { value: "quote", children: "Quote" })] })] })), showInlineGroup && (_jsxs("div", { className: "likhari-toolbar-group", children: [fmt.bold && (_jsx(ToolbarButton, { icon: IconBold, title: "Bold", active: state.activeFormats.has('bold'), onClick: () => formatText('bold') })), fmt.italic && (_jsx(ToolbarButton, { icon: IconItalic, title: "Italic", active: state.activeFormats.has('italic'), onClick: () => formatText('italic') })), fmt.underline && (_jsx(ToolbarButton, { icon: IconUnderline, title: "Underline", active: state.activeFormats.has('underline'), onClick: () => formatText('underline') }))] })), (config.font.family || config.font.size) && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet likhari-toolbar-select-group", children: [config.font.family && (_jsxs(_Fragment, { children: [_jsx(SelectIcon, { icon: IconTypography }), _jsx("select", { className: "likhari-toolbar-select", "aria-label": "Font family", disabled: true, title: "Font family (coming soon)", children: _jsx("option", { children: "Font" }) })] })), config.font.size && (_jsxs(_Fragment, { children: [_jsx(SelectIcon, { icon: IconTextSize }), _jsx("select", { className: "likhari-toolbar-select", "aria-label": "Font size", disabled: true, title: "Font size (coming soon)", children: _jsx("option", { children: "Size" }) })] }))] })), showAlignGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-select-group", children: [_jsx(SelectIcon, { icon: AlignIcon }), _jsxs("select", { className: "likhari-toolbar-select", "aria-label": "Alignment", value: state.elementFormat || 'start', onChange: (e) => formatElement(e.target.value), children: [config.alignment.start && _jsx("option", { value: "start", children: "Align start" }), config.alignment.center && _jsx("option", { value: "center", children: "Align center" }), config.alignment.start && _jsx("option", { value: "end", children: "Align end" }), config.alignment.justify && _jsx("option", { value: "justify", children: "Justify" }), config.alignment.left && _jsx("option", { value: "left", children: "Align left" }), config.alignment.right && _jsx("option", { value: "right", children: "Align right" })] })] })), showInsertPoetryGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.links && _jsx(StubButton, { icon: IconLink, title: "Insert link" }), (config.images.linked || config.images.embedded) && _jsx(StubButton, { icon: IconPhoto, title: "Insert image" }), config.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: "Poetry blocks" })] })), showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && _jsx(StubButton, { icon: IconWand, title: "Auto-correct" }), config.language.textCleanup && _jsx(StubButton, { icon: IconSparkles, title: "Text cleanup" }), config.language.spellCheck && _jsx(StubButton, { icon: IconAbc, title: "Spell-checker" })] })), showOverflowMenu && (_jsxs("div", { className: "likhari-toolbar-overflow", ref: overflowRef, children: [_jsx(ToolbarButton, { icon: IconDots, title: "More formatting", active: overflowOpen, onClick: () => setOverflowOpen((o) => !o) }), overflowOpen && (_jsxs("div", { className: "likhari-toolbar-overflow-panel", role: "menu", children: [fmt.strikethrough && (_jsx(ToolbarButton, { icon: IconStrikethrough, title: "Strikethrough", active: state.activeFormats.has('strikethrough'), onClick: () => formatText('strikethrough') })), fmt.superscript && (_jsx(ToolbarButton, { icon: IconSuperscript, title: "Superscript", active: state.activeFormats.has('superscript'), onClick: () => formatText('superscript') })), fmt.subscript && (_jsx(ToolbarButton, { icon: IconSubscript, title: "Subscript", active: state.activeFormats.has('subscript'), onClick: () => formatText('subscript') })), fmt.caseTransforms && (_jsxs(_Fragment, { children: [_jsx(ToolbarButton, { icon: IconLetterCaseUpper, title: "UPPERCASE", onClick: () => applyCaseTransform('upper') }), _jsx(ToolbarButton, { icon: IconLetterCaseLower, title: "lowercase", onClick: () => applyCaseTransform('lower') }), _jsx(ToolbarButton, { icon: IconLetterCase, title: "Capitalize", onClick: () => applyCaseTransform('capitalize') })] })), fmt.clearFormatting && _jsx(ToolbarButton, { icon: IconClearFormatting, title: "Clear formatting", onClick: clearFormatting }), config.indent && (_jsxs(_Fragment, { children: [_jsx(ToolbarButton, { icon: IconIndentDecrease, title: "Outdent", onClick: () => editor.dispatchCommand(OUTDENT_CONTENT_COMMAND, undefined) }), _jsx(ToolbarButton, { icon: IconIndentIncrease, title: "Indent", onClick: () => editor.dispatchCommand(INDENT_CONTENT_COMMAND, undefined) })] }))] }))] }))] }));
|
|
230
204
|
}
|
package/dist/theme/editor.css
CHANGED
|
@@ -23,14 +23,11 @@
|
|
|
23
23
|
align-items: center;
|
|
24
24
|
gap: 8px;
|
|
25
25
|
min-height: 46px;
|
|
26
|
-
padding:
|
|
26
|
+
padding: 0 8px;
|
|
27
27
|
background: var(--editor-paper-dim);
|
|
28
28
|
border-bottom: 1px solid var(--editor-line);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
escape this box. */
|
|
32
|
-
flex-wrap: wrap;
|
|
33
|
-
row-gap: 4px;
|
|
29
|
+
flex-wrap: nowrap;
|
|
30
|
+
overflow-x: auto;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
.likhari-toolbar-group {
|
|
@@ -38,11 +35,11 @@
|
|
|
38
35
|
align-items: center;
|
|
39
36
|
gap: 2px;
|
|
40
37
|
padding: 0 6px;
|
|
41
|
-
border-
|
|
38
|
+
border-right: 0.5px solid var(--editor-line);
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
.likhari-toolbar-group:last-child {
|
|
45
|
-
border-
|
|
42
|
+
border-right: none;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
.likhari-toolbar-button {
|
|
@@ -82,54 +79,86 @@
|
|
|
82
79
|
cursor: not-allowed;
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
themed from the --editor-* tokens (the closed input only; the dropdown is
|
|
87
|
-
portaled to <body>, outside the token scope, so it uses Mantine's own
|
|
88
|
-
variables, which the editor's MantineProvider already maps to the accent). */
|
|
89
|
-
.likhari-toolbar-select-input {
|
|
82
|
+
.likhari-toolbar-select {
|
|
90
83
|
height: 30px;
|
|
91
|
-
|
|
84
|
+
max-width: 116px;
|
|
92
85
|
font-size: 12px;
|
|
93
86
|
border-radius: 6px;
|
|
94
87
|
border: 1px solid var(--editor-line-strong);
|
|
95
88
|
background: var(--editor-paper);
|
|
96
89
|
color: var(--editor-ink);
|
|
90
|
+
padding: 0 6px;
|
|
97
91
|
}
|
|
98
92
|
|
|
99
|
-
.likhari-toolbar-select
|
|
93
|
+
.likhari-toolbar-select:disabled {
|
|
100
94
|
opacity: 0.4;
|
|
101
95
|
cursor: not-allowed;
|
|
102
96
|
}
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
/* Icon shown to the left of a <select> — native selects can't render an
|
|
99
|
+
icon per-option, so the control itself is labeled this way instead. */
|
|
100
|
+
.likhari-toolbar-select-group {
|
|
101
|
+
gap: 4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.likhari-toolbar-select-icon {
|
|
105
|
+
display: inline-flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
color: var(--editor-ink-soft);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Overflow menu ("⋯") — houses less-frequent formatting (strikethrough,
|
|
111
|
+
super/subscript, case transforms, clear formatting, indent/outdent) so
|
|
112
|
+
the primary row stays compact on small/mobile viewports. */
|
|
113
|
+
.likhari-toolbar-overflow {
|
|
114
|
+
position: relative;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.likhari-toolbar-overflow-panel {
|
|
120
|
+
position: absolute;
|
|
121
|
+
top: calc(100% + 4px);
|
|
122
|
+
inset-inline-end: 0;
|
|
123
|
+
z-index: 20;
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-wrap: wrap;
|
|
126
|
+
gap: 2px;
|
|
127
|
+
width: 168px;
|
|
128
|
+
padding: 6px;
|
|
129
|
+
background: var(--editor-paper);
|
|
130
|
+
border: 0.5px solid var(--editor-line);
|
|
131
|
+
border-radius: 8px;
|
|
107
132
|
}
|
|
108
133
|
|
|
109
134
|
/* Responsive collapse — editor-ui-design-spec.md §3.3. These groups hold
|
|
110
135
|
only not-yet-implemented placeholders, so hiding them below the tablet
|
|
111
136
|
breakpoint costs nothing functional while keeping the primary row usable
|
|
112
|
-
on small screens; the toolbar
|
|
113
|
-
still-visible controls alone overflow a narrow viewport. */
|
|
137
|
+
on small screens; the toolbar's horizontal scroll (below) remains the
|
|
138
|
+
fallback if the still-visible controls alone overflow a narrow viewport. */
|
|
114
139
|
@media (max-width: 900px) {
|
|
115
140
|
.likhari-toolbar-group--collapse-tablet {
|
|
116
141
|
display: none;
|
|
117
142
|
}
|
|
118
143
|
}
|
|
119
144
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
145
|
+
.likhari-save-button {
|
|
146
|
+
display: inline-flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 6px;
|
|
149
|
+
height: 30px;
|
|
150
|
+
padding: 0 14px;
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
border: 1px solid var(--editor-accent);
|
|
123
153
|
background: var(--editor-accent);
|
|
124
154
|
color: var(--editor-paper);
|
|
155
|
+
font-size: 12px;
|
|
156
|
+
font-weight: 500;
|
|
157
|
+
cursor: pointer;
|
|
125
158
|
}
|
|
126
159
|
|
|
127
|
-
.likhari-
|
|
128
|
-
background:
|
|
129
|
-
filter: brightness(1.1);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.likhari-toolbar-button[data-dirty='false'] {
|
|
160
|
+
.likhari-save-button[data-dirty='false'] {
|
|
161
|
+
background: transparent;
|
|
133
162
|
color: var(--editor-accent);
|
|
134
163
|
}
|
|
135
164
|
|