@inshapardaz/likhari-react 0.1.28-dev.254 → 0.1.28-dev.257

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":"AA0CA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAgDtF,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AA+hBvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,gCAAgC;IAChC,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IACnE,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,iGAAiG;IACjG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAqB,EAAE,eAAoB,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAgB,EAAE,YAAY,EAAE,SAAiB,EAAE,aAAa,EAAE,eAAuB,EAAE,mBAAmB,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BAspCvU"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA0CA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAgDtF,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AAggBvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,gCAAgC;IAChC,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IACnE,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,iGAAiG;IACjG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAqB,EAAE,eAAoB,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAgB,EAAE,YAAY,EAAE,SAAiB,EAAE,aAAa,EAAE,eAAuB,EAAE,mBAAmB,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BA+/BvU"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { Menu, Select } from '@mantine/core';
2
+ import { Fragment, forwardRef, useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Menu, Select, Toolbar as MantineToolbar } from '@mantine/core';
4
4
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
5
5
  import { $getSelection, $isRangeSelection, $isElementNode, $setSelection, $getRoot, $selectAll, $getNearestNodeFromDOMNode, $createTextNode, KEY_MODIFIER_COMMAND, COMMAND_PRIORITY_NORMAL, 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
6
  import { $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType } from '@lexical/selection';
@@ -108,15 +108,18 @@ function iconOptionRenderer(icons, fallback) {
108
108
  return (_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(Icon, { size: 16, stroke: ICON_STROKE }), option.label] }));
109
109
  };
110
110
  }
111
- function ToolbarButton({ icon: Icon, title, active, dirty, disabled, onClick, }) {
112
- return (_jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": active ? 'true' : 'false', "data-dirty": dirty === undefined ? undefined : dirty ? '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 }) }));
113
- }
114
- /** A disabled placeholder for a feature whose config flag is on but that
115
- * isn't implemented yet (link/image/poetry/font/language-tooling — see
116
- * docs/lexical-editor-spec.md §13's phasing). Renders so the toolbar's
117
- * layout is final now and only needs its onClick wired up later. */
118
- function StubButton({ icon, title, comingSoon }) {
119
- return _jsx(ToolbarButton, { icon: icon, title: comingSoon(title), disabled: true, onClick: undefined });
111
+ const ToolbarButton = forwardRef(function ToolbarButton({ icon: Icon, title, active, dirty, onMouseDown, ...rest }, ref) {
112
+ return (_jsx(MantineToolbar.Toggle, { ref: ref, "aria-label": title, title: title, active: active, "data-dirty": dirty === undefined ? undefined : dirty ? 'true' : 'false',
113
+ // Keep the editor's selection: a mousedown here would blur the canvas.
114
+ onMouseDown: (e) => {
115
+ e.preventDefault();
116
+ onMouseDown?.(e);
117
+ }, ...rest, children: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }) }));
118
+ });
119
+ function withDividers(sections) {
120
+ return sections
121
+ .filter((section) => Boolean(section))
122
+ .flatMap((section, index) => (index === 0 ? [section] : [_jsx(MantineToolbar.Divider, {}, `divider-${index}`), section]));
120
123
  }
121
124
  /** Mantine Select (combobox) for the toolbar's dropdown controls. The icon
122
125
  * labels the control itself, since a dropdown can't show one per option in
@@ -126,24 +129,9 @@ function ToolbarSelect({ icon: Icon, label, value, data, width, disabled, placeh
126
129
  const portalTarget = usePortalTarget();
127
130
  return (_jsx(Select, { size: "xs", w: width, "aria-label": label, title: disabled && comingSoon ? comingSoon(label) : label, data: data, value: value, placeholder: placeholder, disabled: disabled, searchable: searchable, renderOption: renderOption, nothingFoundMessage: searchable ? noMatchMessage : undefined, allowDeselect: false, leftSection: _jsx(Icon, { size: 15, stroke: ICON_STROKE }), comboboxProps: { withinPortal: true, position: 'bottom-start', middlewares: { flip: true, shift: true }, portalProps: { target: portalTarget } }, classNames: { input: 'likhari-toolbar-select-input', dropdown: 'likhari-toolbar-select-dropdown' }, onChange: (v) => v && onChange?.(v) }));
128
131
  }
129
- const EMPTY_OVERFLOW = new Set();
130
- function setsEqual(a, b) {
131
- if (a.size !== b.size)
132
- return false;
133
- for (const v of a)
134
- if (!b.has(v))
135
- return false;
136
- return true;
137
- }
138
- /** Renders a movable group's items as normal inline toolbar buttons (used
139
- * both in the real toolbar and in the hidden width-measuring clone). */
140
- function MovableGroupButtons({ group }) {
141
- return (_jsx("div", { className: "likhari-toolbar-group", "data-group-key": group.key, children: group.items.map((item) => (_jsx(ToolbarButton, { icon: item.icon, title: item.label, active: item.active, onClick: item.onClick }, item.key))) }));
142
- }
143
- function OverflowItem({ icon: Icon, label, active, onClick, }) {
144
- return (_jsx(Menu.Item, { leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), "data-active": active ? 'true' : 'false', className: "likhari-overflow-item",
145
- // Keep the editor's selection: a mousedown here would blur the canvas.
146
- onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: label }));
132
+ /** An item in the "..." menu. A mousedown here would blur the canvas, so it is kept. */
133
+ function MoreMenuItem({ icon: Icon, label, active, onClick, }) {
134
+ return (_jsx(Menu.Item, { leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), "data-active": active ? 'true' : 'false', onMouseDown: (e) => e.preventDefault(), onClick: onClick, children: label }));
147
135
  }
148
136
  function LinkMenuItems({ url, onEdit, onRemove, strings }) {
149
137
  // Only offer the link as clickable if it's a URL the editor would have accepted.
@@ -194,15 +182,6 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
194
182
  const [draftsOpen, setDraftsOpen] = useState(false);
195
183
  const rtl = direction === 'rtl';
196
184
  const ALIGN_ICONS = rtl ? ALIGN_ICONS_RTL : ALIGN_ICONS_LTR;
197
- // Responsive "priority+" overflow: the toolbar's outer container and a
198
- // hidden nowrap clone (rendered with every movable group forced inline)
199
- // are both measured; whenever the clone's natural width exceeds the
200
- // container's available width, the lowest-priority movable groups are
201
- // moved into the "..." menu, lowest-priority first, until what remains
202
- // inline fits (or there's nothing left to move).
203
- const toolbarContainerRef = useRef(null);
204
- const toolbarMeasureRef = useRef(null);
205
- const [overflowGroupKeys, setOverflowGroupKeys] = useState(EMPTY_OVERFLOW);
206
185
  const updateToolbar = useCallback(() => {
207
186
  editor.getEditorState().read(() => {
208
187
  const selection = $getSelection();
@@ -342,7 +321,7 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
342
321
  };
343
322
  const snapshotSelectionRef = useRef(snapshotSelection);
344
323
  snapshotSelectionRef.current = snapshotSelection;
345
- const runOverflowAction = (action) => () => {
324
+ const runMenuItem = (action) => () => {
346
325
  const saved = menuSelectionRef.current;
347
326
  if (saved)
348
327
  editor.update(() => $setSelection(saved.clone()), { discrete: true });
@@ -646,35 +625,22 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
646
625
  const headingLevels = config.blocks.headingLevels ?? [];
647
626
  const fmt = config.formatting;
648
627
  const showFormattingGroup = headingLevels.length > 0 || config.lists.bullet || config.lists.numbered || config.lists.check || config.blocks.quote;
649
- const showInlineGroup = fmt.bold || fmt.italic || fmt.underline;
650
628
  const showAlignGroup = config.alignment.start || config.alignment.center || config.alignment.justify || config.alignment.left || config.alignment.right;
651
- const showStubInsertGroup = config.images.linked ||
629
+ const showInsertGroup = config.links ||
630
+ config.images.linked ||
652
631
  config.images.embedded ||
653
632
  config.tables ||
654
633
  config.blocks.horizontalRule ||
655
634
  config.blocks.pageBreak ||
656
635
  config.columns ||
657
636
  config.footnotes ||
658
- config.poetry.enabled;
637
+ (config.poetry.enabled && locale !== 'en') ||
638
+ (config.tables && state.inTable) ||
639
+ (config.poetry.enabled && state.inPoetry);
659
640
  const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
660
- // The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
661
- // as normal inline toolbar groups whenever there's room, and only move
662
- // into the "..." overflow menu when the measured toolbar content doesn't
663
- // fit the available width (see the ResizeObserver effect below) — this
664
- // replaces the old always-on overflow menu, which was driven purely by
665
- // these config flags regardless of actual space.
666
- const scriptCleanupItems = [];
667
- if (fmt.strikethrough) {
668
- scriptCleanupItems.push({
669
- key: 'strikethrough',
670
- icon: IconStrikethrough,
671
- label: strings.toolbar.strikethrough,
672
- active: state.activeFormats.has('strikethrough'),
673
- onClick: () => formatText('strikethrough'),
674
- });
675
- }
641
+ const moreItems = [];
676
642
  if (fmt.superscript) {
677
- scriptCleanupItems.push({
643
+ moreItems.push({
678
644
  key: 'superscript',
679
645
  icon: IconSuperscript,
680
646
  label: strings.toolbar.superscript,
@@ -683,7 +649,7 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
683
649
  });
684
650
  }
685
651
  if (fmt.subscript) {
686
- scriptCleanupItems.push({
652
+ moreItems.push({
687
653
  key: 'subscript',
688
654
  icon: IconSubscript,
689
655
  label: strings.toolbar.subscript,
@@ -692,10 +658,10 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
692
658
  });
693
659
  }
694
660
  if (fmt.caseTransforms) {
695
- scriptCleanupItems.push({ key: 'upper', icon: IconLetterCaseUpper, label: strings.toolbar.uppercase, onClick: () => applyCaseTransform('upper') }, { key: 'lower', icon: IconLetterCaseLower, label: strings.toolbar.lowercase, onClick: () => applyCaseTransform('lower') }, { key: 'capitalize', icon: IconLetterCase, label: strings.toolbar.capitalize, onClick: () => applyCaseTransform('capitalize') });
661
+ moreItems.push({ key: 'upper', icon: IconLetterCaseUpper, label: strings.toolbar.uppercase, onClick: () => applyCaseTransform('upper') }, { key: 'lower', icon: IconLetterCaseLower, label: strings.toolbar.lowercase, onClick: () => applyCaseTransform('lower') }, { key: 'capitalize', icon: IconLetterCase, label: strings.toolbar.capitalize, onClick: () => applyCaseTransform('capitalize') });
696
662
  }
697
663
  if (fmt.clearFormatting) {
698
- scriptCleanupItems.push({ key: 'clear', icon: IconClearFormatting, label: strings.toolbar.clearFormatting, onClick: clearFormatting });
664
+ moreItems.push({ key: 'clear', icon: IconClearFormatting, label: strings.toolbar.clearFormatting, onClick: clearFormatting });
699
665
  }
700
666
  // UI spec §5: indent/outdent arrows are direction-relative and swap in RTL
701
667
  // (outdent moves content toward the start edge, which is the right in RTL).
@@ -715,56 +681,6 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
715
681
  },
716
682
  ]
717
683
  : [];
718
- // Low to high priority: the first group here is the first one pushed into
719
- // overflow once content stops fitting (UI spec §3.3).
720
- const movableGroups = [
721
- { key: 'scriptCleanup', items: scriptCleanupItems },
722
- { key: 'indentOutdent', items: indentItems },
723
- ].filter((g) => g.items.length > 0);
724
- const movableGroupKeys = useMemo(() => movableGroups.map((g) => g.key).join(','), [movableGroups]);
725
- // Re-measure whenever the container is resized, or whenever the set of
726
- // movable groups (and so the hidden clone's content) changes.
727
- useLayoutEffect(() => {
728
- const container = toolbarContainerRef.current;
729
- const measure = toolbarMeasureRef.current;
730
- if (!container || !measure)
731
- return;
732
- // Rough width of the "..." button itself (icon button + its gap) —
733
- // only charged against the budget once something has actually moved
734
- // into it, since it isn't rendered at all otherwise.
735
- const OVERFLOW_BUTTON_WIDTH = 38;
736
- const recompute = () => {
737
- const available = container.clientWidth;
738
- const total = measure.scrollWidth;
739
- if (total <= available) {
740
- setOverflowGroupKeys((prev) => (prev.size === 0 ? prev : EMPTY_OVERFLOW));
741
- return;
742
- }
743
- const widths = new Map();
744
- measure.querySelectorAll('[data-group-key]').forEach((el) => {
745
- widths.set(el.dataset.groupKey, el.getBoundingClientRect().width);
746
- });
747
- let remaining = total;
748
- const next = new Set();
749
- for (const key of movableGroupKeys.split(',').filter(Boolean)) {
750
- if (remaining <= available)
751
- break;
752
- const w = widths.get(key);
753
- if (w === undefined)
754
- continue;
755
- remaining -= w;
756
- next.add(key);
757
- }
758
- if (next.size > 0)
759
- remaining += OVERFLOW_BUTTON_WIDTH;
760
- setOverflowGroupKeys((prev) => (setsEqual(prev, next) ? prev : next));
761
- };
762
- recompute();
763
- const ro = new ResizeObserver(recompute);
764
- ro.observe(container);
765
- ro.observe(measure);
766
- return () => ro.disconnect();
767
- });
768
684
  // Grouped for the dropdown when options declare groups (Latin / Urdu).
769
685
  const fontData = (() => {
770
686
  const data = [];
@@ -807,36 +723,26 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
807
723
  ];
808
724
  const formattingValue = state.listType ?? state.blockType;
809
725
  const AlignIcon = ALIGN_ICONS[state.elementFormat] ?? IconAlignLeft;
810
- // The toolbar's "always inline" groups (UI spec §3.3's Desktop tier) —
811
- // built as an array so the exact same elements can be rendered both in
812
- // the visible toolbar and in the hidden width-measuring clone below,
813
- // without duplicating the JSX. The link right-click context menu is kept
814
- // out of this list: it's a zero-size, fixed-position anchor, not part of
815
- // the toolbar's own layout width.
816
- const fixedGroups = [
817
- showSave && (_jsx("div", { className: "likhari-toolbar-group", children: _jsx(ToolbarButton, { icon: IconDeviceFloppy, title: strings.toolbar.save, dirty: Boolean(isDirty), onClick: onSave }) }, "save")),
818
- drafts && (_jsx("div", { className: "likhari-toolbar-group", children: _jsx(ToolbarButton, { icon: IconHistory, title: strings.toolbar.drafts, onClick: () => setDraftsOpen(true) }) }, "drafts")),
819
- config.history && (_jsxs("div", { className: "likhari-toolbar-group", children: [_jsx(ToolbarButton, { icon: IconArrowBackUp, title: strings.toolbar.undo, disabled: !state.canUndo, onClick: () => editor.dispatchCommand(UNDO_COMMAND, undefined) }), _jsx(ToolbarButton, { icon: IconArrowForwardUp, title: strings.toolbar.redo, disabled: !state.canRedo, onClick: () => editor.dispatchCommand(REDO_COMMAND, undefined) })] }, "history")),
820
- showFormattingGroup && (_jsx("div", { className: "likhari-toolbar-group", children: _jsx(ToolbarSelect, { icon: IconPilcrow, label: strings.toolbar.formattingLabel, width: 148, value: formattingValue, data: formattingOptions, comingSoon: strings.toolbar.comingSoon, renderOption: iconOptionRenderer(FORMATTING_ICONS, IconPilcrow), onChange: withRefocus((v) => applyFormatting(v)) }) }, "formatting")),
821
- showInlineGroup && (_jsxs("div", { className: "likhari-toolbar-group", children: [fmt.bold && (_jsx(ToolbarButton, { icon: IconBold, title: strings.toolbar.bold, active: state.activeFormats.has('bold'), onClick: () => formatText('bold') })), fmt.italic && (_jsx(ToolbarButton, { icon: IconItalic, title: strings.toolbar.italic, active: state.activeFormats.has('italic'), onClick: () => formatText('italic') })), fmt.underline && (_jsx(ToolbarButton, { icon: IconUnderline, title: strings.toolbar.underline, active: state.activeFormats.has('underline'), onClick: () => formatText('underline') }))] }, "inline")),
822
- (config.font.family || config.font.size) && (_jsxs("div", { className: "likhari-toolbar-group", children: [config.font.family && (_jsx(ToolbarSelect, { icon: IconTypography, label: strings.toolbar.fontFamily, width: 150, value: fontOptions.some((f) => f.family === shownFontFamily) ? shownFontFamily : null, placeholder: strings.toolbar.fontFamilyPlaceholder, data: fontData, searchable: true, noMatchMessage: strings.toolbar.noMatch, renderOption: ({ option }) => _jsx("span", { style: { fontFamily: option.value }, children: option.label }), onChange: withRefocus((v) => applyFont('font-family', v)) })), config.font.size && (_jsx(ToolbarSelect, { icon: IconTextSize, label: strings.toolbar.fontSize, width: 84, value: FONT_SIZES_PX.some((px) => `${px}px` === shownFontSize) ? shownFontSize : null, placeholder: strings.toolbar.fontSizePlaceholder, data: FONT_SIZES_PX.map((px) => ({ value: `${px}px`, label: String(px) })), onChange: withRefocus((v) => applyFont('font-size', v)) }))] }, "font")),
823
- showAlignGroup && (_jsx("div", { className: "likhari-toolbar-group", children: _jsx(ToolbarSelect, { icon: AlignIcon, label: strings.toolbar.alignment, width: 138, value: state.elementFormat || 'start', data: alignOptions, renderOption: iconOptionRenderer(ALIGN_ICONS, IconAlignLeft), onChange: withRefocus((v) => formatElement(v)) }) }, "align")),
824
- config.links && (_jsx("div", { className: "likhari-toolbar-group", children: state.isLink ? (
825
- // Caret is in a link: the button opens a menu to see, edit or remove it.
826
- _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.linkOptions, title: strings.toolbar.linkOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconLink, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(LinkMenuItems, { url: state.linkUrl, onEdit: () => {
827
- restoreSelection();
828
- openLinkDialog();
829
- }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
830
- showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.findReplace && (_jsx(ToolbarButton, { icon: IconSearch, title: strings.findReplace.toggle, active: findOpen, onClick: onToggleFind })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout }) }))] }, "stubInsert")),
831
- config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] }) }, "tableActions")),
832
- config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] }) }, "poetryActions")),
833
- showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && (_jsx(ToolbarButton, { icon: IconWand, title: strings.toolbar.autocorrect, active: autoCorrectOpen, onClick: onToggleAutoCorrect })), config.language.textCleanup && (_jsx(ToolbarButton, { icon: IconSparkles, title: strings.autoCorrect.correctDocument, onClick: () => editor.dispatchCommand(CORRECT_DOCUMENT_COMMAND, undefined) })), config.language.spellCheck && (_jsx(ToolbarButton, { icon: IconAbc, title: strings.toolbar.spellChecker, active: spellOpen, onClick: onToggleSpell }))] }, "language")),
726
+ const formatSection = (showFormattingGroup || fmt.bold || fmt.italic || fmt.underline || fmt.strikethrough || moreItems.length > 0) && (_jsxs(Fragment, { children: [showFormattingGroup && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: IconPilcrow, label: strings.toolbar.formattingLabel, width: 148, value: formattingValue, data: formattingOptions, comingSoon: strings.toolbar.comingSoon, renderOption: iconOptionRenderer(FORMATTING_ICONS, IconPilcrow), onChange: withRefocus((v) => applyFormatting(v)) }) })), (fmt.bold || fmt.italic || fmt.underline || fmt.strikethrough || moreItems.length > 0) && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [fmt.bold && _jsx(ToolbarButton, { icon: IconBold, title: strings.toolbar.bold, active: state.activeFormats.has('bold'), onClick: () => formatText('bold') }), fmt.italic && (_jsx(ToolbarButton, { icon: IconItalic, title: strings.toolbar.italic, active: state.activeFormats.has('italic'), onClick: () => formatText('italic') })), fmt.underline && (_jsx(ToolbarButton, { icon: IconUnderline, title: strings.toolbar.underline, active: state.activeFormats.has('underline'), onClick: () => formatText('underline') })), fmt.strikethrough && (_jsx(ToolbarButton, { icon: IconStrikethrough, title: strings.toolbar.strikethrough, active: state.activeFormats.has('strikethrough'), onClick: () => formatText('strikethrough') })), moreItems.length > 0 && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 210, closeOnItemClick: true, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconDots, title: strings.toolbar.moreFormatting }) }), _jsx(Menu.Dropdown, { children: moreItems.map((item) => (_jsx(MoreMenuItem, { icon: item.icon, label: item.label, active: item.active, onClick: runMenuItem(item.onClick) }, item.key))) })] }))] }))] }, "format"));
727
+ const alignSection = (showAlignGroup || indentItems.length > 0) && (_jsxs(Fragment, { children: [showAlignGroup && (_jsx(MantineToolbar.Group, { children: _jsx(ToolbarSelect, { icon: AlignIcon, label: strings.toolbar.alignment, width: 138, value: state.elementFormat || 'start', data: alignOptions, renderOption: iconOptionRenderer(ALIGN_ICONS, IconAlignLeft), onChange: withRefocus((v) => formatElement(v)) }) })), indentItems.length > 0 && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: indentItems.map((item) => (_jsx(ToolbarButton, { icon: item.icon, title: item.label, onClick: item.onClick }, item.key))) }))] }, "align"));
728
+ const fontSection = (config.font.family || config.font.size) && (_jsxs(MantineToolbar.Group, { children: [config.font.family && (_jsx(ToolbarSelect, { icon: IconTypography, label: strings.toolbar.fontFamily, width: 150, value: fontOptions.some((f) => f.family === shownFontFamily) ? shownFontFamily : null, placeholder: strings.toolbar.fontFamilyPlaceholder, data: fontData, searchable: true, noMatchMessage: strings.toolbar.noMatch, renderOption: ({ option }) => _jsx("span", { style: { fontFamily: option.value }, children: option.label }), onChange: withRefocus((v) => applyFont('font-family', v)) })), config.font.size && (_jsx(ToolbarSelect, { icon: IconTextSize, label: strings.toolbar.fontSize, width: 84, value: FONT_SIZES_PX.some((px) => `${px}px` === shownFontSize) ? shownFontSize : null, placeholder: strings.toolbar.fontSizePlaceholder, data: FONT_SIZES_PX.map((px) => ({ value: `${px}px`, label: String(px) })), onChange: withRefocus((v) => applyFont('font-size', v)) }))] }, "font"));
729
+ const insertSection = showInsertGroup && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [config.links &&
730
+ (state.isLink ? (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.linkOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(LinkMenuItems, { url: state.linkUrl, onEdit: () => {
731
+ restoreSelection();
732
+ openLinkDialog();
733
+ }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog }))), (config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.tables && state.inTable && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconTableOptions, title: strings.toolbar.tableOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout }) })), config.poetry.enabled && state.inPoetry && (_jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.poetryOptions, active: true, "aria-haspopup": "menu" }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, layout: state.poetryLayout, scale: state.poetryScale, centered: state.poetryCentered, onAction: runMenuAction }) })] }))] }, "insert"));
734
+ const toolsSection = showLanguageGroup && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [config.language.autocorrect && (_jsx(ToolbarButton, { icon: IconWand, title: strings.toolbar.autocorrect, active: autoCorrectOpen, onClick: onToggleAutoCorrect })), config.language.textCleanup && (_jsx(ToolbarButton, { icon: IconSparkles, title: strings.autoCorrect.correctDocument, onClick: () => editor.dispatchCommand(CORRECT_DOCUMENT_COMMAND, undefined) })), config.language.spellCheck && (_jsx(ToolbarButton, { icon: IconAbc, title: strings.toolbar.spellChecker, active: spellOpen, onClick: onToggleSpell }))] }, "tools"));
735
+ const endSection = (drafts || config.findReplace) && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", style: { marginInlineStart: 'auto' }, children: [drafts && _jsx(ToolbarButton, { icon: IconHistory, title: strings.toolbar.drafts, onClick: () => setDraftsOpen(true) }), config.findReplace && (_jsx(ToolbarButton, { icon: IconSearch, title: strings.findReplace.toggle, active: findOpen, onClick: onToggleFind }))] }, "end"));
736
+ const startSections = [
737
+ showSave && (_jsx(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: _jsx(ToolbarButton, { icon: IconDeviceFloppy, title: strings.toolbar.save, dirty: Boolean(isDirty), onClick: onSave }) }, "save")),
738
+ config.history && (_jsxs(MantineToolbar.Group, { className: "likhari-toolbar-cluster", children: [_jsx(ToolbarButton, { icon: IconArrowBackUp, title: strings.toolbar.undo, disabled: !state.canUndo, onClick: () => editor.dispatchCommand(UNDO_COMMAND, undefined) }), _jsx(ToolbarButton, { icon: IconArrowForwardUp, title: strings.toolbar.redo, disabled: !state.canRedo, onClick: () => editor.dispatchCommand(REDO_COMMAND, undefined) })] }, "history")),
739
+ formatSection,
740
+ alignSection,
741
+ fontSection,
742
+ insertSection,
743
+ toolsSection,
834
744
  ];
835
- return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
836
- .filter((group) => !overflowGroupKeys.has(group.key))
837
- .map((group) => (_jsx(MovableGroupButtons, { group: group }, group.key))), overflowGroupKeys.size > 0 && (_jsxs(Menu, { position: "bottom-end", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 210, closeOnItemClick: true, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "aria-label": strings.toolbar.moreFormatting, title: strings.toolbar.moreFormatting, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconDots, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: movableGroups
838
- .filter((group) => overflowGroupKeys.has(group.key))
839
- .flatMap((group) => group.items.map((item) => (_jsx(OverflowItem, { icon: item.icon, label: item.label, active: item.active, onClick: runOverflowAction(item.onClick) }, item.key)))) })] })), _jsxs(Menu, { opened: contextMenu !== null, onChange: (opened) => {
745
+ return (_jsxs(MantineToolbar, { "aria-label": strings.toolbar.ariaLabel, className: "likhari-toolbar", children: [withDividers(startSections), endSection, _jsxs(Menu, { opened: contextMenu !== null, onChange: (opened) => {
840
746
  if (!opened)
841
747
  setContextMenu(null);
842
748
  }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 260, 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: () => {
@@ -864,14 +770,5 @@ export function Toolbar({ config, dictionaryStores = [], thesaurusStores = [], o
864
770
  }, children: synonym }, synonym))), _jsx(Menu.Divider, {})] })), contextMenu?.link !== undefined && (_jsxs(_Fragment, { children: [_jsx(LinkMenuItems, { url: contextMenu.link, onEdit: () => {
865
771
  restoreSelection();
866
772
  openLinkDialog();
867
- }, 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, {})] })), _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 })] })] }), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
868
- position: 'absolute',
869
- visibility: 'hidden',
870
- pointerEvents: 'none',
871
- top: 0,
872
- left: 0,
873
- display: 'flex',
874
- flexWrap: 'nowrap',
875
- gap: 8,
876
- }, children: [fixedGroups, movableGroups.map((group) => (_jsx(MovableGroupButtons, { group: group }, group.key)))] }), (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 })] }));
773
+ }, 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, {})] })), _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 })] })] }), (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 })] }));
877
774
  }
@@ -52,7 +52,7 @@ const en = {
52
52
  subscript: 'Subscript',
53
53
  uppercase: 'UPPERCASE',
54
54
  lowercase: 'lowercase',
55
- capitalize: 'Capitalize',
55
+ capitalize: 'InitCap',
56
56
  clearFormatting: 'Clear formatting',
57
57
  outdent: 'Outdent',
58
58
  indent: 'Indent',
@@ -19,12 +19,6 @@
19
19
  its own height so only the canvas below scrolls. */
20
20
  .likhari-toolbar {
21
21
  display: flex;
22
- /* Containing block for the hidden width-measuring clone (Toolbar.tsx):
23
- without this, its position: absolute would resolve against the
24
- viewport instead, and its intentionally-unwrapped (possibly very wide)
25
- content could inflate the host page's scroll area instead of being
26
- clipped by .likhari-root's overflow: hidden. */
27
- position: relative;
28
22
  flex-shrink: 0;
29
23
  align-items: center;
30
24
  gap: 8px;
@@ -57,55 +51,6 @@
57
51
  pointer-events: auto;
58
52
  }
59
53
 
60
- .likhari-toolbar-group {
61
- display: flex;
62
- align-items: center;
63
- gap: 2px;
64
- padding: 0 6px;
65
- border-inline-end: 0.5px solid var(--editor-line);
66
- }
67
-
68
- .likhari-toolbar-group:last-child {
69
- border-inline-end: none;
70
- }
71
-
72
- .likhari-toolbar-button {
73
- display: inline-flex;
74
- align-items: center;
75
- justify-content: center;
76
- min-width: 30px;
77
- height: 30px;
78
- padding: 0 6px;
79
- border-radius: 6px;
80
- border: none;
81
- background: transparent;
82
- color: var(--editor-ink);
83
- cursor: pointer;
84
- }
85
-
86
- .likhari-toolbar-button svg {
87
- display: block;
88
- }
89
-
90
- .likhari-toolbar-button:hover {
91
- background: var(--editor-line);
92
- }
93
-
94
- .likhari-toolbar-button[data-active='true'] {
95
- background: var(--editor-accent-soft);
96
- color: var(--editor-accent);
97
- }
98
-
99
- .likhari-toolbar-button:focus-visible {
100
- outline: 2px solid var(--editor-accent);
101
- outline-offset: 1px;
102
- }
103
-
104
- .likhari-toolbar-button:disabled {
105
- opacity: 0.4;
106
- cursor: not-allowed;
107
- }
108
-
109
54
  /* Mantine Select inside the toolbar — sized to the 30px control height and
110
55
  themed from the --editor-* tokens (the closed input only; the dropdown is
111
56
  portaled to <body>, outside the token scope, so it uses Mantine's own
@@ -125,35 +70,35 @@
125
70
  cursor: not-allowed;
126
71
  }
127
72
 
128
- .likhari-overflow-item[data-active='true'] {
129
- background: var(--mantine-primary-color-light);
130
- color: var(--mantine-primary-color-filled);
73
+ /* A cluster of related buttons (undo/redo, bold/italic/underline/strikethrough, ...) reads as one control. */
74
+ .likhari-toolbar-cluster {
75
+ border: 1px solid var(--editor-line-strong);
76
+ border-radius: 6px;
77
+ background: var(--editor-paper);
78
+ overflow: hidden;
131
79
  }
132
80
 
133
- /* Responsive collapse — editor-ui-design-spec.md §3.3. These groups hold
134
- only not-yet-implemented placeholders, so hiding them below the tablet
135
- breakpoint costs nothing functional while keeping the primary row usable
136
- on small screens; the toolbar wraps onto a second row if the
137
- still-visible controls alone overflow a narrow viewport. */
138
- @media (max-width: 900px) {
139
- .likhari-toolbar-group--collapse-tablet {
140
- display: none;
141
- }
81
+ .likhari-toolbar-cluster [data-toolbar-toggle] {
82
+ border: none;
83
+ border-radius: 0;
84
+ }
85
+
86
+ .likhari-toolbar-cluster [data-toolbar-toggle] + [data-toolbar-toggle] {
87
+ border-inline-start: 1px solid var(--editor-line);
142
88
  }
143
89
 
144
- /* Save (icon-only ToolbarButton): filled accent while there are unsaved
145
- changes, back to the plain outline once saved — editor-ui-design-spec.md §7. */
146
- .likhari-toolbar-button[data-dirty='true'] {
90
+ /* Save: filled accent while there are unsaved changes, back to the plain outline once saved. */
91
+ .likhari-toolbar [data-dirty='true'] {
147
92
  background: var(--editor-accent);
148
93
  color: var(--editor-paper);
149
94
  }
150
95
 
151
- .likhari-toolbar-button[data-dirty='true']:hover {
96
+ .likhari-toolbar [data-dirty='true']:hover {
152
97
  background: var(--editor-accent);
153
98
  filter: brightness(1.1);
154
99
  }
155
100
 
156
- .likhari-toolbar-button[data-dirty='false'] {
101
+ .likhari-toolbar [data-dirty='false'] {
157
102
  color: var(--editor-accent);
158
103
  }
159
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.28-dev.254",
3
+ "version": "0.1.28-dev.257",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -47,14 +47,14 @@
47
47
  "nspell": "^2.1.5"
48
48
  },
49
49
  "peerDependencies": {
50
- "@mantine/core": "^9.6.3",
51
- "@mantine/hooks": "^9.6.3",
50
+ "@mantine/core": "^9.7.0",
51
+ "@mantine/hooks": "^9.7.0",
52
52
  "react": "^19.2.0",
53
53
  "react-dom": "^19.2.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@mantine/core": "^9.6.3",
57
- "@mantine/hooks": "^9.6.3",
56
+ "@mantine/core": "^9.7.0",
57
+ "@mantine/hooks": "^9.7.0",
58
58
  "@types/nspell": "^2.1.6"
59
59
  }
60
60
  }