@rowsncolumns/spreadsheet 10.2.73 → 10.2.74

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.
Files changed (130) hide show
  1. package/dist/cjs/api/evaluate-formula.d.ts +17 -0
  2. package/dist/cjs/api/evaluate-formula.d.ts.map +1 -0
  3. package/dist/cjs/api/evaluate-formula.js +141 -0
  4. package/dist/cjs/api/go-to.d.ts +19 -0
  5. package/dist/cjs/api/go-to.d.ts.map +1 -0
  6. package/dist/cjs/api/go-to.js +49 -0
  7. package/dist/cjs/atoms/evaluate-formula-dialog.atom.d.ts +36 -0
  8. package/dist/cjs/atoms/evaluate-formula-dialog.atom.d.ts.map +1 -0
  9. package/dist/cjs/atoms/evaluate-formula-dialog.atom.js +36 -0
  10. package/dist/cjs/atoms/go-to-dialog.atom.d.ts +36 -0
  11. package/dist/cjs/atoms/go-to-dialog.atom.d.ts.map +1 -0
  12. package/dist/cjs/atoms/go-to-dialog.atom.js +34 -0
  13. package/dist/cjs/atoms/index.d.ts +4 -0
  14. package/dist/cjs/atoms/index.d.ts.map +1 -1
  15. package/dist/cjs/atoms/index.js +4 -0
  16. package/dist/cjs/atoms/paste-special-dialog.atom.d.ts +29 -0
  17. package/dist/cjs/atoms/paste-special-dialog.atom.d.ts.map +1 -0
  18. package/dist/cjs/atoms/paste-special-dialog.atom.js +34 -0
  19. package/dist/cjs/atoms/remove-duplicates-dialog.atom.d.ts +45 -0
  20. package/dist/cjs/atoms/remove-duplicates-dialog.atom.d.ts.map +1 -0
  21. package/dist/cjs/atoms/remove-duplicates-dialog.atom.js +37 -0
  22. package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
  23. package/dist/cjs/components/canvas-grid/index.js +109 -3
  24. package/dist/cjs/components/canvas-grid/typings.d.ts +54 -8
  25. package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
  26. package/dist/cjs/components/context-menu/index.d.ts +16 -3
  27. package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
  28. package/dist/cjs/components/context-menu/index.js +112 -5
  29. package/dist/cjs/components/context-menu/total-row-functions.d.ts +32 -0
  30. package/dist/cjs/components/context-menu/total-row-functions.d.ts.map +1 -0
  31. package/dist/cjs/components/context-menu/total-row-functions.js +62 -0
  32. package/dist/cjs/components/evaluate-formula-dialog/index.d.ts +34 -0
  33. package/dist/cjs/components/evaluate-formula-dialog/index.d.ts.map +1 -0
  34. package/dist/cjs/components/evaluate-formula-dialog/index.js +106 -0
  35. package/dist/cjs/components/go-to-dialog/index.d.ts +35 -0
  36. package/dist/cjs/components/go-to-dialog/index.d.ts.map +1 -0
  37. package/dist/cjs/components/go-to-dialog/index.js +93 -0
  38. package/dist/cjs/components/paste-special-dialog/index.d.ts +29 -0
  39. package/dist/cjs/components/paste-special-dialog/index.d.ts.map +1 -0
  40. package/dist/cjs/components/paste-special-dialog/index.js +99 -0
  41. package/dist/cjs/components/remove-duplicates-dialog/index.d.ts +37 -0
  42. package/dist/cjs/components/remove-duplicates-dialog/index.d.ts.map +1 -0
  43. package/dist/cjs/components/remove-duplicates-dialog/index.js +91 -0
  44. package/dist/cjs/components/toolbar/index.d.ts +1 -0
  45. package/dist/cjs/components/toolbar/index.d.ts.map +1 -1
  46. package/dist/cjs/components/toolbar/index.js +1 -0
  47. package/dist/cjs/components/toolbar/text-rotation-selector.d.ts +29 -0
  48. package/dist/cjs/components/toolbar/text-rotation-selector.d.ts.map +1 -0
  49. package/dist/cjs/components/toolbar/text-rotation-selector.js +78 -0
  50. package/dist/cjs/components/toolbar/text-wrap-selector.d.ts +7 -1
  51. package/dist/cjs/components/toolbar/text-wrap-selector.d.ts.map +1 -1
  52. package/dist/cjs/components/toolbar/text-wrap-selector.js +10 -3
  53. package/dist/cjs/hooks/use-copy-paste-cut.d.ts +20 -1
  54. package/dist/cjs/hooks/use-copy-paste-cut.d.ts.map +1 -1
  55. package/dist/cjs/hooks/use-copy-paste-cut.js +25 -20
  56. package/dist/cjs/hooks/use-keyboard-handler.d.ts +17 -2
  57. package/dist/cjs/hooks/use-keyboard-handler.d.ts.map +1 -1
  58. package/dist/cjs/hooks/use-keyboard-handler.js +182 -19
  59. package/dist/cjs/index.d.ts +6 -1
  60. package/dist/cjs/index.d.ts.map +1 -1
  61. package/dist/cjs/index.js +7 -1
  62. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  63. package/dist/cjs/types.d.ts +173 -1
  64. package/dist/cjs/types.d.ts.map +1 -1
  65. package/dist/esm/api/evaluate-formula.d.ts +17 -0
  66. package/dist/esm/api/evaluate-formula.d.ts.map +1 -0
  67. package/dist/esm/api/evaluate-formula.js +109 -0
  68. package/dist/esm/api/go-to.d.ts +19 -0
  69. package/dist/esm/api/go-to.d.ts.map +1 -0
  70. package/dist/esm/api/go-to.js +41 -0
  71. package/dist/esm/atoms/evaluate-formula-dialog.atom.d.ts +36 -0
  72. package/dist/esm/atoms/evaluate-formula-dialog.atom.d.ts.map +1 -0
  73. package/dist/esm/atoms/evaluate-formula-dialog.atom.js +25 -0
  74. package/dist/esm/atoms/go-to-dialog.atom.d.ts +36 -0
  75. package/dist/esm/atoms/go-to-dialog.atom.d.ts.map +1 -0
  76. package/dist/esm/atoms/go-to-dialog.atom.js +25 -0
  77. package/dist/esm/atoms/index.d.ts +4 -0
  78. package/dist/esm/atoms/index.d.ts.map +1 -1
  79. package/dist/esm/atoms/index.js +4 -0
  80. package/dist/esm/atoms/paste-special-dialog.atom.d.ts +29 -0
  81. package/dist/esm/atoms/paste-special-dialog.atom.d.ts.map +1 -0
  82. package/dist/esm/atoms/paste-special-dialog.atom.js +25 -0
  83. package/dist/esm/atoms/remove-duplicates-dialog.atom.d.ts +45 -0
  84. package/dist/esm/atoms/remove-duplicates-dialog.atom.d.ts.map +1 -0
  85. package/dist/esm/atoms/remove-duplicates-dialog.atom.js +26 -0
  86. package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
  87. package/dist/esm/components/canvas-grid/index.js +107 -3
  88. package/dist/esm/components/canvas-grid/typings.d.ts +54 -8
  89. package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
  90. package/dist/esm/components/context-menu/index.d.ts +16 -3
  91. package/dist/esm/components/context-menu/index.d.ts.map +1 -1
  92. package/dist/esm/components/context-menu/index.js +114 -7
  93. package/dist/esm/components/context-menu/total-row-functions.d.ts +32 -0
  94. package/dist/esm/components/context-menu/total-row-functions.d.ts.map +1 -0
  95. package/dist/esm/components/context-menu/total-row-functions.js +50 -0
  96. package/dist/esm/components/evaluate-formula-dialog/index.d.ts +34 -0
  97. package/dist/esm/components/evaluate-formula-dialog/index.d.ts.map +1 -0
  98. package/dist/esm/components/evaluate-formula-dialog/index.js +86 -0
  99. package/dist/esm/components/go-to-dialog/index.d.ts +35 -0
  100. package/dist/esm/components/go-to-dialog/index.d.ts.map +1 -0
  101. package/dist/esm/components/go-to-dialog/index.js +84 -0
  102. package/dist/esm/components/paste-special-dialog/index.d.ts +29 -0
  103. package/dist/esm/components/paste-special-dialog/index.d.ts.map +1 -0
  104. package/dist/esm/components/paste-special-dialog/index.js +90 -0
  105. package/dist/esm/components/remove-duplicates-dialog/index.d.ts +37 -0
  106. package/dist/esm/components/remove-duplicates-dialog/index.d.ts.map +1 -0
  107. package/dist/esm/components/remove-duplicates-dialog/index.js +82 -0
  108. package/dist/esm/components/toolbar/index.d.ts +1 -0
  109. package/dist/esm/components/toolbar/index.d.ts.map +1 -1
  110. package/dist/esm/components/toolbar/index.js +1 -0
  111. package/dist/esm/components/toolbar/text-rotation-selector.d.ts +29 -0
  112. package/dist/esm/components/toolbar/text-rotation-selector.d.ts.map +1 -0
  113. package/dist/esm/components/toolbar/text-rotation-selector.js +71 -0
  114. package/dist/esm/components/toolbar/text-wrap-selector.d.ts +7 -1
  115. package/dist/esm/components/toolbar/text-wrap-selector.d.ts.map +1 -1
  116. package/dist/esm/components/toolbar/text-wrap-selector.js +10 -3
  117. package/dist/esm/hooks/use-copy-paste-cut.d.ts +20 -1
  118. package/dist/esm/hooks/use-copy-paste-cut.d.ts.map +1 -1
  119. package/dist/esm/hooks/use-copy-paste-cut.js +8 -3
  120. package/dist/esm/hooks/use-keyboard-handler.d.ts +17 -2
  121. package/dist/esm/hooks/use-keyboard-handler.d.ts.map +1 -1
  122. package/dist/esm/hooks/use-keyboard-handler.js +169 -19
  123. package/dist/esm/index.d.ts +6 -1
  124. package/dist/esm/index.d.ts.map +1 -1
  125. package/dist/esm/index.js +5 -0
  126. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  127. package/dist/esm/types.d.ts +173 -1
  128. package/dist/esm/types.d.ts.map +1 -1
  129. package/dist/spreadsheet.min.css +1 -1
  130. package/package.json +5 -5
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PasteSpecialDialogHost = exports.PasteSpecialDialog = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var ui_1 = require("@rowsncolumns/ui");
6
+ var react_1 = tslib_1.__importStar(require("react"));
7
+ var paste_special_dialog_atom_1 = require("../../atoms/paste-special-dialog.atom");
8
+ var PASTE_MODES = [
9
+ { value: "all", label: "All" },
10
+ { value: "formulas", label: "Formulas" },
11
+ { value: "values", label: "Values" },
12
+ { value: "formats", label: "Formats" },
13
+ ];
14
+ var OPERATIONS = [
15
+ { value: "none", label: "None" },
16
+ { value: "add", label: "Add" },
17
+ { value: "subtract", label: "Subtract" },
18
+ { value: "multiply", label: "Multiply" },
19
+ { value: "divide", label: "Divide" },
20
+ ];
21
+ var RadioGroup = function (_a) {
22
+ var name = _a.name, legend = _a.legend, value = _a.value, options = _a.options, onChange = _a.onChange, disabled = _a.disabled;
23
+ return (react_1.default.createElement("fieldset", { className: "flex flex-col gap-1 min-w-0 disabled:opacity-50", disabled: disabled },
24
+ react_1.default.createElement("legend", { className: "text-xs font-medium mb-1" }, legend),
25
+ options.map(function (option) { return (react_1.default.createElement("label", { key: option.value, className: "flex items-center gap-2 text-xs py-0.5 cursor-pointer" },
26
+ react_1.default.createElement("input", { type: "radio", name: name, value: option.value, checked: value === option.value, disabled: disabled, onChange: function () { return onChange(option.value); } }),
27
+ option.label)); })));
28
+ };
29
+ /**
30
+ * Excel's Paste Special dialog: what to paste (all / formulas / values /
31
+ * formats), an arithmetic operation against the destination, skip blanks and
32
+ * transpose. Every choice composes into one `PasteSpecialOptions` request.
33
+ */
34
+ exports.PasteSpecialDialog = (0, react_1.memo)(function (_a) {
35
+ var open = _a.open, onOpenChange = _a.onOpenChange, onApply = _a.onApply;
36
+ var _b = tslib_1.__read((0, react_1.useState)("all"), 2), paste = _b[0], setPaste = _b[1];
37
+ var _c = tslib_1.__read((0, react_1.useState)("none"), 2), operation = _c[0], setOperation = _c[1];
38
+ var _d = tslib_1.__read((0, react_1.useState)(false), 2), skipBlanks = _d[0], setSkipBlanks = _d[1];
39
+ var _e = tslib_1.__read((0, react_1.useState)(false), 2), transpose = _e[0], setTranspose = _e[1];
40
+ // Excel greys Operation out under Paste → Formats (a formats paste never
41
+ // changes destination values); the choice is kept but not applied.
42
+ var operationDisabled = paste === "formats";
43
+ var commit = (0, react_1.useCallback)(function () {
44
+ onApply({
45
+ paste: paste,
46
+ operation: operation === "none" || operationDisabled ? undefined : operation,
47
+ skipBlanks: skipBlanks,
48
+ transpose: transpose,
49
+ });
50
+ onOpenChange(false);
51
+ }, [
52
+ onApply,
53
+ onOpenChange,
54
+ paste,
55
+ operation,
56
+ operationDisabled,
57
+ skipBlanks,
58
+ transpose,
59
+ ]);
60
+ return (react_1.default.createElement(ui_1.Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
61
+ react_1.default.createElement(ui_1.DialogContent, { className: "w-[380px] max-w-[92vw] pointer-events-auto" },
62
+ react_1.default.createElement(ui_1.DialogTitle, null, "Paste Special"),
63
+ react_1.default.createElement(ui_1.DialogCloseButton, null),
64
+ react_1.default.createElement(ui_1.DialogBody, { className: "flex flex-col gap-4" },
65
+ react_1.default.createElement("div", { className: "grid grid-cols-2 gap-4" },
66
+ react_1.default.createElement(RadioGroup, { name: "paste-special-paste", legend: "Paste", value: paste, options: PASTE_MODES, onChange: setPaste }),
67
+ react_1.default.createElement(RadioGroup, { name: "paste-special-operation", legend: "Operation", value: operation, options: OPERATIONS, onChange: setOperation, disabled: operationDisabled })),
68
+ react_1.default.createElement("div", { className: "flex flex-col gap-1" },
69
+ react_1.default.createElement("label", { className: "flex items-center gap-2 text-xs py-0.5 cursor-pointer" },
70
+ react_1.default.createElement("input", { type: "checkbox", checked: skipBlanks, onChange: function (e) { return setSkipBlanks(e.target.checked); } }),
71
+ "Skip blanks"),
72
+ react_1.default.createElement("label", { className: "flex items-center gap-2 text-xs py-0.5 cursor-pointer" },
73
+ react_1.default.createElement("input", { type: "checkbox", checked: transpose, onChange: function (e) { return setTranspose(e.target.checked); } }),
74
+ "Transpose")),
75
+ react_1.default.createElement(ui_1.Separator, null),
76
+ react_1.default.createElement("div", { className: "flex justify-end gap-2" },
77
+ react_1.default.createElement(ui_1.Button, { variant: "outline", size: "sm", onClick: function () { return onOpenChange(false); } }, "Cancel"),
78
+ react_1.default.createElement(ui_1.Button, { size: "sm", onClick: commit }, "OK"))))));
79
+ });
80
+ exports.PasteSpecialDialog.displayName = "PasteSpecialDialog";
81
+ /**
82
+ * Host-mounted, atom-driven wrapper around {@link PasteSpecialDialog}. Mount
83
+ * it once in the host alongside the other editor dialogs; the grid opens it
84
+ * from the context menu ("Paste special…") and `Ctrl+Alt+V`. Remounted per
85
+ * request so each opening starts from Excel's defaults.
86
+ */
87
+ var PasteSpecialDialogHost = function () {
88
+ (0, paste_special_dialog_atom_1.useRegisterPasteSpecialDialogHost)();
89
+ var _a = tslib_1.__read((0, paste_special_dialog_atom_1.usePasteSpecialDialogState)(), 2), request = _a[0], setRequest = _a[1];
90
+ var close = (0, react_1.useCallback)(function () { return setRequest(null); }, [setRequest]);
91
+ if (!request)
92
+ return null;
93
+ return (react_1.default.createElement(exports.PasteSpecialDialog, { open: true, onOpenChange: function (open) {
94
+ if (!open)
95
+ close();
96
+ }, onApply: request.onApply }));
97
+ };
98
+ exports.PasteSpecialDialogHost = PasteSpecialDialogHost;
99
+ exports.PasteSpecialDialogHost.displayName = "PasteSpecialDialogHost";
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import { type RemoveDuplicatesDialogColumn } from "../../atoms/remove-duplicates-dialog.atom";
3
+ import type { RemoveDuplicatesOptions } from "../../types";
4
+ export type { RemoveDuplicatesDialogColumn };
5
+ export type RemoveDuplicatesDialogProps = {
6
+ /** Controls visibility. */
7
+ open: boolean;
8
+ onOpenChange(open: boolean): void;
9
+ /** Columns of the range, in sheet order. All start checked. */
10
+ columns: RemoveDuplicatesDialogColumn[];
11
+ /** Initial state of "My data has headers". */
12
+ initialHasHeader?: boolean;
13
+ /**
14
+ * Emits the chosen compare columns and header flag. The host routes these
15
+ * to `onRemoveDuplicates`; the dialog never touches state directly.
16
+ */
17
+ onApply(options: RemoveDuplicatesOptions): void;
18
+ };
19
+ /**
20
+ * Excel's "Remove Duplicates" dialog: a "My data has headers" toggle plus a
21
+ * checklist of the columns that decide whether two rows are duplicates.
22
+ * Unchecked columns still travel with their row — only the comparison
23
+ * narrows.
24
+ */
25
+ export declare const RemoveDuplicatesDialog: React.MemoExoticComponent<({ open, onOpenChange, columns, initialHasHeader, onApply, }: RemoveDuplicatesDialogProps) => React.JSX.Element>;
26
+ /**
27
+ * Host-mounted, atom-driven wrapper around {@link RemoveDuplicatesDialog}.
28
+ * Mount this once in the host (alongside the other editor dialogs); the
29
+ * context menu opens it via `useOpenRemoveDuplicatesDialog`. While no host is
30
+ * mounted the context menu keeps its one-click de-dupe, so adopting the
31
+ * dialog is opt-in per host.
32
+ */
33
+ export declare const RemoveDuplicatesDialogHost: {
34
+ (): React.JSX.Element;
35
+ displayName: string;
36
+ };
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/remove-duplicates-dialog/index.tsx"],"names":[],"mappings":"AAWA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAE/E,OAAO,EACL,KAAK,4BAA4B,EAGlC,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,YAAY,EAAE,4BAA4B,EAAE,CAAC;AAE7C,MAAM,MAAM,2BAA2B,GAAG;IACxC,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,+DAA+D;IAC/D,OAAO,EAAE,4BAA4B,EAAE,CAAC;IACxC,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,0FAO9B,2BAA2B,uBAqI/B,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B;;;CAetC,CAAC"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RemoveDuplicatesDialogHost = exports.RemoveDuplicatesDialog = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var icons_1 = require("@rowsncolumns/icons");
6
+ var ui_1 = require("@rowsncolumns/ui");
7
+ var react_1 = tslib_1.__importStar(require("react"));
8
+ var remove_duplicates_dialog_atom_1 = require("../../atoms/remove-duplicates-dialog.atom");
9
+ /**
10
+ * Excel's "Remove Duplicates" dialog: a "My data has headers" toggle plus a
11
+ * checklist of the columns that decide whether two rows are duplicates.
12
+ * Unchecked columns still travel with their row — only the comparison
13
+ * narrows.
14
+ */
15
+ exports.RemoveDuplicatesDialog = (0, react_1.memo)(function (_a) {
16
+ var open = _a.open, onOpenChange = _a.onOpenChange, columns = _a.columns, _b = _a.initialHasHeader, initialHasHeader = _b === void 0 ? false : _b, onApply = _a.onApply;
17
+ var allColumnIndexes = (0, react_1.useMemo)(function () { return columns.map(function (column) { return column.columnIndex; }); }, [columns]);
18
+ var _c = tslib_1.__read((0, react_1.useState)(initialHasHeader), 2), hasHeader = _c[0], setHasHeader = _c[1];
19
+ var _d = tslib_1.__read((0, react_1.useState)(function () { return new Set(allColumnIndexes); }), 2), checked = _d[0], setChecked = _d[1];
20
+ // Re-seed when reopened for a different range.
21
+ (0, react_1.useEffect)(function () {
22
+ if (open) {
23
+ setHasHeader(initialHasHeader);
24
+ setChecked(new Set(allColumnIndexes));
25
+ }
26
+ // Only reset on open transitions.
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, [open]);
29
+ var toggleColumn = (0, react_1.useCallback)(function (columnIndex) {
30
+ setChecked(function (prev) {
31
+ var next = new Set(prev);
32
+ if (next.has(columnIndex)) {
33
+ next.delete(columnIndex);
34
+ }
35
+ else {
36
+ next.add(columnIndex);
37
+ }
38
+ return next;
39
+ });
40
+ }, []);
41
+ var commit = (0, react_1.useCallback)(function () {
42
+ var columnIndices = allColumnIndexes.filter(function (columnIndex) {
43
+ return checked.has(columnIndex);
44
+ });
45
+ onApply({ columnIndices: columnIndices, hasHeader: hasHeader });
46
+ onOpenChange(false);
47
+ }, [allColumnIndexes, checked, hasHeader, onApply, onOpenChange]);
48
+ return (react_1.default.createElement(ui_1.Dialog, { open: open, onOpenChange: onOpenChange },
49
+ react_1.default.createElement(ui_1.DialogContent, { className: "w-[420px] max-w-[92vw] pointer-events-auto" },
50
+ react_1.default.createElement(ui_1.DialogTitle, null, "Remove duplicates"),
51
+ react_1.default.createElement(ui_1.DialogDescription, null, "Select the columns that contain duplicate values. Rows that match in every selected column are removed, keeping the first one."),
52
+ react_1.default.createElement(ui_1.DialogCloseButton, { onClick: function () { return onOpenChange(false); } },
53
+ react_1.default.createElement(icons_1.Cross1Icon, null)),
54
+ react_1.default.createElement(ui_1.DialogBody, { className: "flex flex-col gap-3" },
55
+ react_1.default.createElement("label", { className: "font-system text-sm flex items-center gap-1" },
56
+ react_1.default.createElement("input", { type: "checkbox", name: "hasHeader", checked: hasHeader, onChange: function (e) { return setHasHeader(e.target.checked); } }),
57
+ "My data has headers"),
58
+ react_1.default.createElement("div", { className: "flex gap-2" },
59
+ react_1.default.createElement(ui_1.Button, { variant: "outline", size: "xs", type: "button", onClick: function () { return setChecked(new Set(allColumnIndexes)); } }, "Select all"),
60
+ react_1.default.createElement(ui_1.Button, { variant: "outline", size: "xs", type: "button", onClick: function () { return setChecked(new Set()); } }, "Unselect all")),
61
+ react_1.default.createElement("div", { className: "flex flex-col gap-1 max-h-[240px] overflow-y-auto border border-rnc-border rounded p-2", role: "group", "aria-label": "Columns" }, columns.map(function (column) {
62
+ var text = hasHeader && column.header ? column.header : column.label;
63
+ return (react_1.default.createElement("label", { key: column.columnIndex, className: "font-system text-sm flex items-center gap-1" },
64
+ react_1.default.createElement("input", { type: "checkbox", name: "column-".concat(column.columnIndex), checked: checked.has(column.columnIndex), onChange: function () { return toggleColumn(column.columnIndex); } }),
65
+ react_1.default.createElement("span", { className: "truncate", title: text }, text)));
66
+ })),
67
+ react_1.default.createElement(ui_1.Separator, null),
68
+ react_1.default.createElement("div", { className: "flex justify-end gap-2" },
69
+ react_1.default.createElement(ui_1.Button, { variant: "outline", size: "sm", type: "button", onClick: function () { return onOpenChange(false); } }, "Cancel"),
70
+ react_1.default.createElement(ui_1.Button, { size: "sm", type: "button", onClick: commit, disabled: checked.size === 0 }, "OK"))))));
71
+ });
72
+ exports.RemoveDuplicatesDialog.displayName = "RemoveDuplicatesDialog";
73
+ /**
74
+ * Host-mounted, atom-driven wrapper around {@link RemoveDuplicatesDialog}.
75
+ * Mount this once in the host (alongside the other editor dialogs); the
76
+ * context menu opens it via `useOpenRemoveDuplicatesDialog`. While no host is
77
+ * mounted the context menu keeps its one-click de-dupe, so adopting the
78
+ * dialog is opt-in per host.
79
+ */
80
+ var RemoveDuplicatesDialogHost = function () {
81
+ var _a;
82
+ (0, remove_duplicates_dialog_atom_1.useRegisterRemoveDuplicatesDialogHost)();
83
+ var _b = tslib_1.__read((0, remove_duplicates_dialog_atom_1.useRemoveDuplicatesDialogState)(), 2), request = _b[0], setRequest = _b[1];
84
+ var close = (0, react_1.useCallback)(function () { return setRequest(null); }, [setRequest]);
85
+ return (react_1.default.createElement(exports.RemoveDuplicatesDialog, { open: request != null, onOpenChange: function (open) {
86
+ if (!open)
87
+ close();
88
+ }, columns: (_a = request === null || request === void 0 ? void 0 : request.columns) !== null && _a !== void 0 ? _a : [], initialHasHeader: request === null || request === void 0 ? void 0 : request.hasHeader, onApply: function (options) { return request === null || request === void 0 ? void 0 : request.onApply(options); } }));
89
+ };
90
+ exports.RemoveDuplicatesDialogHost = RemoveDuplicatesDialogHost;
91
+ exports.RemoveDuplicatesDialogHost.displayName = "RemoveDuplicatesDialogHost";
@@ -10,6 +10,7 @@ export * from "./font-family-selector";
10
10
  export * from "./text-horizontal-align-selector";
11
11
  export * from "./text-vertical-align-selector";
12
12
  export * from "./text-wrap-selector";
13
+ export * from "./text-rotation-selector";
13
14
  export * from "./text-format-selector";
14
15
  export * from "./theme-selector";
15
16
  export * from "./table-actions";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/index.tsx"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/index.tsx"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
@@ -13,6 +13,7 @@ tslib_1.__exportStar(require("./font-family-selector"), exports);
13
13
  tslib_1.__exportStar(require("./text-horizontal-align-selector"), exports);
14
14
  tslib_1.__exportStar(require("./text-vertical-align-selector"), exports);
15
15
  tslib_1.__exportStar(require("./text-wrap-selector"), exports);
16
+ tslib_1.__exportStar(require("./text-rotation-selector"), exports);
16
17
  tslib_1.__exportStar(require("./text-format-selector"), exports);
17
18
  tslib_1.__exportStar(require("./theme-selector"), exports);
18
19
  tslib_1.__exportStar(require("./table-actions"), exports);
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import type { CellFormat } from "../../types";
3
+ export type TextRotationValue = NonNullable<CellFormat["textRotation"]>;
4
+ type TextRotationPreset = {
5
+ label: string;
6
+ value: TextRotationValue;
7
+ };
8
+ /**
9
+ * Excel's Orientation menu. Degrees are counter-clockwise positive (Excel's
10
+ * convention, what the renderer and the xlsx round-trip use), so "clockwise"
11
+ * presets are negative. `0` is horizontal — used instead of clearing the
12
+ * property so both engines apply it as a plain format write.
13
+ */
14
+ export declare const TEXT_ROTATION_PRESETS: TextRotationPreset[];
15
+ export declare const MIN_TEXT_ROTATION = -90;
16
+ export declare const MAX_TEXT_ROTATION = 90;
17
+ /**
18
+ * Coerce free-form degree input into the supported -90…90 range. Returns
19
+ * `undefined` for input that is not a finite number.
20
+ */
21
+ export declare const clampTextRotation: (input: string | number) => number | undefined;
22
+ type TextRotationSelectorProps = {
23
+ disabled?: boolean;
24
+ value?: CellFormat["textRotation"];
25
+ onChange(value: TextRotationValue): void;
26
+ };
27
+ export declare const TextRotationSelector: ({ value, onChange, disabled, }: TextRotationSelectorProps) => React.JSX.Element;
28
+ export {};
29
+ //# sourceMappingURL=text-rotation-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-rotation-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-rotation-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;AAExE,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,iBAAiB,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,EAOrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,GAAG,MAAM,uBAMvD,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,gCAIlC,yBAAyB,sBAoF3B,CAAC"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TextRotationSelector = exports.clampTextRotation = exports.MAX_TEXT_ROTATION = exports.MIN_TEXT_ROTATION = exports.TEXT_ROTATION_PRESETS = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var icons_1 = require("@rowsncolumns/icons");
6
+ var ui_1 = require("@rowsncolumns/ui");
7
+ var react_1 = tslib_1.__importStar(require("react"));
8
+ var atoms_1 = require("../../atoms");
9
+ /**
10
+ * Excel's Orientation menu. Degrees are counter-clockwise positive (Excel's
11
+ * convention, what the renderer and the xlsx round-trip use), so "clockwise"
12
+ * presets are negative. `0` is horizontal — used instead of clearing the
13
+ * property so both engines apply it as a plain format write.
14
+ */
15
+ exports.TEXT_ROTATION_PRESETS = [
16
+ { label: "Horizontal", value: 0 },
17
+ { label: "Angle counterclockwise", value: 45 },
18
+ { label: "Angle clockwise", value: -45 },
19
+ { label: "Vertical text", value: "vertical" },
20
+ { label: "Rotate text up", value: 90 },
21
+ { label: "Rotate text down", value: -90 },
22
+ ];
23
+ exports.MIN_TEXT_ROTATION = -90;
24
+ exports.MAX_TEXT_ROTATION = 90;
25
+ /**
26
+ * Coerce free-form degree input into the supported -90…90 range. Returns
27
+ * `undefined` for input that is not a finite number.
28
+ */
29
+ var clampTextRotation = function (input) {
30
+ var value = typeof input === "number" ? input : Number(input);
31
+ if (!Number.isFinite(value)) {
32
+ return undefined;
33
+ }
34
+ return Math.max(exports.MIN_TEXT_ROTATION, Math.min(exports.MAX_TEXT_ROTATION, value));
35
+ };
36
+ exports.clampTextRotation = clampTextRotation;
37
+ var TextRotationSelector = function (_a) {
38
+ var value = _a.value, onChange = _a.onChange, disabled = _a.disabled;
39
+ var focusSheet = (0, atoms_1.useFocusSheet)();
40
+ var _b = tslib_1.__read((0, react_1.useState)(false), 2), isOpen = _b[0], onOpenChange = _b[1];
41
+ var current = value !== null && value !== void 0 ? value : 0;
42
+ var _c = tslib_1.__read((0, react_1.useState)(typeof current === "number" ? String(current) : ""), 2), degrees = _c[0], setDegrees = _c[1];
43
+ // Reflect the active cell's rotation in the custom-degrees field whenever
44
+ // the popover opens on a different value.
45
+ (0, react_1.useEffect)(function () {
46
+ if (isOpen) {
47
+ setDegrees(typeof current === "number" ? String(current) : "");
48
+ }
49
+ }, [isOpen, current]);
50
+ var close = function () { return onOpenChange(false); };
51
+ var apply = function (next) {
52
+ onChange(next);
53
+ focusSheet === null || focusSheet === void 0 ? void 0 : focusSheet();
54
+ close();
55
+ };
56
+ var applyDegrees = function () {
57
+ var next = (0, exports.clampTextRotation)(degrees);
58
+ if (next !== undefined) {
59
+ apply(next);
60
+ }
61
+ };
62
+ return (react_1.default.createElement(ui_1.Popover, { open: isOpen, onOpenChange: onOpenChange },
63
+ react_1.default.createElement(ui_1.PopoverTrigger, { asChild: true },
64
+ react_1.default.createElement(ui_1.IconButton, { size: "lg", tooltip: "Text orientation", "aria-label": "Text orientation", disabled: disabled },
65
+ react_1.default.createElement(icons_1.RotateCounterClockwiseIcon, null),
66
+ react_1.default.createElement(icons_1.MdOutlineArrowDropDown, null))),
67
+ react_1.default.createElement(ui_1.PopoverContent, { align: "start" },
68
+ react_1.default.createElement(ui_1.Stack, { direction: "vertical", className: "gap-1 min-w-[200px]" },
69
+ exports.TEXT_ROTATION_PRESETS.map(function (preset) { return (react_1.default.createElement(ui_1.Button, { key: preset.label, size: "xs", variant: current === preset.value ? "primary" : "ghost", className: "justify-start", "aria-pressed": current === preset.value, onClick: function () { return apply(preset.value); } }, preset.label)); }),
70
+ react_1.default.createElement("form", { className: "flex items-center gap-1 pt-1 m-0", onSubmit: function (e) {
71
+ e.preventDefault();
72
+ applyDegrees();
73
+ } },
74
+ react_1.default.createElement("label", { className: "font-system text-xs shrink-0", htmlFor: "rnc-text-rotation-degrees" }, "Degrees"),
75
+ react_1.default.createElement(ui_1.Input, { id: "rnc-text-rotation-degrees", type: "number", min: exports.MIN_TEXT_ROTATION, max: exports.MAX_TEXT_ROTATION, step: 1, value: degrees, onChange: function (e) { return setDegrees(e.target.value); }, className: "w-[72px]" }),
76
+ react_1.default.createElement(ui_1.Button, { size: "xs", type: "submit" }, "Apply"))))));
77
+ };
78
+ exports.TextRotationSelector = TextRotationSelector;
@@ -3,7 +3,13 @@ import type { WrapStrategy } from "../../types";
3
3
  type TextWrapSelectorProps = {
4
4
  value?: WrapStrategy | null;
5
5
  onChange(value: WrapStrategy): void;
6
+ /**
7
+ * Excel's "Shrink to fit": scale the font down so the text fits the cell.
8
+ * The toggle only renders when `onChangeShrinkToFit` is provided.
9
+ */
10
+ shrinkToFit?: boolean | null;
11
+ onChangeShrinkToFit?(value: boolean): void;
6
12
  };
7
- export declare const TextWrapSelector: ({ value, onChange, }: TextWrapSelectorProps) => React.JSX.Element;
13
+ export declare const TextWrapSelector: ({ value, onChange, shrinkToFit, onChangeShrinkToFit, }: TextWrapSelectorProps) => React.JSX.Element;
8
14
  export {};
9
15
  //# sourceMappingURL=text-wrap-selector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-wrap-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-wrap-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,qBAAqB,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,sBAG9B,qBAAqB,sBA8DvB,CAAC"}
1
+ {"version":3,"file":"text-wrap-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-wrap-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,qBAAqB,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,mBAAmB,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,wDAK9B,qBAAqB,sBAoFvB,CAAC"}
@@ -8,13 +8,13 @@ var ui_1 = require("@rowsncolumns/ui");
8
8
  var react_1 = tslib_1.__importStar(require("react"));
9
9
  var atoms_1 = require("../../atoms");
10
10
  var TextWrapSelector = function (_a) {
11
- var _b = _a.value, value = _b === void 0 ? "overflow" : _b, onChange = _a.onChange;
11
+ var _b = _a.value, value = _b === void 0 ? "overflow" : _b, onChange = _a.onChange, shrinkToFit = _a.shrinkToFit, onChangeShrinkToFit = _a.onChangeShrinkToFit;
12
12
  var focusSheet = (0, atoms_1.useFocusSheet)();
13
13
  var _c = tslib_1.__read((0, react_1.useState)(false), 2), isOpen = _c[0], onOpenChange = _c[1];
14
14
  var close = function () { return onOpenChange(false); };
15
15
  return (react_1.default.createElement(ui_1.Popover, { open: isOpen, onOpenChange: onOpenChange },
16
16
  react_1.default.createElement(ui_1.PopoverTrigger, { asChild: true },
17
- react_1.default.createElement(ui_1.IconButton, { size: "lg", tooltip: "Text wrapping" },
17
+ react_1.default.createElement(ui_1.IconButton, { size: "lg", tooltip: "Text wrapping", "aria-label": "Text wrapping" },
18
18
  value === "overflow" ? (react_1.default.createElement(icons_1.TextOverflowIcon, null)) : value === "wrap" ? (react_1.default.createElement(icons_1.TextOverflowWrapIcon, null)) : (react_1.default.createElement(icons_1.TextoverflowClipIcon, null)),
19
19
  react_1.default.createElement(icons_2.MdOutlineArrowDropDown, null))),
20
20
  react_1.default.createElement(ui_1.PopoverContent, { align: "start" },
@@ -36,6 +36,13 @@ var TextWrapSelector = function (_a) {
36
36
  focusSheet === null || focusSheet === void 0 ? void 0 : focusSheet();
37
37
  close();
38
38
  } },
39
- react_1.default.createElement(icons_1.TextoverflowClipIcon, null))))));
39
+ react_1.default.createElement(icons_1.TextoverflowClipIcon, null)),
40
+ onChangeShrinkToFit ? (react_1.default.createElement(ui_1.IconButton, { size: "lg", isActive: !!shrinkToFit, "aria-pressed": !!shrinkToFit, tooltip: "Shrink text to fit the cell", "aria-label": "Shrink text to fit the cell", className: "w-full justify-start gap-1", onClick: function () {
41
+ onChangeShrinkToFit(!shrinkToFit);
42
+ focusSheet === null || focusSheet === void 0 ? void 0 : focusSheet();
43
+ close();
44
+ } },
45
+ react_1.default.createElement(icons_1.TextoverflowClipIcon, null),
46
+ react_1.default.createElement("span", { className: "text-xs" }, "Shrink to fit"))) : null))));
40
47
  };
41
48
  exports.TextWrapSelector = TextWrapSelector;
@@ -31,11 +31,30 @@ export type UseCopyPasteCutProps = {
31
31
  onCut: CanvasGridProps["onCut"];
32
32
  };
33
33
  export type PasteSpecialType = "Transposed" | "Formatting" | "Value" | "Formula" | "Link";
34
+ /** Excel Paste Special → Operation: combine each pasted cell with the cell it lands on. */
35
+ export type PasteSpecialOperation = "add" | "subtract" | "multiply" | "divide";
36
+ /**
37
+ * Excel's Paste Special dialog as one request. `paste` picks what is
38
+ * pasted (all / values / formulas / formats), `operation` combines the pasted
39
+ * value with the destination (`=dest+source`, formulas stay formulas),
40
+ * `skipBlanks` leaves the destination untouched where the clipboard cell is
41
+ * empty and `transpose` swaps rows and columns. Every flag composes with the
42
+ * others; the legacy single-mode {@link PasteSpecialType} strings map onto
43
+ * the same fields.
44
+ */
45
+ export type PasteSpecialOptions = {
46
+ paste?: "all" | "values" | "formulas" | "formats";
47
+ operation?: PasteSpecialOperation;
48
+ skipBlanks?: boolean;
49
+ transpose?: boolean;
50
+ };
51
+ /** Argument accepted by `paste()` — a legacy single mode or the full dialog request. */
52
+ export type PasteSpecialRequest = PasteSpecialType | PasteSpecialOptions;
34
53
  export declare const useCopyPasteCut: ({ selections, activeCell, sheetId, onPaste, onCopy, onCut, focusSheet, }: UseCopyPasteCutProps) => {
35
54
  selections: SelectionArea[];
36
55
  copy: () => void;
37
56
  cut: () => void;
38
- paste: (pasteSpecialType?: PasteSpecialType) => Promise<void>;
57
+ paste: (pasteSpecial?: PasteSpecialRequest) => Promise<void>;
39
58
  copiedSelections: any[];
40
59
  clearCopiedSelections: () => void;
41
60
  onInternalCopy: (e: ClipboardEvent, isCutOperation?: boolean) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"use-copy-paste-cut.d.ts","sourceRoot":"","sources":["../../../hooks/use-copy-paste-cut.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAYvE,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,mCAAmC,CAAC;AAK3C,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,UAAU,EAAE,aAAa,CAAC;IAC1B;;OAEG;IACH,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B;;OAEG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACpC;;OAEG;IACH,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,YAAY,GACZ,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAEX,eAAO,MAAM,eAAe,GAAI,0EAQ7B,oBAAoB;;;;+BAoRO,gBAAgB;;;wBA1KtC,cAAc;yBAuDd,cAAc;uBAtBd,cAAc;CAmMrB,CAAC"}
1
+ {"version":3,"file":"use-copy-paste-cut.d.ts","sourceRoot":"","sources":["../../../hooks/use-copy-paste-cut.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAYvE,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,mCAAmC,CAAC;AAK3C,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,UAAU,EAAE,aAAa,CAAC;IAC1B;;OAEG;IACH,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B;;OAEG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACpC;;OAEG;IACH,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,YAAY,GACZ,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAEX,2FAA2F;AAC3F,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE/E;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAClD,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wFAAwF;AACxF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAEzE,eAAO,MAAM,eAAe,GAAI,0EAQ7B,oBAAoB;;;;2BAoRG,mBAAmB;;;wBA1KrC,cAAc;yBAuDd,cAAc;uBAtBd,cAAc;CA0MrB,CAAC"}
@@ -216,16 +216,16 @@ var useCopyPasteCut = function (_a) {
216
216
  focusSheet();
217
217
  }, [cellEditorInstance, focusSheet]);
218
218
  // From contextmenu
219
- var paste = (0, react_1.useCallback)(function (pasteSpecialType) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
219
+ var paste = (0, react_1.useCallback)(function (pasteSpecial) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
220
220
  function getClipboardContents() {
221
221
  return tslib_1.__awaiter(this, void 0, void 0, function () {
222
222
  var editorEl, dataTransfer, evt, clipboardItems, clipboardItems_1, clipboardItems_1_1, clipboardItem, _a, _b, type, blob, text, e_1_1, e_2_1, err_1;
223
223
  var e_2, _c, e_1, _d;
224
- var _e, _f, _g;
225
- return tslib_1.__generator(this, function (_h) {
226
- switch (_h.label) {
224
+ var _e, _f, _g, _h;
225
+ return tslib_1.__generator(this, function (_j) {
226
+ switch (_j.label) {
227
227
  case 0:
228
- _h.trys.push([0, 17, , 18]);
228
+ _j.trys.push([0, 17, , 18]);
229
229
  editorEl = (_e = cellEditorInstance === null || cellEditorInstance === void 0 ? void 0 : cellEditorInstance.el) === null || _e === void 0 ? void 0 : _e.call(cellEditorInstance);
230
230
  // Focus editor
231
231
  cellEditorInstance === null || cellEditorInstance === void 0 ? void 0 : cellEditorInstance.focus();
@@ -237,37 +237,37 @@ var useCopyPasteCut = function (_a) {
237
237
  });
238
238
  return [4 /*yield*/, navigator.clipboard.read()];
239
239
  case 1:
240
- clipboardItems = _h.sent();
241
- _h.label = 2;
240
+ clipboardItems = _j.sent();
241
+ _j.label = 2;
242
242
  case 2:
243
- _h.trys.push([2, 14, 15, 16]);
243
+ _j.trys.push([2, 14, 15, 16]);
244
244
  clipboardItems_1 = tslib_1.__values(clipboardItems), clipboardItems_1_1 = clipboardItems_1.next();
245
- _h.label = 3;
245
+ _j.label = 3;
246
246
  case 3:
247
247
  if (!!clipboardItems_1_1.done) return [3 /*break*/, 13];
248
248
  clipboardItem = clipboardItems_1_1.value;
249
- _h.label = 4;
249
+ _j.label = 4;
250
250
  case 4:
251
- _h.trys.push([4, 10, 11, 12]);
251
+ _j.trys.push([4, 10, 11, 12]);
252
252
  _a = (e_1 = void 0, tslib_1.__values(clipboardItem.types)), _b = _a.next();
253
- _h.label = 5;
253
+ _j.label = 5;
254
254
  case 5:
255
255
  if (!!_b.done) return [3 /*break*/, 9];
256
256
  type = _b.value;
257
257
  return [4 /*yield*/, clipboardItem.getType(type)];
258
258
  case 6:
259
- blob = _h.sent();
259
+ blob = _j.sent();
260
260
  return [4 /*yield*/, blob.text()];
261
261
  case 7:
262
- text = _h.sent();
262
+ text = _j.sent();
263
263
  (_f = evt.clipboardData) === null || _f === void 0 ? void 0 : _f.setData(type, text);
264
- _h.label = 8;
264
+ _j.label = 8;
265
265
  case 8:
266
266
  _b = _a.next();
267
267
  return [3 /*break*/, 5];
268
268
  case 9: return [3 /*break*/, 12];
269
269
  case 10:
270
- e_1_1 = _h.sent();
270
+ e_1_1 = _j.sent();
271
271
  e_1 = { error: e_1_1 };
272
272
  return [3 /*break*/, 12];
273
273
  case 11:
@@ -281,7 +281,7 @@ var useCopyPasteCut = function (_a) {
281
281
  return [3 /*break*/, 3];
282
282
  case 13: return [3 /*break*/, 16];
283
283
  case 14:
284
- e_2_1 = _h.sent();
284
+ e_2_1 = _j.sent();
285
285
  e_2 = { error: e_2_1 };
286
286
  return [3 /*break*/, 16];
287
287
  case 15:
@@ -291,15 +291,20 @@ var useCopyPasteCut = function (_a) {
291
291
  finally { if (e_2) throw e_2.error; }
292
292
  return [7 /*endfinally*/];
293
293
  case 16:
294
- if (pasteSpecialType) {
295
- (_g = evt.clipboardData) === null || _g === void 0 ? void 0 : _g.setData("pasteSpecialType", pasteSpecialType);
294
+ // The paste pipelines read the request back from these synthetic
295
+ // clipboard keys (`resolvePasteSpecialOptions` in spreadsheet-state).
296
+ if (typeof pasteSpecial === "string") {
297
+ (_g = evt.clipboardData) === null || _g === void 0 ? void 0 : _g.setData("pasteSpecialType", pasteSpecial);
298
+ }
299
+ else if (pasteSpecial) {
300
+ (_h = evt.clipboardData) === null || _h === void 0 ? void 0 : _h.setData("pasteSpecialOptions", JSON.stringify(pasteSpecial));
296
301
  }
297
302
  if (editorEl) {
298
303
  editorEl.dispatchEvent(evt);
299
304
  }
300
305
  return [3 /*break*/, 18];
301
306
  case 17:
302
- err_1 = _h.sent();
307
+ err_1 = _j.sent();
303
308
  console.error(err_1);
304
309
  return [3 /*break*/, 18];
305
310
  case 18: return [2 /*return*/];
@@ -1,12 +1,16 @@
1
1
  import type { CellInterface, SelectionArea } from "@rowsncolumns/grid";
2
2
  import type React from "react";
3
3
  import type { CanvasGridProps, SelectionAttributes } from "../components/canvas-grid/typings";
4
- import type { CellFormat, SheetRange } from "../types";
4
+ import type { CellFormat, ExtendedValue, Maybe, SheetRange } from "../types";
5
5
  import type { useContextMenu } from "./use-context-menu";
6
6
  type FormattingKeyboardProps = {
7
7
  sheetId: number;
8
8
  locale?: string;
9
9
  showFormulas?: boolean;
10
+ /**
11
+ * Resolves the Excel-vs-Sheets shortcut conflicts; defaults to "sheets".
12
+ */
13
+ keymap?: CanvasGridProps["keymap"];
10
14
  defaultFontSizePoint: NonNullable<CanvasGridProps["defaultFontSizePoint"]>;
11
15
  canEditCell(sheetId: number, cell: CellInterface): boolean;
12
16
  onChangeFormatting: CanvasGridProps["onChangeFormatting"];
@@ -20,6 +24,9 @@ type FormattingKeyboardProps = {
20
24
  selections: SelectionArea<SelectionAttributes>[];
21
25
  selectedRows: Record<string, boolean>;
22
26
  selectedColumns: Record<string, boolean>;
27
+ /** Whole rows / columns selected through their headers */
28
+ selectedRowHeadersIds?: number[];
29
+ selectedColumnHeadersIds?: number[];
23
30
  onSelectNextSheet: CanvasGridProps["onSelectNextSheet"];
24
31
  onSelectPreviousSheet: CanvasGridProps["onSelectPreviousSheet"];
25
32
  onCreateNewSheet: CanvasGridProps["onCreateNewSheet"];
@@ -51,7 +58,15 @@ type FormattingKeyboardProps = {
51
58
  onShowContextMenu: ReturnType<typeof useContextMenu>["showContextMenu"];
52
59
  onAddQuickEdit(range: SheetRange): void;
53
60
  isSheetFocused(): boolean;
61
+ onCalculateNow?: CanvasGridProps["onCalculateNow"];
62
+ onRequestGoTo?: CanvasGridProps["onRequestGoTo"];
63
+ /** Ctrl+Alt+V — Paste Special dialog (Excel). */
64
+ onRequestPasteSpecial?: () => void;
65
+ /** Ctrl+' / Ctrl+Shift+" write the cell above into the active cell */
66
+ onChangeBatch?: CanvasGridProps["onChangeBatch"];
67
+ getUserEnteredValue?(sheetId: number, rowIndex: number, columnIndex: number): string | undefined | null;
68
+ getEffectiveValue?(sheetId: number, rowIndex: number, columnIndex: number): Maybe<ExtendedValue>;
54
69
  };
55
- export declare const useKeyboardHandler: ({ sheetId, locale, selectedRows, selectedColumns, showFormulas, defaultFontSizePoint, onShowFormulas, onChangeFormatting, onRepeatFormatting, onClearFormatting, onSelectNextSheet, onSelectPreviousSheet, onCreateNewSheet, onFillRange, disableFillRightShortcut, onInsertTime, onInsertDate, onInsertDateTime, onInsertNote, onInsertAutoSum, onRedo, onUndo, onRequestSearch, onRequestReplace, onRequestFormatCells, onRequestInsertComment, onRequestInsertLink, onCreateTable, onCreateBasicFilter, clearCopiedSelections, getCellFormat, onShowContextMenu, onHideColumn, onGroupColumns, onUngroupColumns, onHideRow, onGroupRows, onUngroupRows, onDeleteRow, onInsertRow, onAddQuickEdit, canEditCell, isSheetFocused, activeCell, selections, }: FormattingKeyboardProps) => (e: React.KeyboardEvent<HTMLDivElement>) => void;
70
+ export declare const useKeyboardHandler: ({ sheetId, locale, selectedRows, selectedColumns, selectedRowHeadersIds, selectedColumnHeadersIds, showFormulas, keymap, defaultFontSizePoint, onShowFormulas, onChangeFormatting, onRepeatFormatting, onClearFormatting, onSelectNextSheet, onSelectPreviousSheet, onCreateNewSheet, onFillRange, disableFillRightShortcut, onInsertTime, onInsertDate, onInsertDateTime, onInsertNote, onInsertAutoSum, onRedo, onUndo, onRequestSearch, onRequestReplace, onRequestFormatCells, onRequestInsertComment, onRequestInsertLink, onCreateTable, onCreateBasicFilter, clearCopiedSelections, getCellFormat, onShowContextMenu, onHideColumn, onShowColumn, onGroupColumns, onUngroupColumns, onHideRow, onShowRow, onGroupRows, onUngroupRows, onDeleteRow, onInsertRow, onAddQuickEdit, onCalculateNow, onRequestGoTo, onRequestPasteSpecial, onChangeBatch, getUserEnteredValue, getEffectiveValue, canEditCell, isSheetFocused, activeCell, selections, }: FormattingKeyboardProps) => (e: React.KeyboardEvent<HTMLDivElement>) => void;
56
71
  export {};
57
72
  //# sourceMappingURL=use-keyboard-handler.d.ts.map