@rowsncolumns/spreadsheet 10.2.74 → 10.2.75
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/cjs/api/excel-validation-kinds.d.ts +71 -0
- package/dist/cjs/api/excel-validation-kinds.d.ts.map +1 -0
- package/dist/cjs/api/excel-validation-kinds.js +304 -0
- package/dist/cjs/api/go-to-special.d.ts +32 -0
- package/dist/cjs/api/go-to-special.d.ts.map +1 -0
- package/dist/cjs/api/go-to-special.js +111 -0
- package/dist/cjs/api/validation-tooltip.d.ts +17 -0
- package/dist/cjs/api/validation-tooltip.d.ts.map +1 -0
- package/dist/cjs/api/validation-tooltip.js +42 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts +28 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/advanced-filter-dialog.atom.js +32 -0
- package/dist/cjs/atoms/go-to-dialog.atom.d.ts +11 -1
- package/dist/cjs/atoms/go-to-dialog.atom.d.ts.map +1 -1
- package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts +29 -0
- package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/goal-seek-dialog.atom.js +30 -0
- package/dist/cjs/atoms/index.d.ts +1 -0
- package/dist/cjs/atoms/index.d.ts.map +1 -1
- package/dist/cjs/atoms/index.js +1 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.d.ts +39 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/subtotal-dialog.atom.js +30 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts +34 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/text-to-columns-dialog.atom.js +36 -0
- package/dist/cjs/atoms/watch-window.atom.d.ts +37 -0
- package/dist/cjs/atoms/watch-window.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/watch-window.atom.js +61 -0
- package/dist/cjs/components/advanced-filter-dialog/index.d.ts +28 -0
- package/dist/cjs/components/advanced-filter-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/advanced-filter-dialog/index.js +134 -0
- package/dist/cjs/components/calculation-options-dialog/index.d.ts +33 -0
- package/dist/cjs/components/calculation-options-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/calculation-options-dialog/index.js +69 -0
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +104 -21
- package/dist/cjs/components/canvas-grid/typings.d.ts +19 -2
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.d.ts +30 -0
- package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.js +56 -1
- package/dist/cjs/components/cell/index.d.ts.map +1 -1
- package/dist/cjs/components/cell/index.js +15 -7
- package/dist/cjs/components/cell-diff/index.d.ts +3 -2
- package/dist/cjs/components/cell-diff/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.js +2 -1
- package/dist/cjs/components/context-menu/index.d.ts +6 -1
- package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
- package/dist/cjs/components/context-menu/index.js +146 -3
- package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/cjs/components/embeds/embed-component.js +16 -1
- package/dist/cjs/components/go-to-dialog/index.d.ts +11 -1
- package/dist/cjs/components/go-to-dialog/index.d.ts.map +1 -1
- package/dist/cjs/components/go-to-dialog/index.js +67 -24
- package/dist/cjs/components/goal-seek-dialog/index.d.ts +32 -0
- package/dist/cjs/components/goal-seek-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/goal-seek-dialog/index.js +149 -0
- package/dist/cjs/components/subtotal-dialog/index.d.ts +30 -0
- package/dist/cjs/components/subtotal-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/subtotal-dialog/index.js +126 -0
- package/dist/cjs/components/text-to-columns-dialog/index.d.ts +37 -0
- package/dist/cjs/components/text-to-columns-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/text-to-columns-dialog/index.js +166 -0
- package/dist/cjs/components/toolbar/background-color-selector.d.ts +13 -1
- package/dist/cjs/components/toolbar/background-color-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/background-color-selector.js +57 -11
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts +15 -2
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/cell-style-selector.js +56 -3
- package/dist/cjs/components/toolbar/fill-selectors.d.ts +49 -0
- package/dist/cjs/components/toolbar/fill-selectors.d.ts.map +1 -0
- package/dist/cjs/components/toolbar/fill-selectors.js +196 -0
- package/dist/cjs/components/toolbar/index.d.ts +1 -0
- package/dist/cjs/components/toolbar/index.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/index.js +1 -0
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/text-horizontal-align-selector.js +23 -4
- package/dist/cjs/components/watch-window/index.d.ts +25 -0
- package/dist/cjs/components/watch-window/index.d.ts.map +1 -0
- package/dist/cjs/components/watch-window/index.js +63 -0
- package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-cell-renderer.js +17 -6
- package/dist/cjs/hooks/use-get-tooltip.d.ts.map +1 -1
- package/dist/cjs/hooks/use-get-tooltip.js +7 -2
- package/dist/cjs/hooks/use-keyboard-handler.d.ts.map +1 -1
- package/dist/cjs/hooks/use-keyboard-handler.js +24 -12
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +117 -4
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +19 -1
- package/dist/esm/api/excel-validation-kinds.d.ts +71 -0
- package/dist/esm/api/excel-validation-kinds.d.ts.map +1 -0
- package/dist/esm/api/excel-validation-kinds.js +287 -0
- package/dist/esm/api/go-to-special.d.ts +32 -0
- package/dist/esm/api/go-to-special.d.ts.map +1 -0
- package/dist/esm/api/go-to-special.js +96 -0
- package/dist/esm/api/validation-tooltip.d.ts +17 -0
- package/dist/esm/api/validation-tooltip.d.ts.map +1 -0
- package/dist/esm/api/validation-tooltip.js +34 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts +28 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/advanced-filter-dialog.atom.js +21 -0
- package/dist/esm/atoms/go-to-dialog.atom.d.ts +11 -1
- package/dist/esm/atoms/go-to-dialog.atom.d.ts.map +1 -1
- package/dist/esm/atoms/goal-seek-dialog.atom.d.ts +29 -0
- package/dist/esm/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/goal-seek-dialog.atom.js +21 -0
- package/dist/esm/atoms/index.d.ts +1 -0
- package/dist/esm/atoms/index.d.ts.map +1 -1
- package/dist/esm/atoms/index.js +1 -0
- package/dist/esm/atoms/subtotal-dialog.atom.d.ts +39 -0
- package/dist/esm/atoms/subtotal-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/subtotal-dialog.atom.js +21 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts +34 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/text-to-columns-dialog.atom.js +25 -0
- package/dist/esm/atoms/watch-window.atom.d.ts +37 -0
- package/dist/esm/atoms/watch-window.atom.d.ts.map +1 -0
- package/dist/esm/atoms/watch-window.atom.js +37 -0
- package/dist/esm/components/advanced-filter-dialog/index.d.ts +28 -0
- package/dist/esm/components/advanced-filter-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/advanced-filter-dialog/index.js +125 -0
- package/dist/esm/components/calculation-options-dialog/index.d.ts +33 -0
- package/dist/esm/components/calculation-options-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/calculation-options-dialog/index.js +61 -0
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +99 -20
- package/dist/esm/components/canvas-grid/typings.d.ts +19 -2
- package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.d.ts +30 -0
- package/dist/esm/components/cell/alignment.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.js +51 -0
- package/dist/esm/components/cell/index.d.ts.map +1 -1
- package/dist/esm/components/cell/index.js +16 -8
- package/dist/esm/components/cell-diff/index.d.ts +3 -2
- package/dist/esm/components/cell-diff/index.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.js +2 -1
- package/dist/esm/components/context-menu/index.d.ts +6 -1
- package/dist/esm/components/context-menu/index.d.ts.map +1 -1
- package/dist/esm/components/context-menu/index.js +139 -4
- package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/esm/components/embeds/embed-component.js +17 -1
- package/dist/esm/components/go-to-dialog/index.d.ts +11 -1
- package/dist/esm/components/go-to-dialog/index.d.ts.map +1 -1
- package/dist/esm/components/go-to-dialog/index.js +63 -23
- package/dist/esm/components/goal-seek-dialog/index.d.ts +32 -0
- package/dist/esm/components/goal-seek-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/goal-seek-dialog/index.js +125 -0
- package/dist/esm/components/subtotal-dialog/index.d.ts +30 -0
- package/dist/esm/components/subtotal-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/subtotal-dialog/index.js +115 -0
- package/dist/esm/components/text-to-columns-dialog/index.d.ts +37 -0
- package/dist/esm/components/text-to-columns-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/text-to-columns-dialog/index.js +147 -0
- package/dist/esm/components/toolbar/background-color-selector.d.ts +13 -1
- package/dist/esm/components/toolbar/background-color-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/background-color-selector.js +61 -11
- package/dist/esm/components/toolbar/cell-style-selector.d.ts +15 -2
- package/dist/esm/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/cell-style-selector.js +46 -2
- package/dist/esm/components/toolbar/fill-selectors.d.ts +49 -0
- package/dist/esm/components/toolbar/fill-selectors.d.ts.map +1 -0
- package/dist/esm/components/toolbar/fill-selectors.js +162 -0
- package/dist/esm/components/toolbar/index.d.ts +1 -0
- package/dist/esm/components/toolbar/index.d.ts.map +1 -1
- package/dist/esm/components/toolbar/index.js +1 -0
- package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
- package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/text-horizontal-align-selector.js +21 -5
- package/dist/esm/components/watch-window/index.d.ts +25 -0
- package/dist/esm/components/watch-window/index.d.ts.map +1 -0
- package/dist/esm/components/watch-window/index.js +54 -0
- package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-cell-renderer.js +17 -6
- package/dist/esm/hooks/use-get-tooltip.d.ts.map +1 -1
- package/dist/esm/hooks/use-get-tooltip.js +7 -2
- package/dist/esm/hooks/use-keyboard-handler.d.ts.map +1 -1
- package/dist/esm/hooks/use-keyboard-handler.js +24 -12
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +8 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +117 -4
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +18 -0
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SubtotalOptions } from "../types";
|
|
2
|
+
/** One column of the range being subtotalled. */
|
|
3
|
+
export type SubtotalDialogColumn = {
|
|
4
|
+
/** Absolute sheet column index. */
|
|
5
|
+
columnIndex: number;
|
|
6
|
+
/** Positional label, e.g. "Column B". */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Text of the first row, shown instead of `label` when the data has headers. */
|
|
9
|
+
header?: string;
|
|
10
|
+
};
|
|
11
|
+
/** The dialog's choices; the opener adds the `range`. */
|
|
12
|
+
export type SubtotalDialogChoices = Omit<SubtotalOptions, "range">;
|
|
13
|
+
/**
|
|
14
|
+
* Open-state + payload for the Subtotal dialog (Excel's Data → Outline →
|
|
15
|
+
* Subtotal). The context menu opens it with the range's columns; the
|
|
16
|
+
* host-mounted dialog routes the choices back through `onApply`.
|
|
17
|
+
*/
|
|
18
|
+
export type SubtotalDialogRequest = {
|
|
19
|
+
columns: SubtotalDialogColumn[];
|
|
20
|
+
/** Initial state of "My data has headers". */
|
|
21
|
+
hasHeader: boolean;
|
|
22
|
+
/** Routed to `onSubtotal` by the opener. */
|
|
23
|
+
onApply: (choices: SubtotalDialogChoices) => void;
|
|
24
|
+
};
|
|
25
|
+
export declare const subtotalDialogAtom: import("jotai").PrimitiveAtom<SubtotalDialogRequest | null> & {
|
|
26
|
+
init: SubtotalDialogRequest | null;
|
|
27
|
+
};
|
|
28
|
+
export declare const useSubtotalDialogState: () => [SubtotalDialogRequest | null, (args_0: SubtotalDialogRequest | ((prev: SubtotalDialogRequest | null) => SubtotalDialogRequest | null) | null) => void];
|
|
29
|
+
/** Number of mounted `SubtotalDialogHost`s — the menu item is offered only while > 0. */
|
|
30
|
+
export declare const subtotalDialogHostCountAtom: import("jotai").PrimitiveAtom<number> & {
|
|
31
|
+
init: number;
|
|
32
|
+
};
|
|
33
|
+
/** True when at least one `SubtotalDialogHost` is mounted. */
|
|
34
|
+
export declare const useIsSubtotalDialogMounted: () => boolean;
|
|
35
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
36
|
+
export declare const useRegisterSubtotalDialogHost: () => void;
|
|
37
|
+
/** Returns a function that opens the host-mounted Subtotal dialog. */
|
|
38
|
+
export declare const useOpenSubtotalDialog: () => (request: SubtotalDialogRequest) => void;
|
|
39
|
+
//# sourceMappingURL=subtotal-dialog.atom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subtotal-dialog.atom.d.ts","sourceRoot":"","sources":["../../../atoms/subtotal-dialog.atom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG;IACjC,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,kBAAkB;;CAA2C,CAAC;AAE3E,eAAO,MAAM,sBAAsB,+JAAoC,CAAC;AAExE,yFAAyF;AACzF,eAAO,MAAM,2BAA2B;;CAAU,CAAC;AAEnD,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,eACQ,CAAC;AAEhD,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,YAMzC,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,kBAGpB,qBAAqB,SAGlC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
export const subtotalDialogAtom = atom(null);
|
|
4
|
+
export const useSubtotalDialogState = () => useAtom(subtotalDialogAtom);
|
|
5
|
+
/** Number of mounted `SubtotalDialogHost`s — the menu item is offered only while > 0. */
|
|
6
|
+
export const subtotalDialogHostCountAtom = atom(0);
|
|
7
|
+
/** True when at least one `SubtotalDialogHost` is mounted. */
|
|
8
|
+
export const useIsSubtotalDialogMounted = () => useAtomValue(subtotalDialogHostCountAtom) > 0;
|
|
9
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
10
|
+
export const useRegisterSubtotalDialogHost = () => {
|
|
11
|
+
const setCount = useSetAtom(subtotalDialogHostCountAtom);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setCount((count) => count + 1);
|
|
14
|
+
return () => setCount((count) => count - 1);
|
|
15
|
+
}, [setCount]);
|
|
16
|
+
};
|
|
17
|
+
/** Returns a function that opens the host-mounted Subtotal dialog. */
|
|
18
|
+
export const useOpenSubtotalDialog = () => {
|
|
19
|
+
const setRequest = useSetAtom(subtotalDialogAtom);
|
|
20
|
+
return useCallback((request) => setRequest(request), [setRequest]);
|
|
21
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SplitTextOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Open-state + payload for the Text to Columns dialog (Excel's Data → Text
|
|
4
|
+
* to Columns wizard: Delimited / Fixed width). The context menu opens it
|
|
5
|
+
* with a preview of the selected column's texts; the host-mounted dialog
|
|
6
|
+
* routes the chosen delimiter or column breaks back through `onApply`.
|
|
7
|
+
*/
|
|
8
|
+
export type TextToColumnsDialogRequest = {
|
|
9
|
+
/** Texts of the first cells of the selected column (for the preview / ruler). */
|
|
10
|
+
preview: string[];
|
|
11
|
+
/** Routed to `onSplitTextToColumns` by the opener. */
|
|
12
|
+
onApply: (options: {
|
|
13
|
+
delimiter: string;
|
|
14
|
+
} & SplitTextOptions) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare const textToColumnsDialogAtom: import("jotai").PrimitiveAtom<TextToColumnsDialogRequest | null> & {
|
|
17
|
+
init: TextToColumnsDialogRequest | null;
|
|
18
|
+
};
|
|
19
|
+
export declare const useTextToColumnsDialogState: () => [TextToColumnsDialogRequest | null, (args_0: TextToColumnsDialogRequest | ((prev: TextToColumnsDialogRequest | null) => TextToColumnsDialogRequest | null) | null) => void];
|
|
20
|
+
/**
|
|
21
|
+
* Number of mounted `TextToColumnsDialogHost`s. The context menu only offers
|
|
22
|
+
* "Fixed width…" while a host is mounted, so a host that has not adopted the
|
|
23
|
+
* dialog keeps its one-click delimiter items and no dead entry.
|
|
24
|
+
*/
|
|
25
|
+
export declare const textToColumnsDialogHostCountAtom: import("jotai").PrimitiveAtom<number> & {
|
|
26
|
+
init: number;
|
|
27
|
+
};
|
|
28
|
+
/** True when at least one `TextToColumnsDialogHost` is mounted. */
|
|
29
|
+
export declare const useIsTextToColumnsDialogMounted: () => boolean;
|
|
30
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
31
|
+
export declare const useRegisterTextToColumnsDialogHost: () => void;
|
|
32
|
+
/** Returns a function that opens the host-mounted Text to Columns dialog. */
|
|
33
|
+
export declare const useOpenTextToColumnsDialog: () => (request: TextToColumnsDialogRequest) => void;
|
|
34
|
+
//# sourceMappingURL=text-to-columns-dialog.atom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-to-columns-dialog.atom.d.ts","sourceRoot":"","sources":["../../../atoms/text-to-columns-dialog.atom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sDAAsD;IACtD,OAAO,EAAE,CAAC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,gBAAgB,KAAK,IAAI,CAAC;CACtE,CAAC;AAEF,eAAO,MAAM,uBAAuB;;CAEnC,CAAC;AAEF,eAAO,MAAM,2BAA2B,mLACN,CAAC;AAEnC;;;;GAIG;AACH,eAAO,MAAM,gCAAgC;;CAAU,CAAC;AAExD,mEAAmE;AACnE,eAAO,MAAM,+BAA+B,eACQ,CAAC;AAErD,yEAAyE;AACzE,eAAO,MAAM,kCAAkC,YAM9C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,kBAGzB,0BAA0B,SAGvC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
export const textToColumnsDialogAtom = atom(null);
|
|
4
|
+
export const useTextToColumnsDialogState = () => useAtom(textToColumnsDialogAtom);
|
|
5
|
+
/**
|
|
6
|
+
* Number of mounted `TextToColumnsDialogHost`s. The context menu only offers
|
|
7
|
+
* "Fixed width…" while a host is mounted, so a host that has not adopted the
|
|
8
|
+
* dialog keeps its one-click delimiter items and no dead entry.
|
|
9
|
+
*/
|
|
10
|
+
export const textToColumnsDialogHostCountAtom = atom(0);
|
|
11
|
+
/** True when at least one `TextToColumnsDialogHost` is mounted. */
|
|
12
|
+
export const useIsTextToColumnsDialogMounted = () => useAtomValue(textToColumnsDialogHostCountAtom) > 0;
|
|
13
|
+
/** Registers the calling component as a dialog host for its lifetime. */
|
|
14
|
+
export const useRegisterTextToColumnsDialogHost = () => {
|
|
15
|
+
const setCount = useSetAtom(textToColumnsDialogHostCountAtom);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
setCount((count) => count + 1);
|
|
18
|
+
return () => setCount((count) => count - 1);
|
|
19
|
+
}, [setCount]);
|
|
20
|
+
};
|
|
21
|
+
/** Returns a function that opens the host-mounted Text to Columns dialog. */
|
|
22
|
+
export const useOpenTextToColumnsDialog = () => {
|
|
23
|
+
const setRequest = useSetAtom(textToColumnsDialogAtom);
|
|
24
|
+
return useCallback((request) => setRequest(request), [setRequest]);
|
|
25
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SheetCoordinate } from "../types";
|
|
2
|
+
/** A watched cell (Excel's Watch Window row). */
|
|
3
|
+
export type WatchWindowEntry = SheetCoordinate;
|
|
4
|
+
/**
|
|
5
|
+
* What the grid publishes so the host-mounted panel can show live values
|
|
6
|
+
* and jump to a cell. Republished on every grid render while a host is
|
|
7
|
+
* mounted, so values track recalculation.
|
|
8
|
+
*/
|
|
9
|
+
export type WatchWindowContext = {
|
|
10
|
+
getSheetName?: (sheetId: number) => string | undefined;
|
|
11
|
+
getFormattedValue: (sheetId: number, rowIndex: number, columnIndex: number) => string | undefined;
|
|
12
|
+
getFormula: (sheetId: number, rowIndex: number, columnIndex: number) => string | undefined | null;
|
|
13
|
+
onNavigate: (cell: SheetCoordinate) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const watchWindowEntriesAtom: import("jotai").PrimitiveAtom<SheetCoordinate[]> & {
|
|
16
|
+
init: SheetCoordinate[];
|
|
17
|
+
};
|
|
18
|
+
export declare const watchWindowOpenAtom: import("jotai").PrimitiveAtom<boolean> & {
|
|
19
|
+
init: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare const watchWindowContextAtom: import("jotai").PrimitiveAtom<WatchWindowContext | null> & {
|
|
22
|
+
init: WatchWindowContext | null;
|
|
23
|
+
};
|
|
24
|
+
/** Number of mounted `WatchWindowHost`s — the grid offers "Add watch" only then. */
|
|
25
|
+
export declare const watchWindowHostCountAtom: import("jotai").PrimitiveAtom<number> & {
|
|
26
|
+
init: number;
|
|
27
|
+
};
|
|
28
|
+
export declare const useIsWatchWindowMounted: () => boolean;
|
|
29
|
+
export declare const useRegisterWatchWindowHost: () => void;
|
|
30
|
+
export declare const useWatchWindowEntries: () => [SheetCoordinate[], (args_0: SheetCoordinate[] | ((prev: SheetCoordinate[]) => SheetCoordinate[])) => void];
|
|
31
|
+
export declare const useWatchWindowOpen: () => [boolean, (args_0: boolean | ((prev: boolean) => boolean)) => void];
|
|
32
|
+
export declare const useWatchWindowContext: () => WatchWindowContext | null;
|
|
33
|
+
export declare const useSetWatchWindowContext: () => (args_0: WatchWindowContext | ((prev: WatchWindowContext | null) => WatchWindowContext | null) | null) => void;
|
|
34
|
+
/** Adds a cell (once) and opens the panel. */
|
|
35
|
+
export declare const useAddWatch: () => (cell: SheetCoordinate) => void;
|
|
36
|
+
export declare const useRemoveWatch: () => (cell: SheetCoordinate) => void;
|
|
37
|
+
//# sourceMappingURL=watch-window.atom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-window.atom.d.ts","sourceRoot":"","sources":["../../../atoms/watch-window.atom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,iDAAiD;AACjD,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,iBAAiB,EAAE,CACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,KAChB,MAAM,GAAG,SAAS,CAAC;IACxB,UAAU,EAAE,CACV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,KAChB,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,sBAAsB;;CAA+B,CAAC;AACnE,eAAO,MAAM,mBAAmB;;CAAc,CAAC;AAC/C,eAAO,MAAM,sBAAsB;;CAAwC,CAAC;AAE5E,oFAAoF;AACpF,eAAO,MAAM,wBAAwB;;CAAU,CAAC;AAEhD,eAAO,MAAM,uBAAuB,eACQ,CAAC;AAE7C,eAAO,MAAM,0BAA0B,YAMtC,CAAC;AAEF,eAAO,MAAM,qBAAqB,mHAAwC,CAAC;AAC3E,eAAO,MAAM,kBAAkB,2EAAqC,CAAC;AACrE,eAAO,MAAM,qBAAqB,iCAA6C,CAAC;AAChF,eAAO,MAAM,wBAAwB,sHACD,CAAC;AAOrC,8CAA8C;AAC9C,eAAO,MAAM,WAAW,eAIb,eAAe,SAUzB,CAAC;AAEF,eAAO,MAAM,cAAc,eAGhB,eAAe,SAMzB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
export const watchWindowEntriesAtom = atom([]);
|
|
4
|
+
export const watchWindowOpenAtom = atom(false);
|
|
5
|
+
export const watchWindowContextAtom = atom(null);
|
|
6
|
+
/** Number of mounted `WatchWindowHost`s — the grid offers "Add watch" only then. */
|
|
7
|
+
export const watchWindowHostCountAtom = atom(0);
|
|
8
|
+
export const useIsWatchWindowMounted = () => useAtomValue(watchWindowHostCountAtom) > 0;
|
|
9
|
+
export const useRegisterWatchWindowHost = () => {
|
|
10
|
+
const setCount = useSetAtom(watchWindowHostCountAtom);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
setCount((count) => count + 1);
|
|
13
|
+
return () => setCount((count) => count - 1);
|
|
14
|
+
}, [setCount]);
|
|
15
|
+
};
|
|
16
|
+
export const useWatchWindowEntries = () => useAtom(watchWindowEntriesAtom);
|
|
17
|
+
export const useWatchWindowOpen = () => useAtom(watchWindowOpenAtom);
|
|
18
|
+
export const useWatchWindowContext = () => useAtomValue(watchWindowContextAtom);
|
|
19
|
+
export const useSetWatchWindowContext = () => useSetAtom(watchWindowContextAtom);
|
|
20
|
+
const sameCell = (a, b) => a.sheetId === b.sheetId &&
|
|
21
|
+
a.rowIndex === b.rowIndex &&
|
|
22
|
+
a.columnIndex === b.columnIndex;
|
|
23
|
+
/** Adds a cell (once) and opens the panel. */
|
|
24
|
+
export const useAddWatch = () => {
|
|
25
|
+
const setEntries = useSetAtom(watchWindowEntriesAtom);
|
|
26
|
+
const setOpen = useSetAtom(watchWindowOpenAtom);
|
|
27
|
+
return useCallback((cell) => {
|
|
28
|
+
setEntries((entries) => entries.some((entry) => sameCell(entry, cell))
|
|
29
|
+
? entries
|
|
30
|
+
: [...entries, cell]);
|
|
31
|
+
setOpen(true);
|
|
32
|
+
}, [setEntries, setOpen]);
|
|
33
|
+
};
|
|
34
|
+
export const useRemoveWatch = () => {
|
|
35
|
+
const setEntries = useSetAtom(watchWindowEntriesAtom);
|
|
36
|
+
return useCallback((cell) => setEntries((entries) => entries.filter((entry) => !sameCell(entry, cell))), [setEntries]);
|
|
37
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AdvancedFilterOptions } from "../../types";
|
|
3
|
+
export type AdvancedFilterDialogProps = {
|
|
4
|
+
/** Controls visibility. */
|
|
5
|
+
open: boolean;
|
|
6
|
+
onOpenChange(open: boolean): void;
|
|
7
|
+
/** Default "List range" (A1 reference of the data block around the active cell). */
|
|
8
|
+
initialListRange: string;
|
|
9
|
+
/** Emits the resolved options; the host routes them to `onAdvancedFilter`. */
|
|
10
|
+
onApply(options: AdvancedFilterOptions): void;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Excel's Advanced Filter dialog: filter in place or copy to another
|
|
14
|
+
* location, a list range, a criteria range (header row + criteria rows),
|
|
15
|
+
* the copy destination and "Unique records only".
|
|
16
|
+
*/
|
|
17
|
+
export declare const AdvancedFilterDialog: React.MemoExoticComponent<({ open, onOpenChange, initialListRange, onApply }: AdvancedFilterDialogProps) => React.JSX.Element>;
|
|
18
|
+
/**
|
|
19
|
+
* Host-mounted, atom-driven wrapper around {@link AdvancedFilterDialog}.
|
|
20
|
+
* Mount once in the host; the context menu opens it via
|
|
21
|
+
* `useOpenAdvancedFilterDialog` and only offers the item while a host is
|
|
22
|
+
* mounted.
|
|
23
|
+
*/
|
|
24
|
+
export declare const AdvancedFilterDialogHost: {
|
|
25
|
+
(): React.JSX.Element;
|
|
26
|
+
displayName: string;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/advanced-filter-dialog/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAMtE,OAAO,KAAK,EAAwB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,MAAM,yBAAyB,GAAG;IACtC,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC/C,CAAC;AAQF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gFACqB,yBAAyB,uBAsK9E,CAAC;AAIF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;CAcpC,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Cross1Icon } from "@rowsncolumns/icons";
|
|
2
|
+
import { Button, Dialog, DialogBody, DialogCloseButton, DialogContent, DialogDescription, DialogTitle, Input, Separator, } from "@rowsncolumns/ui";
|
|
3
|
+
import { addressToCell, addressToSelection } from "@rowsncolumns/utils";
|
|
4
|
+
import React, { memo, useCallback, useEffect, useState } from "react";
|
|
5
|
+
import { useAdvancedFilterDialogState, useRegisterAdvancedFilterDialogHost, } from "../../atoms/advanced-filter-dialog.atom";
|
|
6
|
+
const parseRange = (text) => {
|
|
7
|
+
const trimmed = text.trim().replace(/^[^!]*!/, "");
|
|
8
|
+
if (!/^\$?[A-Za-z]{1,3}\$?\d+(:\$?[A-Za-z]{1,3}\$?\d+)?$/.test(trimmed))
|
|
9
|
+
return undefined;
|
|
10
|
+
return addressToSelection(trimmed)?.range;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Excel's Advanced Filter dialog: filter in place or copy to another
|
|
14
|
+
* location, a list range, a criteria range (header row + criteria rows),
|
|
15
|
+
* the copy destination and "Unique records only".
|
|
16
|
+
*/
|
|
17
|
+
export const AdvancedFilterDialog = memo(({ open, onOpenChange, initialListRange, onApply }) => {
|
|
18
|
+
const [action, setAction] = useState("filter");
|
|
19
|
+
const [listRange, setListRange] = useState(initialListRange);
|
|
20
|
+
const [criteriaRange, setCriteriaRange] = useState("");
|
|
21
|
+
const [copyTo, setCopyTo] = useState("");
|
|
22
|
+
const [uniqueOnly, setUniqueOnly] = useState(false);
|
|
23
|
+
const [error, setError] = useState(undefined);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (open) {
|
|
26
|
+
setAction("filter");
|
|
27
|
+
setListRange(initialListRange);
|
|
28
|
+
setCriteriaRange("");
|
|
29
|
+
setCopyTo("");
|
|
30
|
+
setUniqueOnly(false);
|
|
31
|
+
setError(undefined);
|
|
32
|
+
}
|
|
33
|
+
// Only reset on open transitions.
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
}, [open]);
|
|
36
|
+
const submit = useCallback((event) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
const list = parseRange(listRange);
|
|
39
|
+
if (!list) {
|
|
40
|
+
setError("List range must be a range reference, e.g. A1:C20.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const criteria = parseRange(criteriaRange);
|
|
44
|
+
if (!criteria) {
|
|
45
|
+
setError("Criteria range must be a range reference, e.g. E1:F3.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let destination;
|
|
49
|
+
if (action === "copy") {
|
|
50
|
+
const cell = addressToCell(copyTo.trim().replace(/^[^!]*!/, "").split(":")[0] ?? "");
|
|
51
|
+
if (!copyTo.trim() || !cell) {
|
|
52
|
+
setError("Copy to must be a cell reference, e.g. H1.");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
destination = cell;
|
|
56
|
+
}
|
|
57
|
+
setError(undefined);
|
|
58
|
+
onApply({
|
|
59
|
+
listRange: list,
|
|
60
|
+
criteriaRange: criteria,
|
|
61
|
+
action,
|
|
62
|
+
copyTo: destination,
|
|
63
|
+
uniqueOnly,
|
|
64
|
+
});
|
|
65
|
+
onOpenChange(false);
|
|
66
|
+
}, [action, listRange, criteriaRange, copyTo, uniqueOnly, onApply, onOpenChange]);
|
|
67
|
+
return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange },
|
|
68
|
+
React.createElement(DialogContent, { className: "w-[400px] max-w-[92vw] pointer-events-auto" },
|
|
69
|
+
React.createElement(DialogTitle, null, "Advanced filter"),
|
|
70
|
+
React.createElement(DialogDescription, null, "Filter a list with a criteria range: conditions in one row AND together, rows OR together."),
|
|
71
|
+
React.createElement(DialogCloseButton, { onClick: () => onOpenChange(false) },
|
|
72
|
+
React.createElement(Cross1Icon, null)),
|
|
73
|
+
React.createElement(DialogBody, { className: "flex flex-col gap-3 font-system text-sm" },
|
|
74
|
+
React.createElement("form", { className: "flex flex-col gap-3", onSubmit: submit },
|
|
75
|
+
React.createElement("div", { className: "flex flex-col gap-1", role: "radiogroup", "aria-label": "Action" },
|
|
76
|
+
React.createElement("label", { className: "flex items-center gap-1" },
|
|
77
|
+
React.createElement("input", { type: "radio", name: "advanced-filter-action", value: "filter", checked: action === "filter", onChange: () => setAction("filter") }),
|
|
78
|
+
"Filter the list, in-place"),
|
|
79
|
+
React.createElement("label", { className: "flex items-center gap-1" },
|
|
80
|
+
React.createElement("input", { type: "radio", name: "advanced-filter-action", value: "copy", checked: action === "copy", onChange: () => setAction("copy") }),
|
|
81
|
+
"Copy to another location")),
|
|
82
|
+
React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
|
|
83
|
+
"List range:",
|
|
84
|
+
React.createElement(Input, { value: listRange, "aria-label": "List range", onChange: (e) => {
|
|
85
|
+
setListRange(e.target.value);
|
|
86
|
+
setError(undefined);
|
|
87
|
+
} })),
|
|
88
|
+
React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
|
|
89
|
+
"Criteria range:",
|
|
90
|
+
React.createElement(Input, { autoFocus: true, value: criteriaRange, "aria-label": "Criteria range", placeholder: "e.g. E1:F3", onChange: (e) => {
|
|
91
|
+
setCriteriaRange(e.target.value);
|
|
92
|
+
setError(undefined);
|
|
93
|
+
} })),
|
|
94
|
+
action === "copy" ? (React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
|
|
95
|
+
"Copy to:",
|
|
96
|
+
React.createElement(Input, { value: copyTo, "aria-label": "Copy to", placeholder: "e.g. H1", onChange: (e) => {
|
|
97
|
+
setCopyTo(e.target.value);
|
|
98
|
+
setError(undefined);
|
|
99
|
+
} }))) : null,
|
|
100
|
+
React.createElement("label", { className: "flex items-center gap-1" },
|
|
101
|
+
React.createElement("input", { type: "checkbox", name: "uniqueOnly", checked: uniqueOnly, onChange: (e) => setUniqueOnly(e.target.checked) }),
|
|
102
|
+
"Unique records only"),
|
|
103
|
+
error ? (React.createElement("div", { role: "alert", className: "text-xs text-rnc-destructive" }, error)) : null,
|
|
104
|
+
React.createElement(Separator, null),
|
|
105
|
+
React.createElement("div", { className: "flex justify-end gap-2" },
|
|
106
|
+
React.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
107
|
+
React.createElement(Button, { type: "submit", size: "sm" }, "OK")))))));
|
|
108
|
+
});
|
|
109
|
+
AdvancedFilterDialog.displayName = "AdvancedFilterDialog";
|
|
110
|
+
/**
|
|
111
|
+
* Host-mounted, atom-driven wrapper around {@link AdvancedFilterDialog}.
|
|
112
|
+
* Mount once in the host; the context menu opens it via
|
|
113
|
+
* `useOpenAdvancedFilterDialog` and only offers the item while a host is
|
|
114
|
+
* mounted.
|
|
115
|
+
*/
|
|
116
|
+
export const AdvancedFilterDialogHost = () => {
|
|
117
|
+
useRegisterAdvancedFilterDialogHost();
|
|
118
|
+
const [request, setRequest] = useAdvancedFilterDialogState();
|
|
119
|
+
const close = useCallback(() => setRequest(null), [setRequest]);
|
|
120
|
+
return (React.createElement(AdvancedFilterDialog, { open: request != null, onOpenChange: (open) => {
|
|
121
|
+
if (!open)
|
|
122
|
+
close();
|
|
123
|
+
}, initialListRange: request?.listRange ?? "", onApply: (options) => request?.onApply(options) }));
|
|
124
|
+
};
|
|
125
|
+
AdvancedFilterDialogHost.displayName = "AdvancedFilterDialogHost";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { IterativeCalculationOptions } from "../../types";
|
|
3
|
+
/** Excel's defaults (File → Options → Formulas → Calculation options). */
|
|
4
|
+
export declare const DEFAULT_ITERATIVE_CALCULATION: Required<IterativeCalculationOptions>;
|
|
5
|
+
/**
|
|
6
|
+
* Normalise a form draft the way Excel does: iterations are a whole number
|
|
7
|
+
* 1…32767, maximum change is a non-negative number; anything else falls
|
|
8
|
+
* back to the current value.
|
|
9
|
+
*/
|
|
10
|
+
export declare const normalizeIterativeCalculation: (draft: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
maxIterations: string;
|
|
13
|
+
maxChange: string;
|
|
14
|
+
}, current: IterativeCalculationOptions | undefined) => IterativeCalculationOptions;
|
|
15
|
+
export type CalculationOptionsDialogProps = {
|
|
16
|
+
open: boolean;
|
|
17
|
+
onOpenChange(open: boolean): void;
|
|
18
|
+
/** The workbook's current policy (host state / engine setting). */
|
|
19
|
+
value?: IterativeCalculationOptions | null;
|
|
20
|
+
/**
|
|
21
|
+
* Receives the new policy on OK. Legacy engine: feed it back into the
|
|
22
|
+
* `iterativeCalculation` prop of `useSpreadsheetState`; rnc engine: the
|
|
23
|
+
* `set-iterative-calculation` command.
|
|
24
|
+
*/
|
|
25
|
+
onChange(next: IterativeCalculationOptions): void;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Excel's iterative-calculation options (enable / maximum iterations /
|
|
29
|
+
* maximum change) as a dialog. Purely presentational — the host owns the
|
|
30
|
+
* setting and applies it through `onChange`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const CalculationOptionsDialog: React.MemoExoticComponent<({ open, onOpenChange, value, onChange }: CalculationOptionsDialogProps) => React.JSX.Element>;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/calculation-options-dialog/index.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAE/D,0EAA0E;AAC1E,eAAO,MAAM,6BAA6B,EAAE,QAAQ,CAAC,2BAA2B,CAK7E,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACxC,OAAO;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EACrE,SAAS,2BAA2B,GAAG,SAAS,KAC/C,2BAeF,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,KAAK,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,sEACO,6BAA6B,uBAgFxE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Button, Dialog, DialogBody, DialogCloseButton, DialogContent, DialogTitle, Input, Separator, } from "@rowsncolumns/ui";
|
|
2
|
+
import React, { memo, useState } from "react";
|
|
3
|
+
/** Excel's defaults (File → Options → Formulas → Calculation options). */
|
|
4
|
+
export const DEFAULT_ITERATIVE_CALCULATION = {
|
|
5
|
+
enabled: false,
|
|
6
|
+
maxIterations: 100,
|
|
7
|
+
maxChange: 0.001,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Normalise a form draft the way Excel does: iterations are a whole number
|
|
11
|
+
* 1…32767, maximum change is a non-negative number; anything else falls
|
|
12
|
+
* back to the current value.
|
|
13
|
+
*/
|
|
14
|
+
export const normalizeIterativeCalculation = (draft, current) => {
|
|
15
|
+
const parse = (text) => (text.trim() === "" ? NaN : Number(text));
|
|
16
|
+
const iterations = parse(draft.maxIterations);
|
|
17
|
+
const change = parse(draft.maxChange);
|
|
18
|
+
return {
|
|
19
|
+
enabled: draft.enabled,
|
|
20
|
+
maxIterations: Number.isFinite(iterations) && iterations >= 1
|
|
21
|
+
? Math.min(32767, Math.floor(iterations))
|
|
22
|
+
: current?.maxIterations ?? DEFAULT_ITERATIVE_CALCULATION.maxIterations,
|
|
23
|
+
maxChange: Number.isFinite(change) && change >= 0
|
|
24
|
+
? change
|
|
25
|
+
: current?.maxChange ?? DEFAULT_ITERATIVE_CALCULATION.maxChange,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Excel's iterative-calculation options (enable / maximum iterations /
|
|
30
|
+
* maximum change) as a dialog. Purely presentational — the host owns the
|
|
31
|
+
* setting and applies it through `onChange`.
|
|
32
|
+
*/
|
|
33
|
+
export const CalculationOptionsDialog = memo(({ open, onOpenChange, value, onChange }) => {
|
|
34
|
+
const [enabled, setEnabled] = useState(value?.enabled ?? false);
|
|
35
|
+
const [maxIterations, setMaxIterations] = useState(String(value?.maxIterations ?? DEFAULT_ITERATIVE_CALCULATION.maxIterations));
|
|
36
|
+
const [maxChange, setMaxChange] = useState(String(value?.maxChange ?? DEFAULT_ITERATIVE_CALCULATION.maxChange));
|
|
37
|
+
return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
|
|
38
|
+
React.createElement(DialogContent, { className: "w-[360px] max-w-[92vw] pointer-events-auto" },
|
|
39
|
+
React.createElement(DialogTitle, null, "Calculation options"),
|
|
40
|
+
React.createElement(DialogCloseButton, null),
|
|
41
|
+
React.createElement(DialogBody, { className: "flex flex-col gap-3" },
|
|
42
|
+
React.createElement("form", { className: "flex flex-col gap-3", onSubmit: (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
onChange(normalizeIterativeCalculation({ enabled, maxIterations, maxChange }, value ?? undefined));
|
|
45
|
+
onOpenChange(false);
|
|
46
|
+
} },
|
|
47
|
+
React.createElement("label", { className: "flex items-center gap-2 text-xs font-medium" },
|
|
48
|
+
React.createElement("input", { type: "checkbox", checked: enabled, onChange: (e) => setEnabled(e.target.checked) }),
|
|
49
|
+
"Enable iterative calculation"),
|
|
50
|
+
React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
|
|
51
|
+
"Maximum iterations:",
|
|
52
|
+
React.createElement(Input, { type: "number", min: 1, max: 32767, step: 1, value: maxIterations, disabled: !enabled, onChange: (e) => setMaxIterations(e.target.value) })),
|
|
53
|
+
React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
|
|
54
|
+
"Maximum change:",
|
|
55
|
+
React.createElement(Input, { type: "number", min: 0, step: "any", value: maxChange, disabled: !enabled, onChange: (e) => setMaxChange(e.target.value) })),
|
|
56
|
+
React.createElement(Separator, null),
|
|
57
|
+
React.createElement("div", { className: "flex justify-end gap-2" },
|
|
58
|
+
React.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
59
|
+
React.createElement(Button, { type: "submit", size: "sm" }, "OK")))))));
|
|
60
|
+
});
|
|
61
|
+
CalculationOptionsDialog.displayName = "CalculationOptionsDialog";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/canvas-grid/index.tsx"],"names":[],"mappings":"AA2CA,OAAO,KASN,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/canvas-grid/index.tsx"],"names":[],"mappings":"AA2CA,OAAO,KASN,MAAM,OAAO,CAAC;AAsLf,OAAO,KAAK,EAEV,QAAQ,EAST,MAAM,aAAa,CAAC;AAarB,OAAO,KAAK,EACV,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EAEhB,MAAM,WAAW,CAAC;AA61JnB,eAAO,MAAM,UAAU,EAA4C,CACjE,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,EAEvB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GACvB,wBAAwB,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC"}
|