@casualoffice/sheets 0.13.0 → 0.14.0

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/chrome.js CHANGED
@@ -526,7 +526,7 @@ function AutoSumPicker({ api }) {
526
526
  }
527
527
 
528
528
  // src/chrome/Toolbar.tsx
529
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
529
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
530
530
  var NO_STYLE = {
531
531
  bold: false,
532
532
  italic: false,
@@ -536,7 +536,8 @@ var NO_STYLE = {
536
536
  vt: 0,
537
537
  tb: 0,
538
538
  ff: "",
539
- fs: 0
539
+ fs: 0,
540
+ nf: ""
540
541
  };
541
542
  function readActiveStyle(api) {
542
543
  const sel = api.getSelection();
@@ -553,138 +554,236 @@ function readActiveStyle(api) {
553
554
  vt: typeof s?.vt === "number" ? s.vt : 0,
554
555
  tb: typeof s?.tb === "number" ? s.tb : 0,
555
556
  ff: typeof s?.ff === "string" ? s.ff : "",
556
- fs: typeof s?.fs === "number" ? s.fs : 0
557
+ fs: typeof s?.fs === "number" ? s.fs : 0,
558
+ nf: typeof s?.n?.pattern === "string" ? s.n.pattern : ""
557
559
  };
558
560
  }
559
- var FONT_FAMILIES = ["Arial", "Calibri", "Times New Roman", "Courier New", "Georgia", "Verdana"];
560
- var FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36, 48, 72];
561
+ var FONT_FAMILIES = [
562
+ "Calibri",
563
+ "Arial",
564
+ "Helvetica",
565
+ "Inter",
566
+ "Times New Roman",
567
+ "Georgia",
568
+ "Verdana",
569
+ "Courier New",
570
+ "JetBrains Mono"
571
+ ];
572
+ var FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 72];
573
+ var NUMBER_FORMAT_PATTERNS = {
574
+ general: "",
575
+ integer: "#,##0",
576
+ number: "#,##0.00",
577
+ currency: '"$"#,##0.00',
578
+ accounting: '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
579
+ percent: "0.00%",
580
+ date: "yyyy-mm-dd",
581
+ time: "hh:mm:ss",
582
+ scientific: "0.00E+00",
583
+ text: "@"
584
+ };
585
+ var NUMBER_FORMAT_OPTIONS = [
586
+ { value: "general", label: "General" },
587
+ { value: "integer", label: "Number (no decimals)" },
588
+ { value: "number", label: "Number (2 decimals)" },
589
+ { value: "currency", label: "Currency" },
590
+ { value: "accounting", label: "Accounting" },
591
+ { value: "percent", label: "Percent" },
592
+ { value: "date", label: "Date (yyyy-mm-dd)" },
593
+ { value: "time", label: "Time (hh:mm:ss)" },
594
+ { value: "scientific", label: "Scientific" },
595
+ { value: "text", label: "Text" }
596
+ ];
597
+ function detectFormatKey(pattern) {
598
+ for (const k of Object.keys(NUMBER_FORMAT_PATTERNS)) {
599
+ if (NUMBER_FORMAT_PATTERNS[k] === pattern) return k;
600
+ }
601
+ return "general";
602
+ }
561
603
  var GROUPS = [
562
- [
563
- { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
564
- { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" }
565
- ],
566
- [
567
- { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
568
- {
569
- id: "italic",
570
- label: "Italic",
571
- command: "sheet.command.set-range-italic",
572
- icon: "format_italic"
573
- },
574
- {
575
- id: "underline",
576
- label: "Underline",
577
- command: "sheet.command.set-range-underline",
578
- icon: "format_underlined"
579
- },
580
- {
581
- id: "strikethrough",
582
- label: "Strikethrough",
583
- command: "sheet.command.set-range-stroke",
584
- icon: "format_strikethrough"
585
- }
586
- ],
587
- [
588
- {
589
- id: "align-left",
590
- label: "Align left",
591
- command: "sheet.command.set-horizontal-text-align",
592
- icon: "format_align_left",
593
- params: { value: 1 }
594
- },
595
- {
596
- id: "align-center",
597
- label: "Align center",
598
- command: "sheet.command.set-horizontal-text-align",
599
- icon: "format_align_center",
600
- params: { value: 2 }
601
- },
602
- {
603
- id: "align-right",
604
- label: "Align right",
605
- command: "sheet.command.set-horizontal-text-align",
606
- icon: "format_align_right",
607
- params: { value: 3 }
608
- }
609
- ],
610
- [
611
- {
612
- id: "align-top",
613
- label: "Align top",
614
- command: "sheet.command.set-vertical-text-align",
615
- icon: "vertical_align_top",
616
- params: { value: 1 }
617
- },
618
- {
619
- id: "align-middle",
620
- label: "Align middle",
621
- command: "sheet.command.set-vertical-text-align",
622
- icon: "vertical_align_center",
623
- params: { value: 2 }
624
- },
625
- {
626
- id: "align-bottom",
627
- label: "Align bottom",
628
- command: "sheet.command.set-vertical-text-align",
629
- icon: "vertical_align_bottom",
630
- params: { value: 3 }
631
- },
632
- {
633
- id: "wrap-text",
634
- label: "Wrap text",
635
- command: "sheet.command.set-text-wrap",
636
- icon: "wrap_text",
637
- params: { value: 3 }
638
- }
639
- ],
640
- [
641
- {
642
- id: "merge",
643
- label: "Merge cells",
644
- command: "sheet.command.add-worksheet-merge-all",
645
- icon: "cell_merge"
646
- },
647
- {
648
- id: "unmerge",
649
- label: "Unmerge cells",
650
- command: "sheet.command.remove-worksheet-merge",
651
- icon: "splitscreen_vertical_add"
652
- }
653
- ],
654
- [
655
- {
656
- id: "currency",
657
- label: "Currency format",
658
- command: "sheet.command.numfmt.set.currency",
659
- icon: "attach_money"
660
- },
661
- {
662
- id: "percent",
663
- label: "Percent format",
664
- command: "sheet.command.numfmt.set.percent",
665
- icon: "percent"
666
- },
667
- {
668
- id: "decimal-increase",
669
- label: "Increase decimals",
670
- command: "sheet.command.numfmt.add.decimal.command",
671
- icon: "decimal_increase"
672
- },
673
- {
674
- id: "decimal-decrease",
675
- label: "Decrease decimals",
676
- command: "sheet.command.numfmt.subtract.decimal.command",
677
- icon: "decimal_decrease"
678
- }
679
- ],
680
- [
681
- {
682
- id: "clear-format",
683
- label: "Clear formatting",
684
- command: "sheet.command.clear-selection-format",
685
- icon: "format_clear"
686
- }
687
- ]
604
+ {
605
+ feature: "history",
606
+ actions: [
607
+ { id: "undo", label: "Undo (Ctrl+Z)", command: "univer.command.undo", icon: "undo" },
608
+ { id: "redo", label: "Redo (Ctrl+Y)", command: "univer.command.redo", icon: "redo" }
609
+ ]
610
+ },
611
+ {
612
+ feature: "clipboard",
613
+ actions: [
614
+ {
615
+ id: "paste",
616
+ label: "Paste (Ctrl+V)",
617
+ command: "univer.command.paste",
618
+ icon: "content_paste"
619
+ },
620
+ { id: "cut", label: "Cut (Ctrl+X)", command: "univer.command.cut", icon: "content_cut" },
621
+ { id: "copy", label: "Copy (Ctrl+C)", command: "univer.command.copy", icon: "content_copy" },
622
+ {
623
+ id: "paste-values",
624
+ label: "Paste values only",
625
+ command: "univer.command.paste",
626
+ icon: "content_paste_go",
627
+ params: { value: "special-paste-value" }
628
+ }
629
+ ]
630
+ },
631
+ {
632
+ feature: "format-painter",
633
+ actions: [
634
+ {
635
+ id: "format-painter",
636
+ label: "Format Painter",
637
+ command: "sheet.command.set-once-format-painter",
638
+ icon: "format_paint"
639
+ }
640
+ ]
641
+ },
642
+ {
643
+ feature: "font-style",
644
+ actions: [
645
+ {
646
+ id: "bold",
647
+ label: "Bold (Ctrl+B)",
648
+ command: "sheet.command.set-range-bold",
649
+ icon: "format_bold"
650
+ },
651
+ {
652
+ id: "italic",
653
+ label: "Italic (Ctrl+I)",
654
+ command: "sheet.command.set-range-italic",
655
+ icon: "format_italic"
656
+ },
657
+ {
658
+ id: "underline",
659
+ label: "Underline (Ctrl+U)",
660
+ command: "sheet.command.set-range-underline",
661
+ icon: "format_underlined"
662
+ },
663
+ {
664
+ id: "strikethrough",
665
+ label: "Strikethrough",
666
+ command: "sheet.command.set-range-stroke",
667
+ icon: "format_strikethrough"
668
+ }
669
+ ]
670
+ },
671
+ {
672
+ feature: "alignment",
673
+ actions: [
674
+ {
675
+ id: "align-left",
676
+ label: "Align left",
677
+ command: "sheet.command.set-horizontal-text-align",
678
+ icon: "format_align_left",
679
+ params: { value: 1 }
680
+ },
681
+ {
682
+ id: "align-center",
683
+ label: "Align center",
684
+ command: "sheet.command.set-horizontal-text-align",
685
+ icon: "format_align_center",
686
+ params: { value: 2 }
687
+ },
688
+ {
689
+ id: "align-right",
690
+ label: "Align right",
691
+ command: "sheet.command.set-horizontal-text-align",
692
+ icon: "format_align_right",
693
+ params: { value: 3 }
694
+ },
695
+ {
696
+ id: "wrap-text",
697
+ label: "Wrap text",
698
+ command: "sheet.command.set-text-wrap",
699
+ icon: "wrap_text",
700
+ params: { value: 3 }
701
+ }
702
+ ]
703
+ },
704
+ {
705
+ feature: "alignment",
706
+ actions: [
707
+ {
708
+ id: "align-top",
709
+ label: "Align top",
710
+ command: "sheet.command.set-vertical-text-align",
711
+ icon: "vertical_align_top",
712
+ params: { value: 1 }
713
+ },
714
+ {
715
+ id: "align-middle",
716
+ label: "Align middle",
717
+ command: "sheet.command.set-vertical-text-align",
718
+ icon: "vertical_align_center",
719
+ params: { value: 2 }
720
+ },
721
+ {
722
+ id: "align-bottom",
723
+ label: "Align bottom",
724
+ command: "sheet.command.set-vertical-text-align",
725
+ icon: "vertical_align_bottom",
726
+ params: { value: 3 }
727
+ }
728
+ ]
729
+ },
730
+ {
731
+ feature: "merge",
732
+ actions: [
733
+ {
734
+ id: "merge",
735
+ label: "Merge cells",
736
+ command: "sheet.command.add-worksheet-merge-all",
737
+ icon: "cell_merge"
738
+ },
739
+ {
740
+ id: "unmerge",
741
+ label: "Unmerge cells",
742
+ command: "sheet.command.remove-worksheet-merge",
743
+ icon: "splitscreen_vertical_add"
744
+ }
745
+ ]
746
+ },
747
+ {
748
+ feature: "number",
749
+ actions: [
750
+ {
751
+ id: "currency",
752
+ label: "Currency format",
753
+ command: "sheet.command.numfmt.set.currency",
754
+ icon: "attach_money"
755
+ },
756
+ {
757
+ id: "percent",
758
+ label: "Percent format",
759
+ command: "sheet.command.numfmt.set.percent",
760
+ icon: "percent"
761
+ },
762
+ {
763
+ id: "decimal-increase",
764
+ label: "Increase decimals",
765
+ command: "sheet.command.numfmt.add.decimal.command",
766
+ icon: "decimal_increase"
767
+ },
768
+ {
769
+ id: "decimal-decrease",
770
+ label: "Decrease decimals",
771
+ command: "sheet.command.numfmt.subtract.decimal.command",
772
+ icon: "decimal_decrease"
773
+ }
774
+ ]
775
+ },
776
+ {
777
+ feature: "clear-format",
778
+ actions: [
779
+ {
780
+ id: "clear-format",
781
+ label: "Clear formatting",
782
+ command: "sheet.command.clear-selection-format",
783
+ icon: "format_clear"
784
+ }
785
+ ]
786
+ }
688
787
  ];
689
788
  var BAR_STYLE = {
690
789
  display: "flex",
@@ -694,7 +793,8 @@ var BAR_STYLE = {
694
793
  borderBottom: "1px solid var(--cs-chrome-border, #e6e9ee)",
695
794
  background: "var(--cs-chrome-bg, #eef1f5)",
696
795
  flex: "0 0 auto",
697
- userSelect: "none"
796
+ userSelect: "none",
797
+ flexWrap: "wrap"
698
798
  };
699
799
  var BTN_STYLE4 = {
700
800
  width: 30,
@@ -727,6 +827,9 @@ var SELECT_STYLE = {
727
827
  padding: "0 4px",
728
828
  cursor: "pointer"
729
829
  };
830
+ function enabled(features, key) {
831
+ return features?.[key] !== false;
832
+ }
730
833
  function isActive(id, s) {
731
834
  switch (id) {
732
835
  case "bold":
@@ -755,7 +858,7 @@ function isActive(id, s) {
755
858
  return false;
756
859
  }
757
860
  }
758
- function Toolbar({ api }) {
861
+ function Toolbar({ api, features, onDialogRequest }) {
759
862
  const [active, setActive] = useState4(NO_STYLE);
760
863
  useEffect4(() => {
761
864
  ensureChromeFonts();
@@ -770,75 +873,194 @@ function Toolbar({ api }) {
770
873
  return () => sub?.dispose();
771
874
  }, [api]);
772
875
  const dispatch = (command, params) => void api?.executeCommand(command, params);
773
- const familyValue = active.ff || "Arial";
876
+ const applyFontSize = (size) => {
877
+ if (!Number.isFinite(size) || size <= 0) return;
878
+ dispatch("sheet.command.set-range-fontsize", { value: size });
879
+ };
880
+ const adjustFontSize = (delta) => {
881
+ const current = active.fs && active.fs > 0 ? active.fs : 11;
882
+ const next = Math.max(6, Math.min(72, current + delta));
883
+ if (next === current) return;
884
+ applyFontSize(next);
885
+ };
886
+ const applyNumberFormat = (pattern) => {
887
+ const sheet = api?.univer.getActiveWorkbook()?.getActiveSheet();
888
+ const range = sheet?.getActiveRange?.();
889
+ range?.setNumberFormat?.(pattern);
890
+ };
891
+ const familyValue = active.ff || "Calibri";
774
892
  const familyOptions = active.ff && !FONT_FAMILIES.includes(active.ff) ? [active.ff, ...FONT_FAMILIES] : FONT_FAMILIES;
775
893
  const sizeValue = active.fs || 11;
776
894
  const sizeOptions = active.fs && !FONT_SIZES.includes(active.fs) ? [active.fs, ...FONT_SIZES] : FONT_SIZES;
895
+ const numberFormatValue = detectFormatKey(active.nf);
896
+ const renderActionButton = (a) => {
897
+ const on = isActive(a.id, active);
898
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
899
+ return /* @__PURE__ */ jsx5(
900
+ "button",
901
+ {
902
+ type: "button",
903
+ title: a.label,
904
+ "aria-label": a.label,
905
+ "aria-pressed": on,
906
+ "data-action": a.id,
907
+ "data-testid": `cs-${a.id}`,
908
+ "data-active": on ? "true" : void 0,
909
+ disabled: !api,
910
+ style: {
911
+ ...BTN_STYLE4,
912
+ background: baseBg,
913
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE4.color
914
+ },
915
+ onMouseDown: (e) => {
916
+ e.preventDefault();
917
+ dispatch(a.command, a.params);
918
+ },
919
+ onMouseEnter: (e) => {
920
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
921
+ },
922
+ onMouseLeave: (e) => {
923
+ e.currentTarget.style.background = baseBg;
924
+ },
925
+ children: /* @__PURE__ */ jsx5(Icon, { name: a.icon, size: 20 })
926
+ },
927
+ a.id
928
+ );
929
+ };
930
+ const renderIconButton = (id, label, icon, onPress, testid) => /* @__PURE__ */ jsx5(
931
+ "button",
932
+ {
933
+ type: "button",
934
+ title: label,
935
+ "aria-label": label,
936
+ "data-action": id,
937
+ "data-testid": testid,
938
+ disabled: !api,
939
+ style: BTN_STYLE4,
940
+ onMouseDown: (e) => {
941
+ e.preventDefault();
942
+ onPress();
943
+ },
944
+ onMouseEnter: (e) => {
945
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
946
+ },
947
+ onMouseLeave: (e) => {
948
+ e.currentTarget.style.background = "transparent";
949
+ },
950
+ children: /* @__PURE__ */ jsx5(Icon, { name: icon, size: 20 })
951
+ },
952
+ id
953
+ );
954
+ let firstGroupRendered = false;
955
+ const groupNodes = GROUPS.map((group, gi) => {
956
+ if (!enabled(features, group.feature)) return null;
957
+ const visible = group.actions.filter((a) => enabled(features, a.feature ?? group.feature));
958
+ if (visible.length === 0) return null;
959
+ const showDivider = firstGroupRendered;
960
+ firstGroupRendered = true;
961
+ return /* @__PURE__ */ jsxs4("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
962
+ showDivider && /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
963
+ visible.map(renderActionButton)
964
+ ] }, gi);
965
+ });
966
+ const showFont = enabled(features, "font");
967
+ const showColor = enabled(features, "color");
968
+ const showBorders = enabled(features, "borders");
969
+ const showAutoSum = enabled(features, "autosum");
970
+ const showNumberDropdown = enabled(features, "number");
971
+ const showFormatCells = enabled(features, "format-cells") && !!onDialogRequest;
972
+ const showInsertChart = enabled(features, "insert-chart") && !!onDialogRequest;
973
+ const showPivotTable = enabled(features, "pivot-table") && !!onDialogRequest;
974
+ const showDialogGroup = showFormatCells || showInsertChart || showPivotTable;
777
975
  return /* @__PURE__ */ jsxs4("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
778
- /* @__PURE__ */ jsx5(
779
- "select",
780
- {
781
- "aria-label": "Font family",
782
- "data-testid": "cs-font-family",
783
- style: { ...SELECT_STYLE, width: 116 },
784
- value: familyValue,
785
- onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
786
- children: familyOptions.map((f) => /* @__PURE__ */ jsx5("option", { value: f, children: f }, f))
787
- }
788
- ),
789
- /* @__PURE__ */ jsx5(
790
- "select",
791
- {
792
- "aria-label": "Font size",
793
- "data-testid": "cs-font-size",
794
- style: { ...SELECT_STYLE, width: 56, marginLeft: 4 },
795
- value: sizeValue,
796
- onChange: (e) => dispatch("sheet.command.set-range-fontsize", { value: Number(e.target.value) }),
797
- children: sizeOptions.map((s) => /* @__PURE__ */ jsx5("option", { value: s, children: s }, s))
798
- }
799
- ),
800
- /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
801
- GROUPS.map((group, gi) => /* @__PURE__ */ jsxs4("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
802
- gi > 0 && /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
803
- group.map((a) => {
804
- const on = isActive(a.id, active);
805
- const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
806
- return /* @__PURE__ */ jsx5(
807
- "button",
808
- {
809
- type: "button",
810
- title: a.label,
811
- "aria-label": a.label,
812
- "aria-pressed": on,
813
- "data-action": a.id,
814
- "data-active": on ? "true" : void 0,
815
- style: {
816
- ...BTN_STYLE4,
817
- background: baseBg,
818
- color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE4.color
819
- },
820
- onMouseDown: (e) => {
821
- e.preventDefault();
822
- dispatch(a.command, a.params);
823
- },
824
- onMouseEnter: (e) => {
825
- if (!on)
826
- e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
827
- },
828
- onMouseLeave: (e) => {
829
- e.currentTarget.style.background = baseBg;
830
- },
831
- children: /* @__PURE__ */ jsx5(Icon, { name: a.icon, size: 20 })
832
- },
833
- a.id
834
- );
835
- })
836
- ] }, gi)),
837
- /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
838
- /* @__PURE__ */ jsx5(ColorPicker, { api }),
839
- /* @__PURE__ */ jsx5(BordersPicker, { api }),
840
- /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
841
- /* @__PURE__ */ jsx5(AutoSumPicker, { api })
976
+ showFont && /* @__PURE__ */ jsxs4(Fragment, { children: [
977
+ /* @__PURE__ */ jsx5(
978
+ "select",
979
+ {
980
+ "aria-label": "Font family",
981
+ "data-testid": "cs-font-family",
982
+ style: { ...SELECT_STYLE, width: 130 },
983
+ value: familyValue,
984
+ disabled: !api,
985
+ onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
986
+ children: familyOptions.map((f) => /* @__PURE__ */ jsx5("option", { value: f, children: f }, f))
987
+ }
988
+ ),
989
+ /* @__PURE__ */ jsx5(
990
+ "select",
991
+ {
992
+ "aria-label": "Font size",
993
+ "data-testid": "cs-font-size",
994
+ style: { ...SELECT_STYLE, width: 56, marginLeft: 4 },
995
+ value: sizeValue,
996
+ disabled: !api,
997
+ onChange: (e) => applyFontSize(Number(e.target.value)),
998
+ children: sizeOptions.map((s) => /* @__PURE__ */ jsx5("option", { value: s, children: s }, s))
999
+ }
1000
+ ),
1001
+ renderIconButton(
1002
+ "font-size-up",
1003
+ "Increase font size",
1004
+ "text_increase",
1005
+ () => adjustFontSize(1),
1006
+ "cs-font-size-up"
1007
+ ),
1008
+ renderIconButton(
1009
+ "font-size-down",
1010
+ "Decrease font size",
1011
+ "text_decrease",
1012
+ () => adjustFontSize(-1),
1013
+ "cs-font-size-down"
1014
+ ),
1015
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true })
1016
+ ] }),
1017
+ groupNodes,
1018
+ (showColor || showBorders) && /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1019
+ showColor && /* @__PURE__ */ jsx5(ColorPicker, { api }),
1020
+ showBorders && /* @__PURE__ */ jsx5(BordersPicker, { api }),
1021
+ showNumberDropdown && /* @__PURE__ */ jsxs4(Fragment, { children: [
1022
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1023
+ /* @__PURE__ */ jsx5(
1024
+ "select",
1025
+ {
1026
+ "aria-label": "Number format",
1027
+ "data-testid": "cs-number-format",
1028
+ style: { ...SELECT_STYLE, width: 124 },
1029
+ value: numberFormatValue,
1030
+ disabled: !api,
1031
+ onChange: (e) => applyNumberFormat(NUMBER_FORMAT_PATTERNS[e.target.value]),
1032
+ children: NUMBER_FORMAT_OPTIONS.map((o) => /* @__PURE__ */ jsx5("option", { value: o.value, children: o.label }, o.value))
1033
+ }
1034
+ )
1035
+ ] }),
1036
+ showAutoSum && /* @__PURE__ */ jsxs4(Fragment, { children: [
1037
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1038
+ /* @__PURE__ */ jsx5(AutoSumPicker, { api })
1039
+ ] }),
1040
+ showDialogGroup && /* @__PURE__ */ jsxs4(Fragment, { children: [
1041
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1042
+ showFormatCells && renderIconButton(
1043
+ "format-cells",
1044
+ "Format Cells\u2026",
1045
+ "format_shapes",
1046
+ () => onDialogRequest?.("format-cells"),
1047
+ "cs-format-cells"
1048
+ ),
1049
+ showInsertChart && renderIconButton(
1050
+ "insert-chart",
1051
+ "Insert chart",
1052
+ "bar_chart",
1053
+ () => onDialogRequest?.("insert-chart"),
1054
+ "cs-insert-chart"
1055
+ ),
1056
+ showPivotTable && renderIconButton(
1057
+ "pivot-table",
1058
+ "Insert PivotTable",
1059
+ "pivot_table_chart",
1060
+ () => onDialogRequest?.("pivot-table"),
1061
+ "cs-pivot-table"
1062
+ )
1063
+ ] })
842
1064
  ] });
843
1065
  }
844
1066
 
@@ -1431,7 +1653,7 @@ function FormulaBar({ api }) {
1431
1653
  // src/chrome/StatusBar.tsx
1432
1654
  import { useEffect as useEffect7, useState as useState7 } from "react";
1433
1655
  import { ICommandService as ICommandService4 } from "@univerjs/core";
1434
- import { Fragment, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1656
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1435
1657
  function readStats(api) {
1436
1658
  const sel = api.getSelection();
1437
1659
  const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
@@ -1543,7 +1765,7 @@ function StatusBar({ api }) {
1543
1765
  const zoomBy = (delta) => setZoomRatio(Math.round((zoom + delta) * 100) / 100);
1544
1766
  const resetZoom = () => setZoomRatio(1);
1545
1767
  return /* @__PURE__ */ jsxs7("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
1546
- stats && /* @__PURE__ */ jsxs7(Fragment, { children: [
1768
+ stats && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1547
1769
  stats.numCount > 0 && /* @__PURE__ */ jsxs7("span", { "data-stat": "average", children: [
1548
1770
  "Average: ",
1549
1771
  fmt(stats.avg)
@@ -1552,7 +1774,7 @@ function StatusBar({ api }) {
1552
1774
  "Count: ",
1553
1775
  stats.count
1554
1776
  ] }),
1555
- stats.numCount > 0 && /* @__PURE__ */ jsxs7(Fragment, { children: [
1777
+ stats.numCount > 0 && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1556
1778
  /* @__PURE__ */ jsxs7("span", { "data-stat": "num-count", children: [
1557
1779
  "Numerical Count: ",
1558
1780
  stats.numCount
@@ -1629,17 +1851,639 @@ function StatusBar({ api }) {
1629
1851
 
1630
1852
  // src/chrome/MenuBar.tsx
1631
1853
  import { useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
1854
+ import { BorderStyleTypes, BorderType } from "@univerjs/core";
1855
+
1856
+ // src/univer/lazy-plugins.ts
1857
+ var LOADERS = {
1858
+ cf: async () => {
1859
+ const [base, ui] = await Promise.all([
1860
+ import("@univerjs/sheets-conditional-formatting"),
1861
+ import("@univerjs/sheets-conditional-formatting-ui")
1862
+ ]);
1863
+ return [
1864
+ [base.UniverSheetsConditionalFormattingPlugin],
1865
+ [ui.UniverSheetsConditionalFormattingUIPlugin]
1866
+ ];
1867
+ },
1868
+ dv: async () => {
1869
+ const [base, ui] = await Promise.all([
1870
+ import("@univerjs/sheets-data-validation"),
1871
+ import("@univerjs/sheets-data-validation-ui")
1872
+ ]);
1873
+ return [
1874
+ [base.UniverSheetsDataValidationPlugin],
1875
+ [ui.UniverSheetsDataValidationUIPlugin]
1876
+ ];
1877
+ },
1878
+ hyperlink: async () => {
1879
+ const [base, ui] = await Promise.all([
1880
+ import("@univerjs/sheets-hyper-link"),
1881
+ import("@univerjs/sheets-hyper-link-ui")
1882
+ ]);
1883
+ return [
1884
+ [base.UniverSheetsHyperLinkPlugin],
1885
+ [ui.UniverSheetsHyperLinkUIPlugin]
1886
+ ];
1887
+ },
1888
+ note: async () => {
1889
+ const [base, ui] = await Promise.all([
1890
+ import("@univerjs/sheets-note"),
1891
+ import("@univerjs/sheets-note-ui")
1892
+ ]);
1893
+ return [[base.UniverSheetsNotePlugin], [ui.UniverSheetsNoteUIPlugin]];
1894
+ },
1895
+ table: async () => {
1896
+ const [base, ui] = await Promise.all([
1897
+ import("@univerjs/sheets-table"),
1898
+ import("@univerjs/sheets-table-ui")
1899
+ ]);
1900
+ return [[base.UniverSheetsTablePlugin], [ui.UniverSheetsTableUIPlugin]];
1901
+ },
1902
+ threadComment: async () => {
1903
+ const [tc, tcUi, sheetsTc, sheetsTcUi] = await Promise.all([
1904
+ import("@univerjs/thread-comment"),
1905
+ import("@univerjs/thread-comment-ui"),
1906
+ import("@univerjs/sheets-thread-comment"),
1907
+ import("@univerjs/sheets-thread-comment-ui")
1908
+ ]);
1909
+ return [
1910
+ [tc.UniverThreadCommentPlugin],
1911
+ [tcUi.UniverThreadCommentUIPlugin],
1912
+ [sheetsTc.UniverSheetsThreadCommentPlugin],
1913
+ [sheetsTcUi.UniverSheetsThreadCommentUIPlugin]
1914
+ ];
1915
+ },
1916
+ drawing: async () => {
1917
+ const [d, dUi, sd, sdUi] = await Promise.all([
1918
+ import("@univerjs/drawing"),
1919
+ import("@univerjs/drawing-ui"),
1920
+ import("@univerjs/sheets-drawing"),
1921
+ import("@univerjs/sheets-drawing-ui"),
1922
+ // Side-effect imports: install FWorksheet.insertImage / getImages /
1923
+ // updateImages on the facade prototype. Without these, code that
1924
+ // reaches in via the FUniver facade (e2e specs, future shell glue)
1925
+ // sees an undefined method even though the plugin is registered.
1926
+ import("@univerjs/sheets-drawing/facade"),
1927
+ import("@univerjs/sheets-drawing-ui/facade")
1928
+ ]);
1929
+ return [
1930
+ [d.UniverDrawingPlugin],
1931
+ [dUi.UniverDrawingUIPlugin],
1932
+ [sd.UniverSheetsDrawingPlugin],
1933
+ [sdUi.UniverSheetsDrawingUIPlugin]
1934
+ ];
1935
+ },
1936
+ sort: async () => {
1937
+ const [base, ui] = await Promise.all([
1938
+ import("@univerjs/sheets-sort"),
1939
+ import("@univerjs/sheets-sort-ui")
1940
+ ]);
1941
+ return [[base.UniverSheetsSortPlugin], [ui.UniverSheetsSortUIPlugin]];
1942
+ },
1943
+ filter: async () => {
1944
+ const [base, ui] = await Promise.all([
1945
+ import("@univerjs/sheets-filter"),
1946
+ import("@univerjs/sheets-filter-ui")
1947
+ ]);
1948
+ return [[base.UniverSheetsFilterPlugin], [ui.UniverSheetsFilterUIPlugin]];
1949
+ },
1950
+ findReplace: async () => {
1951
+ const [base, sheets] = await Promise.all([
1952
+ import("@univerjs/find-replace"),
1953
+ import("@univerjs/sheets-find-replace")
1954
+ ]);
1955
+ return [[base.UniverFindReplacePlugin], [sheets.UniverSheetsFindReplacePlugin]];
1956
+ }
1957
+ };
1958
+ var loaded = /* @__PURE__ */ new Set();
1959
+ var inflight = /* @__PURE__ */ new Map();
1960
+ var currentUniver = null;
1961
+ function ensurePlugin(univer, group) {
1962
+ if (loaded.has(group)) return Promise.resolve();
1963
+ const existing = inflight.get(group);
1964
+ if (existing) return existing;
1965
+ const loader = LOADERS[group];
1966
+ if (!loader) return Promise.resolve();
1967
+ const p = loader().then((plugins) => {
1968
+ for (const [PluginCtor, config] of plugins) {
1969
+ univer.registerPlugin(PluginCtor, config);
1970
+ }
1971
+ loaded.add(group);
1972
+ inflight.delete(group);
1973
+ });
1974
+ inflight.set(group, p);
1975
+ return p;
1976
+ }
1977
+ function ensurePluginByName(group) {
1978
+ if (loaded.has(group)) return Promise.resolve();
1979
+ if (!currentUniver) return Promise.resolve();
1980
+ return ensurePlugin(currentUniver, group);
1981
+ }
1982
+
1983
+ // src/chrome/MenuBar.tsx
1632
1984
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1985
+ function fmtShortcut(shortcut) {
1986
+ const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.platform || "");
1987
+ if (!isMac) return shortcut;
1988
+ return shortcut.split("+").map((part) => {
1989
+ switch (part) {
1990
+ case "Ctrl":
1991
+ return "\u2318";
1992
+ case "Alt":
1993
+ return "\u2325";
1994
+ case "Shift":
1995
+ return "\u21E7";
1996
+ default:
1997
+ return part;
1998
+ }
1999
+ }).join("");
2000
+ }
2001
+ function fu(api) {
2002
+ return api.univer;
2003
+ }
2004
+ function activeRange(api) {
2005
+ return fu(api).getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
2006
+ }
2007
+ function activeSheet(api) {
2008
+ return fu(api).getActiveWorkbook()?.getActiveSheet() ?? null;
2009
+ }
2010
+ function selectionA1(api) {
2011
+ const range = activeRange(api);
2012
+ if (!range) return null;
2013
+ try {
2014
+ return range.getA1Notation();
2015
+ } catch {
2016
+ return null;
2017
+ }
2018
+ }
2019
+ var undo = (api) => void api.executeCommand("univer.command.undo");
2020
+ var redo = (api) => void api.executeCommand("univer.command.redo");
2021
+ var cut = (api) => void api.executeCommand("univer.command.cut");
2022
+ var copy = (api) => void api.executeCommand("univer.command.copy");
2023
+ var paste = (api) => void api.executeCommand("univer.command.paste");
2024
+ var pasteFormattingOnly = (api) => void api.executeCommand("sheet.command.paste-format");
2025
+ var insertRowAbove = (api) => {
2026
+ const range = activeRange(api);
2027
+ const sheet = activeSheet(api);
2028
+ if (!range || !sheet) return;
2029
+ sheet.insertRowBefore(range.getRow());
2030
+ };
2031
+ var insertRowBelow = (api) => {
2032
+ const range = activeRange(api);
2033
+ const sheet = activeSheet(api);
2034
+ if (!range || !sheet) return;
2035
+ sheet.insertRowAfter(range.getRow() + range.getHeight() - 1);
2036
+ };
2037
+ var insertColumnLeft = (api) => {
2038
+ const range = activeRange(api);
2039
+ const sheet = activeSheet(api);
2040
+ if (!range || !sheet) return;
2041
+ sheet.insertColumnBefore(range.getColumn());
2042
+ };
2043
+ var insertColumnRight = (api) => {
2044
+ const range = activeRange(api);
2045
+ const sheet = activeSheet(api);
2046
+ if (!range || !sheet) return;
2047
+ sheet.insertColumnAfter(range.getColumn() + range.getWidth() - 1);
2048
+ };
2049
+ var deleteSelectedRow = (api) => {
2050
+ const range = activeRange(api);
2051
+ const sheet = activeSheet(api);
2052
+ if (!range || !sheet) return;
2053
+ sheet.deleteRows(range.getRow(), range.getHeight());
2054
+ };
2055
+ var deleteSelectedColumn = (api) => {
2056
+ const range = activeRange(api);
2057
+ const sheet = activeSheet(api);
2058
+ if (!range || !sheet) return;
2059
+ sheet.deleteColumns(range.getColumn(), range.getWidth());
2060
+ };
2061
+ var insertNewSheet = (api) => {
2062
+ fu(api).getActiveWorkbook()?.insertSheet();
2063
+ };
2064
+ var insertImage = (api) => void api.executeCommand("sheet.command.insert-float-image");
2065
+ var insertHyperlink = (api) => void api.executeCommand("sheet.operation.insert-hyper-link");
2066
+ var insertComment = (api) => void api.executeCommand("sheet.operation.show-comment-modal");
2067
+ var insertTodayDate = (api) => {
2068
+ const range = activeRange(api);
2069
+ if (!range) return;
2070
+ const today = /* @__PURE__ */ new Date();
2071
+ const pad = (n) => n.toString().padStart(2, "0");
2072
+ const v = `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`;
2073
+ range.setValue({ v });
2074
+ };
2075
+ var insertCurrentTime = (api) => {
2076
+ const range = activeRange(api);
2077
+ if (!range) return;
2078
+ const now = /* @__PURE__ */ new Date();
2079
+ const pad = (n) => n.toString().padStart(2, "0");
2080
+ const v = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
2081
+ range.setValue({ v });
2082
+ };
2083
+ var insertTable = (api) => {
2084
+ void (async () => {
2085
+ const range = activeRange(api);
2086
+ const sheet = activeSheet(api);
2087
+ const wb = fu(api).getActiveWorkbook();
2088
+ if (!range || !sheet || !wb) return;
2089
+ await ensurePluginByName("table");
2090
+ await api.executeCommand("sheet.command.add-table", {
2091
+ unitId: wb.getId(),
2092
+ subUnitId: sheet.getSheetId(),
2093
+ range: {
2094
+ startRow: range.getRow(),
2095
+ startColumn: range.getColumn(),
2096
+ endRow: range.getRow() + range.getHeight() - 1,
2097
+ endColumn: range.getColumn() + range.getWidth() - 1
2098
+ }
2099
+ });
2100
+ })();
2101
+ };
2102
+ var autoFitColumns = (api) => {
2103
+ const range = activeRange(api);
2104
+ const sheet = activeSheet(api);
2105
+ if (!range || !sheet) return;
2106
+ const withAutoWidth = sheet;
2107
+ withAutoWidth.setColumnAutoWidth?.(range.getColumn(), range.getWidth());
2108
+ };
2109
+ var AUTO_FIT_ROW_CAP = 500;
2110
+ var autoFitRows = (api) => {
2111
+ const range = activeRange(api);
2112
+ const sheet = activeSheet(api);
2113
+ if (!range || !sheet) return;
2114
+ const start = range.getRow();
2115
+ const count = Math.min(range.getHeight(), AUTO_FIT_ROW_CAP);
2116
+ for (let r = 0; r < count; r++) sheet.autoFitRow(start + r);
2117
+ };
2118
+ var NUMBER_FORMAT_PATTERNS2 = {
2119
+ general: "",
2120
+ number: "#,##0.00",
2121
+ integer: "#,##0",
2122
+ currency: '"$"#,##0.00',
2123
+ accounting: '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
2124
+ percent: "0.00%",
2125
+ date: "yyyy-mm-dd",
2126
+ time: "hh:mm:ss",
2127
+ scientific: "0.00E+00",
2128
+ text: "@"
2129
+ };
2130
+ var NUM_FORMAT_ORDER = [
2131
+ "general",
2132
+ "number",
2133
+ "integer",
2134
+ "currency",
2135
+ "accounting",
2136
+ "percent",
2137
+ "date",
2138
+ "time",
2139
+ "scientific",
2140
+ "text"
2141
+ ];
2142
+ var NUM_FORMAT_SHORTCUT = {
2143
+ number: "Ctrl+Shift+1",
2144
+ time: "Ctrl+Shift+2",
2145
+ date: "Ctrl+Shift+3",
2146
+ currency: "Ctrl+Shift+4",
2147
+ percent: "Ctrl+Shift+5",
2148
+ scientific: "Ctrl+Shift+6"
2149
+ };
2150
+ function setNumberFormatByKey(api, key) {
2151
+ const range = activeRange(api);
2152
+ range?.setNumberFormat?.(NUMBER_FORMAT_PATTERNS2[key]);
2153
+ }
2154
+ var increaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.add.decimal.command");
2155
+ var decreaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.subtract.decimal.command");
2156
+ var clearFormat = (api) => void api.executeCommand("sheet.command.clear-selection-format");
2157
+ function applyBorders(api, choice) {
2158
+ const range = activeRange(api);
2159
+ if (!range) return;
2160
+ const type = choice === "all" ? BorderType.ALL : choice === "outside" ? BorderType.OUTSIDE : BorderType.NONE;
2161
+ const style = choice === "none" ? BorderStyleTypes.NONE : BorderStyleTypes.THIN;
2162
+ range.setBorder(type, style, "#000000");
2163
+ }
2164
+ function rowSpan(api) {
2165
+ const wb = fu(api).getActiveWorkbook();
2166
+ const sheet = activeSheet(api);
2167
+ const range = activeRange(api);
2168
+ if (!wb || !sheet || !range) return null;
2169
+ const startRow = range.getRow();
2170
+ const endRow = startRow + range.getHeight() - 1;
2171
+ const maxCol = sheet.getMaxColumns?.() ?? 1;
2172
+ return { wb, sheet, startRow, endRow, maxCol };
2173
+ }
2174
+ function colSpan(api) {
2175
+ const wb = fu(api).getActiveWorkbook();
2176
+ const sheet = activeSheet(api);
2177
+ const range = activeRange(api);
2178
+ if (!wb || !sheet || !range) return null;
2179
+ const startColumn = range.getColumn();
2180
+ const endColumn = startColumn + range.getWidth() - 1;
2181
+ const maxRow = sheet.getMaxRows?.() ?? 1;
2182
+ return { wb, sheet, startColumn, endColumn, maxRow };
2183
+ }
2184
+ var hideSelectedRows = (api) => {
2185
+ const s = rowSpan(api);
2186
+ if (!s) return;
2187
+ void api.executeCommand("sheet.command.set-rows-hidden", {
2188
+ unitId: s.wb.getId(),
2189
+ subUnitId: s.sheet.getSheetId(),
2190
+ ranges: [
2191
+ {
2192
+ startRow: s.startRow,
2193
+ endRow: s.endRow,
2194
+ startColumn: 0,
2195
+ endColumn: Math.max(0, s.maxCol - 1),
2196
+ rangeType: 1
2197
+ }
2198
+ ]
2199
+ });
2200
+ };
2201
+ var unhideSelectedRows = (api) => {
2202
+ const s = rowSpan(api);
2203
+ if (!s) return;
2204
+ void api.executeCommand("sheet.command.set-specific-rows-visible", {
2205
+ unitId: s.wb.getId(),
2206
+ subUnitId: s.sheet.getSheetId(),
2207
+ ranges: [
2208
+ {
2209
+ startRow: s.startRow,
2210
+ endRow: s.endRow,
2211
+ startColumn: 0,
2212
+ endColumn: Math.max(0, s.maxCol - 1),
2213
+ rangeType: 1
2214
+ }
2215
+ ]
2216
+ });
2217
+ };
2218
+ var hideSelectedColumns = (api) => {
2219
+ const s = colSpan(api);
2220
+ if (!s) return;
2221
+ void api.executeCommand("sheet.command.set-col-hidden", {
2222
+ unitId: s.wb.getId(),
2223
+ subUnitId: s.sheet.getSheetId(),
2224
+ ranges: [
2225
+ {
2226
+ startRow: 0,
2227
+ endRow: Math.max(0, s.maxRow - 1),
2228
+ startColumn: s.startColumn,
2229
+ endColumn: s.endColumn,
2230
+ rangeType: 2
2231
+ }
2232
+ ]
2233
+ });
2234
+ };
2235
+ var unhideSelectedColumns = (api) => {
2236
+ const s = colSpan(api);
2237
+ if (!s) return;
2238
+ void api.executeCommand("sheet.command.set-col-visible-on-cols", {
2239
+ unitId: s.wb.getId(),
2240
+ subUnitId: s.sheet.getSheetId(),
2241
+ ranges: [
2242
+ {
2243
+ startRow: 0,
2244
+ endRow: Math.max(0, s.maxRow - 1),
2245
+ startColumn: s.startColumn,
2246
+ endColumn: s.endColumn,
2247
+ rangeType: 2
2248
+ }
2249
+ ]
2250
+ });
2251
+ };
2252
+ var freezeFirstRow = (api) => {
2253
+ activeSheet(api)?.setFrozenRows(1);
2254
+ };
2255
+ var freezeFirstColumn = (api) => {
2256
+ activeSheet(api)?.setFrozenColumns(1);
2257
+ };
2258
+ var freezeAtSelection = (api) => void api.executeCommand("sheet.command.set-selection-frozen");
2259
+ var unfreezePanes = (api) => void api.executeCommand("sheet.command.cancel-frozen");
2260
+ var toggleGridlines = (api) => {
2261
+ const wb = fu(api).getActiveWorkbook();
2262
+ const sheet = activeSheet(api);
2263
+ if (!wb || !sheet) return;
2264
+ void api.executeCommand("sheet.command.toggle-gridlines", {
2265
+ unitId: wb.getId(),
2266
+ subUnitId: sheet.getSheetId(),
2267
+ showGridlines: 0
2268
+ });
2269
+ };
2270
+ var toggleCommentPanel = (api) => void api.executeCommand("sheet.operation.toggle-comment-panel");
2271
+ var jumpToFirstCell = (api) => {
2272
+ activeSheet(api)?.getRange(0, 0).activate();
2273
+ };
2274
+ var switchToPreviousSheet = (api) => switchSheetByDelta(api, -1);
2275
+ var switchToNextSheet = (api) => switchSheetByDelta(api, 1);
2276
+ function switchSheetByDelta(api, delta) {
2277
+ const wb = fu(api).getActiveWorkbook();
2278
+ const active = wb?.getActiveSheet();
2279
+ if (!wb || !active) return;
2280
+ const sheets = wb.getSheets();
2281
+ const activeId = active.getSheetId();
2282
+ const idx = sheets.findIndex((s) => s.getSheetId() === activeId);
2283
+ if (idx < 0) return;
2284
+ const nextIdx = idx + delta;
2285
+ if (nextIdx < 0 || nextIdx >= sheets.length) return;
2286
+ wb.setActiveSheet(sheets[nextIdx]);
2287
+ }
2288
+ var showFormulas = (api) => void api.executeCommand("sheet.command.set-show-formula", {});
2289
+ var sortAsc = (api) => sortRange(api, true);
2290
+ var sortDesc = (api) => sortRange(api, false);
2291
+ function sortRange(api, ascending) {
2292
+ const range = activeRange(api);
2293
+ if (!range) return;
2294
+ const withSort = range;
2295
+ withSort.sort?.({ column: range.getColumn(), ascending });
2296
+ }
2297
+ var toggleFilter = (api) => {
2298
+ void (async () => {
2299
+ await ensurePluginByName("filter");
2300
+ const wb = fu(api).getActiveWorkbook();
2301
+ const sheet = activeSheet(api);
2302
+ const range = activeRange(api);
2303
+ if (!wb || !sheet || !range) return;
2304
+ const sheetWithFilter = sheet;
2305
+ if (sheetWithFilter.getFilter?.()) {
2306
+ await api.executeCommand("sheet.command.remove-sheet-filter", {
2307
+ unitId: wb.getId(),
2308
+ subUnitId: sheet.getSheetId()
2309
+ });
2310
+ return;
2311
+ }
2312
+ await api.executeCommand("sheet.command.set-filter-range", {
2313
+ unitId: wb.getId(),
2314
+ subUnitId: sheet.getSheetId(),
2315
+ range: {
2316
+ startRow: range.getRow(),
2317
+ startColumn: range.getColumn(),
2318
+ endRow: range.getRow() + range.getHeight() - 1,
2319
+ endColumn: range.getColumn() + range.getWidth() - 1
2320
+ }
2321
+ });
2322
+ })();
2323
+ };
2324
+ var splitTextToColumns = (api) => void api.executeCommand("sheet.command.split-text-to-columns");
2325
+ var forceRecalculate = (api) => void api.executeCommand("formula.mutation.set-formula-calculation-start", {
2326
+ forceCalculation: true
2327
+ });
1633
2328
  var MENUS = [
2329
+ {
2330
+ id: "file",
2331
+ label: "File",
2332
+ feature: "file",
2333
+ items: [
2334
+ { kind: "item", id: "properties", label: "Properties\u2026", icon: "info", dialog: "properties" },
2335
+ {
2336
+ kind: "item",
2337
+ id: "about",
2338
+ label: "About casual sheets",
2339
+ icon: "help_outline",
2340
+ dialog: "about"
2341
+ }
2342
+ ]
2343
+ },
1634
2344
  {
1635
2345
  id: "edit",
1636
2346
  label: "Edit",
1637
2347
  items: [
1638
- { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
1639
- { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" },
1640
- { id: "cut", label: "Cut", command: "univer.command.cut", icon: "content_cut" },
1641
- { id: "copy", label: "Copy", command: "univer.command.copy", icon: "content_copy" },
1642
- { id: "paste", label: "Paste", command: "univer.command.paste", icon: "content_paste" }
2348
+ { kind: "item", id: "undo", label: "Undo", icon: "undo", shortcut: "Ctrl+Z", run: undo },
2349
+ { kind: "item", id: "redo", label: "Redo", icon: "redo", shortcut: "Ctrl+Y", run: redo },
2350
+ { kind: "separator", id: "sep-clip" },
2351
+ { kind: "item", id: "cut", label: "Cut", icon: "content_cut", shortcut: "Ctrl+X", run: cut },
2352
+ {
2353
+ kind: "item",
2354
+ id: "copy",
2355
+ label: "Copy",
2356
+ icon: "content_copy",
2357
+ shortcut: "Ctrl+C",
2358
+ run: copy
2359
+ },
2360
+ {
2361
+ kind: "item",
2362
+ id: "paste",
2363
+ label: "Paste",
2364
+ icon: "content_paste",
2365
+ shortcut: "Ctrl+V",
2366
+ run: paste
2367
+ },
2368
+ {
2369
+ kind: "item",
2370
+ id: "paste-format",
2371
+ label: "Paste formatting only",
2372
+ icon: "content_paste",
2373
+ shortcut: "Ctrl+Shift+V",
2374
+ run: pasteFormattingOnly
2375
+ },
2376
+ {
2377
+ kind: "item",
2378
+ id: "paste-special",
2379
+ label: "Paste Special\u2026",
2380
+ icon: "content_paste_go",
2381
+ shortcut: "Ctrl+Alt+V",
2382
+ dialog: "paste-special"
2383
+ },
2384
+ { kind: "separator", id: "sep-find" },
2385
+ {
2386
+ kind: "item",
2387
+ id: "find-replace",
2388
+ label: "Find & Replace\u2026",
2389
+ icon: "search",
2390
+ shortcut: "Ctrl+F",
2391
+ dialog: "find-replace"
2392
+ },
2393
+ { kind: "separator", id: "sep-cells" },
2394
+ {
2395
+ kind: "item",
2396
+ id: "edit-insert-cells",
2397
+ label: "Insert cells\u2026",
2398
+ icon: "add_box",
2399
+ shortcut: "Ctrl++",
2400
+ dialog: "insert-cells"
2401
+ },
2402
+ {
2403
+ kind: "item",
2404
+ id: "edit-delete-cells",
2405
+ label: "Delete cells\u2026",
2406
+ icon: "indeterminate_check_box",
2407
+ shortcut: "Ctrl+-",
2408
+ dialog: "delete-cells"
2409
+ }
2410
+ ]
2411
+ },
2412
+ {
2413
+ id: "view",
2414
+ label: "View",
2415
+ items: [
2416
+ {
2417
+ kind: "item",
2418
+ id: "show-formulas",
2419
+ label: "Show formulas",
2420
+ icon: "description",
2421
+ shortcut: "Ctrl+`",
2422
+ run: showFormulas
2423
+ },
2424
+ {
2425
+ kind: "item",
2426
+ id: "toggle-gridlines",
2427
+ label: "Toggle gridlines",
2428
+ icon: "grid_on",
2429
+ run: toggleGridlines
2430
+ },
2431
+ { kind: "separator", id: "sep-freeze" },
2432
+ {
2433
+ kind: "item",
2434
+ id: "freeze-row",
2435
+ label: "Freeze top row",
2436
+ icon: "border_horizontal",
2437
+ run: freezeFirstRow
2438
+ },
2439
+ {
2440
+ kind: "item",
2441
+ id: "freeze-col",
2442
+ label: "Freeze first column",
2443
+ icon: "border_vertical",
2444
+ run: freezeFirstColumn
2445
+ },
2446
+ {
2447
+ kind: "item",
2448
+ id: "freeze-selection",
2449
+ label: "Freeze panes (at selection)",
2450
+ icon: "grid_4x4",
2451
+ run: freezeAtSelection
2452
+ },
2453
+ { kind: "item", id: "unfreeze", label: "Unfreeze", icon: "grid_off", run: unfreezePanes },
2454
+ { kind: "separator", id: "sep-nav" },
2455
+ {
2456
+ kind: "item",
2457
+ id: "jump-home",
2458
+ label: "Jump to A1",
2459
+ icon: "home",
2460
+ shortcut: "Ctrl+Home",
2461
+ run: jumpToFirstCell
2462
+ },
2463
+ {
2464
+ kind: "item",
2465
+ id: "prev-sheet",
2466
+ label: "Previous sheet",
2467
+ icon: "navigate_before",
2468
+ shortcut: "Ctrl+PageUp",
2469
+ run: switchToPreviousSheet
2470
+ },
2471
+ {
2472
+ kind: "item",
2473
+ id: "next-sheet",
2474
+ label: "Next sheet",
2475
+ icon: "navigate_next",
2476
+ shortcut: "Ctrl+PageDown",
2477
+ run: switchToNextSheet
2478
+ },
2479
+ { kind: "separator", id: "sep-panels" },
2480
+ {
2481
+ kind: "item",
2482
+ id: "comments-panel",
2483
+ label: "Comments panel",
2484
+ icon: "forum",
2485
+ run: toggleCommentPanel
2486
+ }
1643
2487
  ]
1644
2488
  },
1645
2489
  {
@@ -1647,28 +2491,140 @@ var MENUS = [
1647
2491
  label: "Insert",
1648
2492
  items: [
1649
2493
  {
1650
- id: "insert-row",
1651
- label: "Insert row above",
1652
- command: "sheet.command.insert-row-before",
1653
- icon: "add_row_above"
2494
+ kind: "item",
2495
+ id: "new-sheet",
2496
+ label: "New sheet",
2497
+ icon: "add_box",
2498
+ shortcut: "Shift+F11",
2499
+ run: insertNewSheet
1654
2500
  },
1655
2501
  {
1656
- id: "insert-col",
1657
- label: "Insert column left",
1658
- command: "sheet.command.insert-col-before",
1659
- icon: "add_column_left"
2502
+ kind: "item",
2503
+ id: "insert-table",
2504
+ label: "Table",
2505
+ icon: "table_rows",
2506
+ shortcut: "Ctrl+L",
2507
+ run: insertTable,
2508
+ feature: "tables"
1660
2509
  },
1661
2510
  {
1662
- id: "delete-row",
1663
- label: "Delete row",
1664
- command: "sheet.command.remove-row",
1665
- icon: "delete"
2511
+ kind: "item",
2512
+ id: "insert-chart",
2513
+ label: "Chart\u2026",
2514
+ icon: "bar_chart",
2515
+ dialog: "insert-chart",
2516
+ feature: "charts"
1666
2517
  },
1667
2518
  {
1668
- id: "delete-col",
1669
- label: "Delete column",
1670
- command: "sheet.command.remove-col",
1671
- icon: "delete"
2519
+ kind: "item",
2520
+ id: "insert-sparkline",
2521
+ label: "Sparkline\u2026",
2522
+ icon: "show_chart",
2523
+ dialog: "insert-sparkline",
2524
+ feature: "sparklines"
2525
+ },
2526
+ {
2527
+ kind: "item",
2528
+ id: "insert-pivot",
2529
+ label: "PivotTable\u2026",
2530
+ icon: "pivot_table_chart",
2531
+ dialog: "insert-pivot",
2532
+ feature: "pivots"
2533
+ },
2534
+ { kind: "separator", id: "sep-objects" },
2535
+ { kind: "item", id: "insert-image", label: "Image\u2026", icon: "image", run: insertImage },
2536
+ {
2537
+ kind: "item",
2538
+ id: "insert-function",
2539
+ label: "Function\u2026",
2540
+ icon: "functions",
2541
+ shortcut: "Shift+F3",
2542
+ dialog: "insert-function"
2543
+ },
2544
+ {
2545
+ kind: "item",
2546
+ id: "insert-link",
2547
+ label: "Hyperlink\u2026",
2548
+ icon: "link",
2549
+ shortcut: "Ctrl+K",
2550
+ run: insertHyperlink
2551
+ },
2552
+ {
2553
+ kind: "item",
2554
+ id: "insert-comment",
2555
+ label: "Comment",
2556
+ icon: "comment",
2557
+ shortcut: "Shift+F2",
2558
+ run: insertComment
2559
+ },
2560
+ { kind: "separator", id: "sep-rowcol" },
2561
+ {
2562
+ kind: "submenu",
2563
+ id: "insert-rowcol",
2564
+ label: "Rows & columns",
2565
+ icon: "grid_on",
2566
+ items: [
2567
+ {
2568
+ kind: "item",
2569
+ id: "insert-row-above",
2570
+ label: "Row above",
2571
+ icon: "vertical_align_top",
2572
+ run: insertRowAbove
2573
+ },
2574
+ {
2575
+ kind: "item",
2576
+ id: "insert-row-below",
2577
+ label: "Row below",
2578
+ icon: "vertical_align_bottom",
2579
+ run: insertRowBelow
2580
+ },
2581
+ {
2582
+ kind: "item",
2583
+ id: "insert-col-left",
2584
+ label: "Column left",
2585
+ icon: "keyboard_tab_rtl",
2586
+ run: insertColumnLeft
2587
+ },
2588
+ {
2589
+ kind: "item",
2590
+ id: "insert-col-right",
2591
+ label: "Column right",
2592
+ icon: "keyboard_tab",
2593
+ run: insertColumnRight
2594
+ }
2595
+ ]
2596
+ },
2597
+ { kind: "separator", id: "sep-autofit" },
2598
+ {
2599
+ kind: "item",
2600
+ id: "autofit-col",
2601
+ label: "Auto-fit column width",
2602
+ icon: "settings_ethernet",
2603
+ run: autoFitColumns
2604
+ },
2605
+ {
2606
+ kind: "item",
2607
+ id: "autofit-row",
2608
+ label: "Auto-fit row height",
2609
+ icon: "height",
2610
+ run: autoFitRows
2611
+ },
2612
+ { kind: "separator", id: "sep-date" },
2613
+ {
2614
+ kind: "item",
2615
+ id: "insert-today",
2616
+ label: "Today's date",
2617
+ icon: "today",
2618
+ shortcut: "Ctrl+;",
2619
+ run: insertTodayDate
2620
+ },
2621
+ {
2622
+ kind: "item",
2623
+ id: "insert-time",
2624
+ label: "Current time",
2625
+ icon: "schedule",
2626
+ shortcut: "Ctrl+Shift+:",
2627
+ run: insertCurrentTime
1672
2628
  }
1673
2629
  ]
1674
2630
  },
@@ -1676,30 +2632,177 @@ var MENUS = [
1676
2632
  id: "format",
1677
2633
  label: "Format",
1678
2634
  items: [
1679
- { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
1680
2635
  {
2636
+ kind: "item",
2637
+ id: "format-cells",
2638
+ label: "Format cells\u2026",
2639
+ icon: "format_shapes",
2640
+ shortcut: "Ctrl+1",
2641
+ dialog: "format-cells"
2642
+ },
2643
+ { kind: "separator", id: "sep-format-cells" },
2644
+ {
2645
+ kind: "item",
2646
+ id: "bold",
2647
+ label: "Bold",
2648
+ icon: "format_bold",
2649
+ shortcut: "Ctrl+B",
2650
+ run: (api) => void api.executeCommand("sheet.command.set-range-bold")
2651
+ },
2652
+ {
2653
+ kind: "item",
1681
2654
  id: "italic",
1682
2655
  label: "Italic",
1683
- command: "sheet.command.set-range-italic",
1684
- icon: "format_italic"
2656
+ icon: "format_italic",
2657
+ shortcut: "Ctrl+I",
2658
+ run: (api) => void api.executeCommand("sheet.command.set-range-italic")
1685
2659
  },
1686
2660
  {
2661
+ kind: "item",
1687
2662
  id: "underline",
1688
2663
  label: "Underline",
1689
- command: "sheet.command.set-range-underline",
1690
- icon: "format_underlined"
2664
+ icon: "format_underlined",
2665
+ shortcut: "Ctrl+U",
2666
+ run: (api) => void api.executeCommand("sheet.command.set-range-underline")
1691
2667
  },
1692
2668
  {
2669
+ kind: "item",
1693
2670
  id: "wrap-text",
1694
2671
  label: "Wrap text",
1695
- command: "sheet.command.set-text-wrap",
1696
- icon: "wrap_text"
2672
+ icon: "wrap_text",
2673
+ run: (api) => void api.executeCommand("sheet.command.set-text-wrap", { value: 3 })
2674
+ },
2675
+ { kind: "separator", id: "sep-numfmt" },
2676
+ {
2677
+ kind: "submenu",
2678
+ id: "num-format",
2679
+ label: "Number format",
2680
+ icon: "looks_one",
2681
+ items: NUM_FORMAT_ORDER.map((k) => ({
2682
+ kind: "item",
2683
+ id: `num-${k}`,
2684
+ label: k[0].toUpperCase() + k.slice(1),
2685
+ icon: "looks_one",
2686
+ shortcut: NUM_FORMAT_SHORTCUT[k],
2687
+ run: (api) => setNumberFormatByKey(api, k)
2688
+ }))
1697
2689
  },
1698
2690
  {
2691
+ kind: "item",
2692
+ id: "decimal-up",
2693
+ label: "Increase decimals",
2694
+ icon: "decimal_increase",
2695
+ run: increaseDecimal
2696
+ },
2697
+ {
2698
+ kind: "item",
2699
+ id: "decimal-down",
2700
+ label: "Decrease decimals",
2701
+ icon: "decimal_decrease",
2702
+ run: decreaseDecimal
2703
+ },
2704
+ { kind: "separator", id: "sep-borders" },
2705
+ {
2706
+ kind: "submenu",
2707
+ id: "borders",
2708
+ label: "Borders",
2709
+ icon: "border_all",
2710
+ items: [
2711
+ {
2712
+ kind: "item",
2713
+ id: "border-all",
2714
+ label: "All borders",
2715
+ icon: "border_all",
2716
+ run: (api) => applyBorders(api, "all")
2717
+ },
2718
+ {
2719
+ kind: "item",
2720
+ id: "border-outside",
2721
+ label: "Outside borders",
2722
+ icon: "border_outer",
2723
+ run: (api) => applyBorders(api, "outside")
2724
+ },
2725
+ {
2726
+ kind: "item",
2727
+ id: "border-none",
2728
+ label: "No border",
2729
+ icon: "border_clear",
2730
+ run: (api) => applyBorders(api, "none")
2731
+ }
2732
+ ]
2733
+ },
2734
+ { kind: "separator", id: "sep-cond" },
2735
+ {
2736
+ kind: "item",
2737
+ id: "conditional-formatting",
2738
+ label: "Conditional formatting\u2026",
2739
+ icon: "palette",
2740
+ dialog: "conditional-formatting",
2741
+ feature: "conditionalFormatting"
2742
+ },
2743
+ { kind: "separator", id: "sep-visibility" },
2744
+ {
2745
+ kind: "submenu",
2746
+ id: "visibility",
2747
+ label: "Visibility",
2748
+ icon: "visibility",
2749
+ items: [
2750
+ {
2751
+ kind: "item",
2752
+ id: "hide-row",
2753
+ label: "Hide row",
2754
+ icon: "visibility_off",
2755
+ shortcut: "Ctrl+9",
2756
+ run: hideSelectedRows
2757
+ },
2758
+ {
2759
+ kind: "item",
2760
+ id: "unhide-row",
2761
+ label: "Unhide row",
2762
+ icon: "visibility",
2763
+ shortcut: "Ctrl+Shift+9",
2764
+ run: unhideSelectedRows
2765
+ },
2766
+ {
2767
+ kind: "item",
2768
+ id: "hide-col",
2769
+ label: "Hide column",
2770
+ icon: "visibility_off",
2771
+ shortcut: "Ctrl+0",
2772
+ run: hideSelectedColumns
2773
+ },
2774
+ {
2775
+ kind: "item",
2776
+ id: "unhide-col",
2777
+ label: "Unhide column",
2778
+ icon: "visibility",
2779
+ shortcut: "Ctrl+Shift+0",
2780
+ run: unhideSelectedColumns
2781
+ }
2782
+ ]
2783
+ },
2784
+ { kind: "separator", id: "sep-clear" },
2785
+ {
2786
+ kind: "item",
1699
2787
  id: "clear-format",
1700
2788
  label: "Clear formatting",
1701
- command: "sheet.command.clear-selection-format",
1702
- icon: "format_clear"
2789
+ icon: "format_clear",
2790
+ run: clearFormat
2791
+ },
2792
+ { kind: "separator", id: "sep-delete" },
2793
+ {
2794
+ kind: "item",
2795
+ id: "delete-row",
2796
+ label: "Delete row",
2797
+ icon: "delete_sweep",
2798
+ run: deleteSelectedRow
2799
+ },
2800
+ {
2801
+ kind: "item",
2802
+ id: "delete-col",
2803
+ label: "Delete column",
2804
+ icon: "folder_delete",
2805
+ run: deleteSelectedColumn
1703
2806
  }
1704
2807
  ]
1705
2808
  },
@@ -1708,46 +2811,97 @@ var MENUS = [
1708
2811
  label: "Data",
1709
2812
  items: [
1710
2813
  {
2814
+ kind: "item",
1711
2815
  id: "sort-asc",
1712
2816
  label: "Sort ascending",
1713
- command: "sheet.command.sort-range-asc",
1714
- icon: "arrow_upward"
2817
+ icon: "arrow_upward",
2818
+ run: sortAsc
1715
2819
  },
1716
2820
  {
2821
+ kind: "item",
1717
2822
  id: "sort-desc",
1718
2823
  label: "Sort descending",
1719
- command: "sheet.command.sort-range-desc",
1720
- icon: "arrow_downward"
2824
+ icon: "arrow_downward",
2825
+ run: sortDesc
2826
+ },
2827
+ {
2828
+ kind: "item",
2829
+ id: "sort-custom",
2830
+ label: "Sort range\u2026",
2831
+ icon: "sort",
2832
+ dialog: "custom-sort"
1721
2833
  },
1722
2834
  {
2835
+ kind: "item",
1723
2836
  id: "toggle-filter",
1724
2837
  label: "Toggle filter",
1725
- command: "sheet.command.smart-toggle-filter",
1726
- icon: "filter_alt"
2838
+ icon: "filter_alt",
2839
+ shortcut: "Ctrl+Shift+L",
2840
+ run: toggleFilter,
2841
+ feature: "filter"
2842
+ },
2843
+ { kind: "separator", id: "sep-tools" },
2844
+ {
2845
+ kind: "item",
2846
+ id: "data-validation",
2847
+ label: "Data validation\u2026",
2848
+ icon: "rule",
2849
+ dialog: "data-validation",
2850
+ feature: "dataValidation"
2851
+ },
2852
+ {
2853
+ kind: "item",
2854
+ id: "name-manager",
2855
+ label: "Name Manager\u2026",
2856
+ icon: "bookmark_add",
2857
+ shortcut: "Ctrl+F3",
2858
+ dialog: "name-manager"
2859
+ },
2860
+ {
2861
+ kind: "item",
2862
+ id: "goal-seek",
2863
+ label: "Goal Seek\u2026",
2864
+ icon: "analytics",
2865
+ dialog: "goal-seek"
2866
+ },
2867
+ { kind: "separator", id: "sep-clean" },
2868
+ {
2869
+ kind: "item",
2870
+ id: "text-to-columns",
2871
+ label: "Text to Columns",
2872
+ icon: "splitscreen",
2873
+ run: splitTextToColumns
2874
+ },
2875
+ {
2876
+ kind: "item",
2877
+ id: "recalculate",
2878
+ label: "Recalculate",
2879
+ icon: "autorenew",
2880
+ shortcut: "F9",
2881
+ run: forceRecalculate
1727
2882
  }
1728
2883
  ]
1729
2884
  },
1730
2885
  {
1731
- id: "view",
1732
- label: "View",
2886
+ id: "help",
2887
+ label: "Help",
1733
2888
  items: [
1734
2889
  {
1735
- id: "freeze",
1736
- label: "Freeze panes",
1737
- command: "sheet.command.set-selection-frozen",
1738
- icon: "table_view"
2890
+ kind: "item",
2891
+ id: "keyboard-shortcuts",
2892
+ label: "Keyboard shortcuts",
2893
+ icon: "info",
2894
+ shortcut: "Ctrl+/",
2895
+ dialog: "keyboard-shortcuts"
1739
2896
  },
2897
+ { kind: "separator", id: "sep-help" },
2898
+ { kind: "item", id: "about", label: "About casual sheets", icon: "info", dialog: "about" },
1740
2899
  {
1741
- id: "unfreeze",
1742
- label: "Unfreeze panes",
1743
- command: "sheet.command.cancel-frozen",
1744
- icon: "grid_off"
1745
- },
1746
- {
1747
- id: "toggle-gridlines",
1748
- label: "Toggle gridlines",
1749
- command: "sheet.command.toggle-gridlines",
1750
- icon: "grid_on"
2900
+ kind: "item",
2901
+ id: "github",
2902
+ label: "View on GitHub",
2903
+ icon: "open_in_new",
2904
+ run: () => window.open("https://github.com/CasualOffice/sheets", "_blank")
1751
2905
  }
1752
2906
  ]
1753
2907
  }
@@ -1781,7 +2935,7 @@ var MENU_BTN_STYLE = {
1781
2935
  var DROPDOWN_STYLE = {
1782
2936
  position: "absolute",
1783
2937
  top: "100%",
1784
- minWidth: 200,
2938
+ minWidth: 220,
1785
2939
  marginTop: 2,
1786
2940
  padding: 4,
1787
2941
  display: "flex",
@@ -1808,8 +2962,63 @@ var ITEM_STYLE3 = {
1808
2962
  fontSize: 13,
1809
2963
  textAlign: "left"
1810
2964
  };
1811
- function MenuBar({ api }) {
2965
+ var SEPARATOR_STYLE = {
2966
+ height: 1,
2967
+ margin: "4px 6px",
2968
+ background: "var(--cs-chrome-border, #e6e9ee)"
2969
+ };
2970
+ var SHORTCUT_STYLE = {
2971
+ marginLeft: "auto",
2972
+ paddingLeft: 16,
2973
+ fontSize: 11,
2974
+ color: "var(--cs-chrome-muted, #6b7280)"
2975
+ };
2976
+ var SUBMENU_PANEL_STYLE = {
2977
+ position: "absolute",
2978
+ top: -4,
2979
+ left: "100%",
2980
+ minWidth: 200,
2981
+ marginLeft: 2,
2982
+ padding: 4,
2983
+ display: "flex",
2984
+ flexDirection: "column",
2985
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
2986
+ borderRadius: 8,
2987
+ background: "var(--cs-chrome-input-bg, #fff)",
2988
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)",
2989
+ zIndex: 1001
2990
+ };
2991
+ function featureOn(feature, features) {
2992
+ if (!feature) return true;
2993
+ return features[feature] !== false;
2994
+ }
2995
+ function keepItem(item, features, hasDialogHost) {
2996
+ if (!featureOn(item.feature, features)) return null;
2997
+ if (item.kind === "separator") return item;
2998
+ if (item.kind === "submenu") {
2999
+ const items = filterItems(item.items, features, hasDialogHost);
3000
+ if (items.length === 0) return null;
3001
+ return { ...item, items };
3002
+ }
3003
+ if (item.dialog && !hasDialogHost) return null;
3004
+ return item;
3005
+ }
3006
+ function filterItems(items, features, hasDialogHost) {
3007
+ const kept = items.map((i) => keepItem(i, features, hasDialogHost)).filter((i) => i !== null);
3008
+ const out = [];
3009
+ for (const item of kept) {
3010
+ if (item.kind === "separator") {
3011
+ if (out.length === 0) continue;
3012
+ if (out[out.length - 1].kind === "separator") continue;
3013
+ }
3014
+ out.push(item);
3015
+ }
3016
+ while (out.length > 0 && out[out.length - 1].kind === "separator") out.pop();
3017
+ return out;
3018
+ }
3019
+ function MenuBar({ api, features = {}, onDialogRequest }) {
1812
3020
  const [open, setOpen] = useState8(null);
3021
+ const [openSubmenu, setOpenSubmenu] = useState8(null);
1813
3022
  const rootRef = useRef6(null);
1814
3023
  useEffect8(() => {
1815
3024
  ensureChromeFonts();
@@ -1817,10 +3026,16 @@ function MenuBar({ api }) {
1817
3026
  useEffect8(() => {
1818
3027
  if (open === null) return;
1819
3028
  const onKey = (e) => {
1820
- if (e.key === "Escape") setOpen(null);
3029
+ if (e.key === "Escape") {
3030
+ setOpen(null);
3031
+ setOpenSubmenu(null);
3032
+ }
1821
3033
  };
1822
3034
  const onDown = (e) => {
1823
- if (!rootRef.current?.contains(e.target)) setOpen(null);
3035
+ if (!rootRef.current?.contains(e.target)) {
3036
+ setOpen(null);
3037
+ setOpenSubmenu(null);
3038
+ }
1824
3039
  };
1825
3040
  document.addEventListener("keydown", onKey);
1826
3041
  document.addEventListener("pointerdown", onDown, true);
@@ -1829,10 +3044,90 @@ function MenuBar({ api }) {
1829
3044
  document.removeEventListener("pointerdown", onDown, true);
1830
3045
  };
1831
3046
  }, [open]);
1832
- const run = (item) => {
3047
+ const close = () => {
1833
3048
  setOpen(null);
1834
- void api?.executeCommand(item.command, item.params);
3049
+ setOpenSubmenu(null);
3050
+ };
3051
+ const runItem = (item) => {
3052
+ close();
3053
+ if (!api) return;
3054
+ if (item.dialog) {
3055
+ const seeded = item.dialog === "insert-chart" || item.dialog === "insert-pivot" || item.dialog === "insert-sparkline" || item.dialog === "insert-cells" || item.dialog === "delete-cells";
3056
+ onDialogRequest?.(item.dialog, seeded ? selectionA1(api) : void 0);
3057
+ return;
3058
+ }
3059
+ item.run?.(api);
1835
3060
  };
3061
+ const hasDialogHost = !!onDialogRequest;
3062
+ const visibleMenus = MENUS.map((menu) => ({
3063
+ ...menu,
3064
+ items: filterItems(menu.items, features, hasDialogHost)
3065
+ })).filter((menu) => featureOn(menu.feature, features) && menu.items.length > 0);
3066
+ const renderItems = (items) => items.map((item) => {
3067
+ if (item.kind === "separator") {
3068
+ return /* @__PURE__ */ jsx9("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
3069
+ }
3070
+ if (item.kind === "submenu") {
3071
+ const isSubOpen = openSubmenu === item.id;
3072
+ return /* @__PURE__ */ jsxs8(
3073
+ "div",
3074
+ {
3075
+ style: { position: "relative" },
3076
+ onMouseEnter: () => setOpenSubmenu(item.id),
3077
+ onMouseLeave: () => setOpenSubmenu((cur) => cur === item.id ? null : cur),
3078
+ children: [
3079
+ /* @__PURE__ */ jsxs8(
3080
+ "button",
3081
+ {
3082
+ type: "button",
3083
+ role: "menuitem",
3084
+ "data-testid": `cs-menuitem-${item.id}`,
3085
+ "aria-haspopup": "menu",
3086
+ "aria-expanded": isSubOpen,
3087
+ disabled: !api,
3088
+ style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
3089
+ onMouseDown: (e) => e.preventDefault(),
3090
+ children: [
3091
+ item.icon ? /* @__PURE__ */ jsx9(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx9("span", { style: { width: 18 }, "aria-hidden": true }),
3092
+ /* @__PURE__ */ jsx9("span", { children: item.label }),
3093
+ /* @__PURE__ */ jsx9(Icon, { name: "chevron_right", size: 18, style: { marginLeft: "auto" } })
3094
+ ]
3095
+ }
3096
+ ),
3097
+ isSubOpen && /* @__PURE__ */ jsx9("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
3098
+ ]
3099
+ },
3100
+ item.id
3101
+ );
3102
+ }
3103
+ return /* @__PURE__ */ jsxs8(
3104
+ "button",
3105
+ {
3106
+ type: "button",
3107
+ role: "menuitem",
3108
+ "data-testid": `cs-menuitem-${item.id}`,
3109
+ disabled: !api,
3110
+ style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
3111
+ onMouseDown: (e) => {
3112
+ e.preventDefault();
3113
+ runItem(item);
3114
+ },
3115
+ onMouseEnter: (e) => {
3116
+ setOpenSubmenu(null);
3117
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
3118
+ },
3119
+ onMouseLeave: (e) => {
3120
+ e.currentTarget.style.background = "transparent";
3121
+ },
3122
+ children: [
3123
+ item.icon ? /* @__PURE__ */ jsx9(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx9("span", { style: { width: 18 }, "aria-hidden": true }),
3124
+ /* @__PURE__ */ jsx9("span", { children: item.label }),
3125
+ item.shortcut && /* @__PURE__ */ jsx9("span", { style: SHORTCUT_STYLE, children: fmtShortcut(item.shortcut) })
3126
+ ]
3127
+ },
3128
+ item.id
3129
+ );
3130
+ });
1836
3131
  return /* @__PURE__ */ jsx9(
1837
3132
  "div",
1838
3133
  {
@@ -1841,7 +3136,7 @@ function MenuBar({ api }) {
1841
3136
  "data-testid": "cs-menubar",
1842
3137
  role: "menubar",
1843
3138
  "aria-label": "Menu bar",
1844
- children: MENUS.map((menu) => {
3139
+ children: visibleMenus.map((menu) => {
1845
3140
  const isOpen = open === menu.id;
1846
3141
  return /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
1847
3142
  /* @__PURE__ */ jsx9(
@@ -1849,6 +3144,7 @@ function MenuBar({ api }) {
1849
3144
  {
1850
3145
  type: "button",
1851
3146
  "data-menu": menu.id,
3147
+ "data-testid": `cs-menu-${menu.id}`,
1852
3148
  "aria-haspopup": "menu",
1853
3149
  "aria-expanded": isOpen,
1854
3150
  style: {
@@ -1858,11 +3154,14 @@ function MenuBar({ api }) {
1858
3154
  },
1859
3155
  onMouseDown: (e) => {
1860
3156
  e.preventDefault();
3157
+ setOpenSubmenu(null);
1861
3158
  setOpen((cur) => cur === menu.id ? null : menu.id);
1862
3159
  },
1863
3160
  onMouseEnter: (e) => {
1864
- if (open !== null && open !== menu.id) setOpen(menu.id);
1865
- else if (!isOpen)
3161
+ if (open !== null && open !== menu.id) {
3162
+ setOpen(menu.id);
3163
+ setOpenSubmenu(null);
3164
+ } else if (!isOpen)
1866
3165
  e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1867
3166
  },
1868
3167
  onMouseLeave: (e) => {
@@ -1871,31 +3170,7 @@ function MenuBar({ api }) {
1871
3170
  children: menu.label
1872
3171
  }
1873
3172
  ),
1874
- isOpen && /* @__PURE__ */ jsx9("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: menu.items.map((item) => /* @__PURE__ */ jsxs8(
1875
- "button",
1876
- {
1877
- type: "button",
1878
- role: "menuitem",
1879
- "data-testid": `cs-menuitem-${item.id}`,
1880
- disabled: !api,
1881
- style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
1882
- onMouseDown: (e) => {
1883
- e.preventDefault();
1884
- run(item);
1885
- },
1886
- onMouseEnter: (e) => {
1887
- e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1888
- },
1889
- onMouseLeave: (e) => {
1890
- e.currentTarget.style.background = "transparent";
1891
- },
1892
- children: [
1893
- item.icon ? /* @__PURE__ */ jsx9(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx9("span", { style: { width: 18 }, "aria-hidden": true }),
1894
- /* @__PURE__ */ jsx9("span", { children: item.label })
1895
- ]
1896
- },
1897
- item.id
1898
- )) })
3173
+ isOpen && /* @__PURE__ */ jsx9("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
1899
3174
  ] }, menu.id);
1900
3175
  })
1901
3176
  }
@@ -2520,9 +3795,9 @@ function replaceInString(text, query, replacement, matchCase) {
2520
3795
  }
2521
3796
 
2522
3797
  // src/chrome/ChromeTop.tsx
2523
- import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3798
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2524
3799
  function ChromeTop({ api }) {
2525
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
3800
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
2526
3801
  /* @__PURE__ */ jsx12(MenuBar, { api }),
2527
3802
  /* @__PURE__ */ jsx12(Toolbar, { api }),
2528
3803
  /* @__PURE__ */ jsx12(FormulaBar, { api })
@@ -2530,9 +3805,9 @@ function ChromeTop({ api }) {
2530
3805
  }
2531
3806
 
2532
3807
  // src/chrome/ChromeBottom.tsx
2533
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3808
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2534
3809
  function ChromeBottom({ api }) {
2535
- return /* @__PURE__ */ jsxs12(Fragment3, { children: [
3810
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
2536
3811
  /* @__PURE__ */ jsx13(SheetTabs, { api }),
2537
3812
  /* @__PURE__ */ jsx13(StatusBar, { api }),
2538
3813
  /* @__PURE__ */ jsx13(FindReplace, { api })