@inshapardaz/likhari-react 0.1.48-dev.354 → 0.1.48-dev.356

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"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"}
@@ -36,7 +36,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX, ZOOM_LEVELS_
36
36
  import { setStyleProperty } from '../utils/style.js';
37
37
  import { useStrings } from '../i18n/useStrings.js';
38
38
  import { usePortalTarget } from '../PortalTargetContext.js';
39
- import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconArrowsJoin, IconBold, IconClearFormatting, IconDeviceFloppy, IconChevronDown, IconChevronUp, IconDots, IconHelpCircle, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconClipboard, IconCopy, IconCut, IconSearch, IconSelectAll, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsMaximize, IconArrowsMinimize, IconArrowsShuffle, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconMinus, IconPlus, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
39
+ import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconArrowsJoin, IconBold, IconClearFormatting, IconDeviceFloppy, IconBook, IconChevronDown, IconChevronUp, IconDots, IconHelpCircle, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconClipboard, IconCopy, IconCut, IconSearch, IconSelectAll, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsMaximize, IconArrowsMinimize, IconArrowsShuffle, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconMinus, IconPlus, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
40
40
  /** Sentinel for "no inline style": Lexical returns the default it is given when
41
41
  * the selection has none, so this tells "unstyled" (show the canvas default)
42
42
  * apart from "styled with a value the dropdown doesn't list" (show nothing). */
@@ -70,6 +70,12 @@ const CONTEXT_MENU_WIDTH = 220;
70
70
  const CONTEXT_MENU_VIEWPORT_PADDING = 8;
71
71
  /** `likhari-context-menu` (editor.css) shrinks the icons to match the smaller text. */
72
72
  const CONTEXT_MENU_CLASS_NAMES = { dropdown: 'likhari-context-menu' };
73
+ /** The submenus: opened to the side, flipped or shifted to stay in the window, and positioned against the window (`fixed`) so
74
+ * the menu's own scrolling does not clip them. */
75
+ const CONTEXT_SUBMENU_PROPS = {
76
+ floatingStrategy: 'fixed',
77
+ middlewares: { flip: true, shift: { crossAxis: true, padding: CONTEXT_MENU_VIEWPORT_PADDING }, size: { padding: CONTEXT_MENU_VIEWPORT_PADDING } },
78
+ };
73
79
  const CONTEXT_MENU_STYLES = {
74
80
  dropdown: { overflowY: 'auto', padding: 4 },
75
81
  item: { fontSize: 'var(--mantine-font-size-xs)', padding: '4px 8px', minHeight: 0 },
@@ -190,8 +196,8 @@ function CommandItem({ mode, icon: Icon, label, onClick, disabled, color, }) {
190
196
  }
191
197
  return (_jsx(Menu.Item, { color: color, disabled: disabled, leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: label }));
192
198
  }
193
- /** A group heading in a menu; the ribbon's tab already names the group, so it shows nothing there. */
194
- const ItemLabel = ({ mode, children }) => (mode === 'ribbon' ? null : _jsx(Menu.Label, { children: children }));
199
+ /** A group heading in a menu; the ribbon's tab and a submenu's parent item already name the group, so they show nothing. */
200
+ const ItemLabel = ({ mode, children }) => (mode === 'menu' ? _jsx(Menu.Label, { children: children }) : null);
195
201
  const ItemDivider = ({ mode }) => (mode === 'ribbon' ? _jsx(MantineToolbar.Divider, {}) : _jsx(Menu.Divider, {}));
196
202
  function LinkMenuItems({ url, onEdit, onRemove, strings, mode = 'menu' }) {
197
203
  // Only offer the link as clickable if it's a URL the editor would have accepted.
@@ -666,6 +672,14 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
666
672
  }
667
673
  }
668
674
  }
675
+ // A misspelled word's menu is its spelling fixes and the clipboard, nothing else: no synonyms to look up either.
676
+ if (menu.spelling) {
677
+ menu.thesaurus = undefined;
678
+ menu.link = undefined;
679
+ menu.table = undefined;
680
+ menu.poetry = undefined;
681
+ menu.text = undefined;
682
+ }
669
683
  setContextMenu(menu);
670
684
  if (word && menu.thesaurus) {
671
685
  // Synonyms can take a while (the first WordNet lookup downloads the database), so the menu
@@ -1003,7 +1017,13 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1003
1017
  }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: CONTEXT_MENU_WIDTH,
1004
1018
  // Stays inside the visible area: flips above the click or shifts sideways when it fits that
1005
1019
  // way, and otherwise is cut to the room there is and scrolls (a long menu in a short window).
1006
- middlewares: { flip: true, shift: true, size: { padding: CONTEXT_MENU_VIEWPORT_PADDING } }, styles: CONTEXT_MENU_STYLES, classNames: CONTEXT_MENU_CLASS_NAMES, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: contextMenu?.x ?? -9999, top: contextMenu?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsxs(Menu.Dropdown, { children: [contextMenu?.spelling && (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: strings.contextMenu.spelling }), contextMenu.spelling.suggestions.length === 0 && _jsx(Menu.Item, { disabled: true, children: strings.contextMenu.noSuggestions }), contextMenu.spelling.suggestions.map((suggestion) => (_jsx(Menu.Item, { onClick: () => {
1020
+ middlewares: { flip: true, shift: true, size: { padding: CONTEXT_MENU_VIEWPORT_PADDING } },
1021
+ // Positioned against the window, not the editor: the menu then takes no room in the editor's layout (so it can
1022
+ // never grow a scroll bar there or make the text jump) and is not cut off at the editor's edge. No open
1023
+ // animation either, as its transform would make the submenus position against the menu instead.
1024
+ floatingStrategy: "fixed", transitionProps: { duration: 0 }, styles: CONTEXT_MENU_STYLES, classNames: CONTEXT_MENU_CLASS_NAMES, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: contextMenu?.x ?? -9999, top: contextMenu?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsxs(Menu.Dropdown, { children: [contextMenu?.spelling ? (
1025
+ // A misspelled word gets its fixes and the clipboard row, and nothing else.
1026
+ _jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: strings.contextMenu.spelling }), contextMenu.spelling.suggestions.length === 0 && _jsx(Menu.Item, { disabled: true, children: strings.contextMenu.noSuggestions }), contextMenu.spelling.suggestions.map((suggestion) => (_jsx(Menu.Item, { onClick: () => {
1007
1027
  const match = contextMenu.spelling.match;
1008
1028
  editor.update(() => $replaceMatch(match, suggestion), { discrete: true });
1009
1029
  editor.focus();
@@ -1021,16 +1041,18 @@ export function Toolbar({ config, toolbarStyle, dictionaryStores = [], thesaurus
1021
1041
  }, children: strings.contextMenu.addToDictionary }), onAddAutoCorrect && config.language.autocorrect && (_jsx(Menu.Item, { onClick: () => {
1022
1042
  onAddAutoCorrect(contextMenu.spelling.word, contextMenu.spelling.language);
1023
1043
  setContextMenu(null);
1024
- }, children: strings.contextMenu.addToAutoCorrect })), _jsx(Menu.Divider, {})] })), contextMenu?.thesaurus && (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: strings.thesaurus.synonyms }), contextMenu.thesaurus.items === undefined && _jsx(Menu.Item, { disabled: true, children: strings.thesaurus.loading }), contextMenu.thesaurus.items?.length === 0 && _jsx(Menu.Item, { disabled: true, children: strings.thesaurus.none }), contextMenu.thesaurus.items?.map((synonym) => (_jsx(Menu.Item, { onClick: () => {
1025
- const match = contextMenu.thesaurus.match;
1026
- editor.update(() => $replaceMatch(match, synonym), { discrete: true });
1027
- editor.focus();
1028
- }, children: synonym }, synonym))), _jsx(Menu.Divider, {})] })), contextMenu?.link !== undefined && (_jsxs(_Fragment, { children: [_jsx(LinkMenuItems, { url: contextMenu.link, onEdit: () => {
1029
- restoreSelection();
1030
- openLinkDialog();
1031
- }, onRemove: removeLink, strings: strings }), _jsx(Menu.Divider, {})] })), contextMenu?.table && (_jsxs(_Fragment, { children: [_jsx(TableMenuItems, { strings: strings, direction: direction, rows: contextMenu.table.rows, columns: contextMenu.table.columns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }), _jsx(Menu.Divider, {})] })), contextMenu?.poetry && (_jsxs(_Fragment, { children: [_jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }), _jsx(Menu.Divider, {})] })), contextMenu?.text && (_jsxs(_Fragment, { children: [_jsx(Menu.Item, { leftSection: _jsx(IconArrowsJoin, { size: ICON_SIZE, stroke: ICON_STROKE }), disabled: !contextMenu.canJoin, onClick: runMenuAction(() => {
1044
+ }, children: strings.contextMenu.addToAutoCorrect })), _jsx(Menu.Divider, {})] })) : (
1045
+ // Everything that applies to what is under the pointer, each kind of content behind its own submenu.
1046
+ _jsxs(_Fragment, { children: [contextMenu?.thesaurus && (_jsxs(Menu.Sub, { ...CONTEXT_SUBMENU_PROPS, children: [_jsx(Menu.Sub.Target, { children: _jsx(Menu.Sub.Item, { leftSection: _jsx(IconBook, { size: ICON_SIZE, stroke: ICON_STROKE }), children: strings.thesaurus.synonyms }) }), _jsxs(Menu.Sub.Dropdown, { children: [contextMenu.thesaurus.items === undefined && _jsx(Menu.Item, { disabled: true, children: strings.thesaurus.loading }), contextMenu.thesaurus.items?.length === 0 && _jsx(Menu.Item, { disabled: true, children: strings.thesaurus.none }), contextMenu.thesaurus.items?.map((synonym) => (_jsx(Menu.Item, { onClick: () => {
1047
+ const match = contextMenu.thesaurus.match;
1048
+ editor.update(() => $replaceMatch(match, synonym), { discrete: true });
1049
+ editor.focus();
1050
+ }, children: synonym }, synonym)))] })] })), contextMenu?.link !== undefined && (_jsxs(Menu.Sub, { ...CONTEXT_SUBMENU_PROPS, children: [_jsx(Menu.Sub.Target, { children: _jsx(Menu.Sub.Item, { leftSection: _jsx(IconLink, { size: ICON_SIZE, stroke: ICON_STROKE }), children: strings.link.menuLabel }) }), _jsx(Menu.Sub.Dropdown, { children: _jsx(LinkMenuItems, { mode: "submenu", url: contextMenu.link, onEdit: () => {
1051
+ restoreSelection();
1052
+ openLinkDialog();
1053
+ }, onRemove: removeLink, strings: strings }) })] })), contextMenu?.table && (_jsxs(Menu.Sub, { ...CONTEXT_SUBMENU_PROPS, children: [_jsx(Menu.Sub.Target, { children: _jsx(Menu.Sub.Item, { leftSection: _jsx(IconTable, { size: ICON_SIZE, stroke: ICON_STROKE }), children: strings.tableMenu.menuLabel }) }), _jsx(Menu.Sub.Dropdown, { children: _jsx(TableMenuItems, { mode: "submenu", strings: strings, direction: direction, rows: contextMenu.table.rows, columns: contextMenu.table.columns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), contextMenu?.poetry && (_jsxs(Menu.Sub, { ...CONTEXT_SUBMENU_PROPS, children: [_jsx(Menu.Sub.Target, { children: _jsx(Menu.Sub.Item, { leftSection: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }), children: strings.poetryMenu.menuLabel }) }), _jsx(Menu.Sub.Dropdown, { children: _jsx(PoetryMenuItems, { mode: "submenu", strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] })), contextMenu?.text && (_jsx(Menu.Item, { leftSection: _jsx(IconArrowsJoin, { size: ICON_SIZE, stroke: ICON_STROKE }), disabled: !contextMenu.canJoin, onClick: runMenuAction(() => {
1032
1054
  $joinSelectedLines();
1033
- }), children: strings.contextMenu.joinLines }), _jsx(Menu.Divider, {})] })), _jsx(Menu.Item, { leftSection: _jsx(IconCut, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void copyOrCut(true)(), children: strings.contextMenu.cut }), _jsx(Menu.Item, { leftSection: _jsx(IconCopy, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void copyOrCut(false)(), children: strings.contextMenu.copy }), _jsx(Menu.Item, { leftSection: _jsx(IconClipboard, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: () => void paste(), children: strings.contextMenu.paste }), _jsx(Menu.Item, { leftSection: _jsx(IconSelectAll, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: runMenuAction(() => $selectAll()), children: strings.contextMenu.selectAll })] })] }));
1055
+ }), children: strings.contextMenu.joinLines })), (contextMenu?.thesaurus || contextMenu?.link !== undefined || contextMenu?.table || contextMenu?.poetry || contextMenu?.text) && (_jsx(Menu.Divider, {}))] })), _jsxs("div", { className: "likhari-context-row", role: "group", children: [_jsx(Menu.Item, { "aria-label": strings.contextMenu.cut, title: strings.contextMenu.cut, onClick: () => void copyOrCut(true)(), children: _jsx(IconCut, { size: ICON_SIZE, stroke: ICON_STROKE }) }), _jsx(Menu.Item, { "aria-label": strings.contextMenu.copy, title: strings.contextMenu.copy, onClick: () => void copyOrCut(false)(), children: _jsx(IconCopy, { size: ICON_SIZE, stroke: ICON_STROKE }) }), _jsx(Menu.Item, { "aria-label": strings.contextMenu.paste, title: strings.contextMenu.paste, onClick: () => void paste(), children: _jsx(IconClipboard, { size: ICON_SIZE, stroke: ICON_STROKE }) })] }), !contextMenu?.spelling && (_jsxs(_Fragment, { children: [_jsx(Menu.Divider, {}), _jsx(Menu.Item, { leftSection: _jsx(IconSelectAll, { size: ICON_SIZE, stroke: ICON_STROKE }), onClick: runMenuAction(() => $selectAll()), children: strings.contextMenu.selectAll })] }))] })] }));
1034
1056
  const dialogsEl = (_jsxs(_Fragment, { children: [(config.images.linked || config.images.embedded) && (_jsx(ImageDialog, { mode: "insert", opened: imageDialogOpen, onSubmit: insertImage, onClose: closeImageDialog })), config.tables && _jsx(TableDialog, { opened: tableDialogOpen, onSubmit: insertTable, onClose: closeTableDialog }), config.columns && _jsx(LayoutDialog, { opened: layoutDialogOpen, onSubmit: insertLayout, onClose: closeLayoutDialog }), config.links && (_jsx(LinkDialog, { opened: linkDialogOpen, initialUrl: state.linkUrl, showTextField: linkNeedsText, onSubmit: applyLink, onRemove: state.isLink ? removeLink : undefined, onClose: closeLinkDialog })), drafts && _jsx(DraftsDialog, { opened: draftsOpen, locale: locale, onClose: () => setDraftsOpen(false), ...drafts })] }));
1035
1057
  // The ribbon layout (UI spec §3.4): the same command groups as above, on tabs.
1036
1058
  if (ribbon) {
@@ -1338,3 +1338,20 @@
1338
1338
  width: 14px;
1339
1339
  height: 14px;
1340
1340
  }
1341
+
1342
+ /* Cut, copy and paste as one row of icon buttons in the right-click menu. */
1343
+ .likhari-context-row {
1344
+ display: flex;
1345
+ gap: 2px;
1346
+ }
1347
+
1348
+ .likhari-context-row [data-menu-item] {
1349
+ flex: 1 1 0;
1350
+ justify-content: center;
1351
+ padding-block: 5px;
1352
+ }
1353
+
1354
+ .likhari-context-row [data-menu-item] > * {
1355
+ display: flex;
1356
+ justify-content: center;
1357
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.48-dev.354",
3
+ "version": "0.1.48-dev.356",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {