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