@rowsncolumns/spreadsheet 10.5.17 → 10.6.4

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 (202) hide show
  1. package/dist/cjs/api/cf-data-bar.d.ts +73 -0
  2. package/dist/cjs/api/cf-data-bar.d.ts.map +1 -0
  3. package/dist/cjs/api/cf-data-bar.js +182 -0
  4. package/dist/cjs/api/cf-icons.d.ts +73 -0
  5. package/dist/cjs/api/cf-icons.d.ts.map +1 -0
  6. package/dist/cjs/api/cf-icons.js +322 -0
  7. package/dist/cjs/api/cf-overlays.d.ts +89 -3
  8. package/dist/cjs/api/cf-overlays.d.ts.map +1 -1
  9. package/dist/cjs/api/cf-overlays.js +256 -80
  10. package/dist/cjs/api/cf-priority.d.ts +34 -0
  11. package/dist/cjs/api/cf-priority.d.ts.map +1 -0
  12. package/dist/cjs/api/cf-priority.js +94 -0
  13. package/dist/cjs/api/heatmap.d.ts +7 -1
  14. package/dist/cjs/api/heatmap.d.ts.map +1 -1
  15. package/dist/cjs/api/heatmap.js +27 -11
  16. package/dist/cjs/api/selections.d.ts +2 -2
  17. package/dist/cjs/api/validation.d.ts +6 -0
  18. package/dist/cjs/api/validation.d.ts.map +1 -1
  19. package/dist/cjs/api/validation.js +10 -1
  20. package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts +8 -3
  21. package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts.map +1 -1
  22. package/dist/cjs/atoms/index.d.ts +4 -0
  23. package/dist/cjs/atoms/index.d.ts.map +1 -1
  24. package/dist/cjs/atoms/index.js +4 -0
  25. package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
  26. package/dist/cjs/components/canvas-grid/index.js +194 -51
  27. package/dist/cjs/components/canvas-grid/overflow.d.ts +37 -0
  28. package/dist/cjs/components/canvas-grid/overflow.d.ts.map +1 -0
  29. package/dist/cjs/components/canvas-grid/overflow.js +49 -0
  30. package/dist/cjs/components/canvas-grid/page-breaks.d.ts +15 -0
  31. package/dist/cjs/components/canvas-grid/page-breaks.d.ts.map +1 -0
  32. package/dist/cjs/components/canvas-grid/page-breaks.js +77 -0
  33. package/dist/cjs/components/canvas-grid/typings.d.ts +32 -3
  34. package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
  35. package/dist/cjs/components/cell/alignment.d.ts +58 -1
  36. package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
  37. package/dist/cjs/components/cell/alignment.js +144 -1
  38. package/dist/cjs/components/cell/cell-background.d.ts +2 -0
  39. package/dist/cjs/components/cell/cell-background.d.ts.map +1 -1
  40. package/dist/cjs/components/cell/cell-background.js +5 -6
  41. package/dist/cjs/components/cell/cell-backgrounds.d.ts.map +1 -1
  42. package/dist/cjs/components/cell/cell-backgrounds.js +5 -1
  43. package/dist/cjs/components/cell/cell-text-runs.js +1 -1
  44. package/dist/cjs/components/cell/index.d.ts.map +1 -1
  45. package/dist/cjs/components/cell/index.js +378 -37
  46. package/dist/cjs/components/cell/number-format-layout.d.ts +35 -0
  47. package/dist/cjs/components/cell/number-format-layout.d.ts.map +1 -0
  48. package/dist/cjs/components/cell/number-format-layout.js +106 -0
  49. package/dist/cjs/components/cell/typings.d.ts +22 -0
  50. package/dist/cjs/components/cell/typings.d.ts.map +1 -1
  51. package/dist/cjs/components/cell-editor/index.js +2 -2
  52. package/dist/cjs/components/cell-filter/index.d.ts.map +1 -1
  53. package/dist/cjs/components/cell-filter/index.js +3 -3
  54. package/dist/cjs/components/cell-overlay/index.d.ts +20 -0
  55. package/dist/cjs/components/cell-overlay/index.d.ts.map +1 -1
  56. package/dist/cjs/components/cell-overlay/index.js +155 -28
  57. package/dist/cjs/components/cf-icon-preview/index.d.ts +16 -0
  58. package/dist/cjs/components/cf-icon-preview/index.d.ts.map +1 -0
  59. package/dist/cjs/components/cf-icon-preview/index.js +48 -0
  60. package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
  61. package/dist/cjs/components/context-menu/index.js +11 -3
  62. package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
  63. package/dist/cjs/components/embeds/embed-component.js +3 -2
  64. package/dist/cjs/components/goal-seek-dialog/index.d.ts +15 -5
  65. package/dist/cjs/components/goal-seek-dialog/index.d.ts.map +1 -1
  66. package/dist/cjs/components/goal-seek-dialog/index.js +145 -42
  67. package/dist/cjs/components/hidden-markers/index.d.ts.map +1 -1
  68. package/dist/cjs/components/hidden-markers/index.js +8 -7
  69. package/dist/cjs/components/hidden-markers/style.d.ts +7 -0
  70. package/dist/cjs/components/hidden-markers/style.d.ts.map +1 -1
  71. package/dist/cjs/components/hidden-markers/style.js +13 -2
  72. package/dist/cjs/components/protect-sheet-dialog/index.d.ts +67 -0
  73. package/dist/cjs/components/protect-sheet-dialog/index.d.ts.map +1 -0
  74. package/dist/cjs/components/protect-sheet-dialog/index.js +196 -0
  75. package/dist/cjs/components/toolbar/types.d.ts +1 -1
  76. package/dist/cjs/components/toolbar/types.d.ts.map +1 -1
  77. package/dist/cjs/config.d.ts +13 -1
  78. package/dist/cjs/config.d.ts.map +1 -1
  79. package/dist/cjs/config.js +15 -3
  80. package/dist/cjs/helpers.d.ts +10 -5
  81. package/dist/cjs/helpers.d.ts.map +1 -1
  82. package/dist/cjs/helpers.js +43 -31
  83. package/dist/cjs/hooks/use-border-styles.d.ts.map +1 -1
  84. package/dist/cjs/hooks/use-border-styles.js +5 -28
  85. package/dist/cjs/hooks/use-cell-renderer.d.ts +35 -3
  86. package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
  87. package/dist/cjs/hooks/use-cell-renderer.js +85 -47
  88. package/dist/cjs/hooks/use-conditional-formatting.d.ts +13 -1
  89. package/dist/cjs/hooks/use-conditional-formatting.d.ts.map +1 -1
  90. package/dist/cjs/hooks/use-conditional-formatting.js +281 -230
  91. package/dist/cjs/index.d.ts +7 -0
  92. package/dist/cjs/index.d.ts.map +1 -1
  93. package/dist/cjs/index.js +13 -1
  94. package/dist/cjs/release-date.js +1 -1
  95. package/dist/cjs/themes.d.ts +8 -0
  96. package/dist/cjs/themes.d.ts.map +1 -1
  97. package/dist/cjs/themes.js +21 -3
  98. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  99. package/dist/cjs/types.d.ts +115 -5
  100. package/dist/cjs/types.d.ts.map +1 -1
  101. package/dist/esm/api/cf-data-bar.d.ts +73 -0
  102. package/dist/esm/api/cf-data-bar.d.ts.map +1 -0
  103. package/dist/esm/api/cf-data-bar.js +178 -0
  104. package/dist/esm/api/cf-icons.d.ts +73 -0
  105. package/dist/esm/api/cf-icons.d.ts.map +1 -0
  106. package/dist/esm/api/cf-icons.js +306 -0
  107. package/dist/esm/api/cf-overlays.d.ts +89 -3
  108. package/dist/esm/api/cf-overlays.d.ts.map +1 -1
  109. package/dist/esm/api/cf-overlays.js +223 -67
  110. package/dist/esm/api/cf-priority.d.ts +34 -0
  111. package/dist/esm/api/cf-priority.d.ts.map +1 -0
  112. package/dist/esm/api/cf-priority.js +61 -0
  113. package/dist/esm/api/heatmap.d.ts +7 -1
  114. package/dist/esm/api/heatmap.d.ts.map +1 -1
  115. package/dist/esm/api/heatmap.js +24 -9
  116. package/dist/esm/api/selections.d.ts +2 -2
  117. package/dist/esm/api/validation.d.ts +6 -0
  118. package/dist/esm/api/validation.d.ts.map +1 -1
  119. package/dist/esm/api/validation.js +6 -0
  120. package/dist/esm/atoms/goal-seek-dialog.atom.d.ts +8 -3
  121. package/dist/esm/atoms/goal-seek-dialog.atom.d.ts.map +1 -1
  122. package/dist/esm/atoms/index.d.ts +4 -0
  123. package/dist/esm/atoms/index.d.ts.map +1 -1
  124. package/dist/esm/atoms/index.js +4 -0
  125. package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
  126. package/dist/esm/components/canvas-grid/index.js +163 -47
  127. package/dist/esm/components/canvas-grid/overflow.d.ts +37 -0
  128. package/dist/esm/components/canvas-grid/overflow.d.ts.map +1 -0
  129. package/dist/esm/components/canvas-grid/overflow.js +39 -0
  130. package/dist/esm/components/canvas-grid/page-breaks.d.ts +15 -0
  131. package/dist/esm/components/canvas-grid/page-breaks.d.ts.map +1 -0
  132. package/dist/esm/components/canvas-grid/page-breaks.js +50 -0
  133. package/dist/esm/components/canvas-grid/typings.d.ts +32 -3
  134. package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
  135. package/dist/esm/components/cell/alignment.d.ts +58 -1
  136. package/dist/esm/components/cell/alignment.d.ts.map +1 -1
  137. package/dist/esm/components/cell/alignment.js +110 -1
  138. package/dist/esm/components/cell/cell-background.d.ts +2 -0
  139. package/dist/esm/components/cell/cell-background.d.ts.map +1 -1
  140. package/dist/esm/components/cell/cell-background.js +4 -5
  141. package/dist/esm/components/cell/cell-backgrounds.d.ts.map +1 -1
  142. package/dist/esm/components/cell/cell-backgrounds.js +5 -1
  143. package/dist/esm/components/cell/cell-text-runs.js +2 -2
  144. package/dist/esm/components/cell/index.d.ts.map +1 -1
  145. package/dist/esm/components/cell/index.js +339 -39
  146. package/dist/esm/components/cell/number-format-layout.d.ts +35 -0
  147. package/dist/esm/components/cell/number-format-layout.d.ts.map +1 -0
  148. package/dist/esm/components/cell/number-format-layout.js +99 -0
  149. package/dist/esm/components/cell/typings.d.ts +22 -0
  150. package/dist/esm/components/cell/typings.d.ts.map +1 -1
  151. package/dist/esm/components/cell-editor/index.js +2 -2
  152. package/dist/esm/components/cell-filter/index.d.ts.map +1 -1
  153. package/dist/esm/components/cell-filter/index.js +4 -4
  154. package/dist/esm/components/cell-overlay/index.d.ts +20 -0
  155. package/dist/esm/components/cell-overlay/index.d.ts.map +1 -1
  156. package/dist/esm/components/cell-overlay/index.js +111 -25
  157. package/dist/esm/components/cf-icon-preview/index.d.ts +16 -0
  158. package/dist/esm/components/cf-icon-preview/index.d.ts.map +1 -0
  159. package/dist/esm/components/cf-icon-preview/index.js +41 -0
  160. package/dist/esm/components/context-menu/index.d.ts.map +1 -1
  161. package/dist/esm/components/context-menu/index.js +11 -3
  162. package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
  163. package/dist/esm/components/embeds/embed-component.js +4 -3
  164. package/dist/esm/components/goal-seek-dialog/index.d.ts +15 -5
  165. package/dist/esm/components/goal-seek-dialog/index.d.ts.map +1 -1
  166. package/dist/esm/components/goal-seek-dialog/index.js +126 -29
  167. package/dist/esm/components/hidden-markers/index.d.ts.map +1 -1
  168. package/dist/esm/components/hidden-markers/index.js +9 -8
  169. package/dist/esm/components/hidden-markers/style.d.ts +7 -0
  170. package/dist/esm/components/hidden-markers/style.d.ts.map +1 -1
  171. package/dist/esm/components/hidden-markers/style.js +8 -1
  172. package/dist/esm/components/protect-sheet-dialog/index.d.ts +67 -0
  173. package/dist/esm/components/protect-sheet-dialog/index.d.ts.map +1 -0
  174. package/dist/esm/components/protect-sheet-dialog/index.js +154 -0
  175. package/dist/esm/components/toolbar/types.d.ts +1 -1
  176. package/dist/esm/components/toolbar/types.d.ts.map +1 -1
  177. package/dist/esm/config.d.ts +13 -1
  178. package/dist/esm/config.d.ts.map +1 -1
  179. package/dist/esm/config.js +13 -1
  180. package/dist/esm/helpers.d.ts +10 -5
  181. package/dist/esm/helpers.d.ts.map +1 -1
  182. package/dist/esm/helpers.js +40 -28
  183. package/dist/esm/hooks/use-border-styles.d.ts.map +1 -1
  184. package/dist/esm/hooks/use-border-styles.js +5 -26
  185. package/dist/esm/hooks/use-cell-renderer.d.ts +35 -3
  186. package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
  187. package/dist/esm/hooks/use-cell-renderer.js +83 -42
  188. package/dist/esm/hooks/use-conditional-formatting.d.ts +13 -1
  189. package/dist/esm/hooks/use-conditional-formatting.d.ts.map +1 -1
  190. package/dist/esm/hooks/use-conditional-formatting.js +215 -120
  191. package/dist/esm/index.d.ts +7 -0
  192. package/dist/esm/index.d.ts.map +1 -1
  193. package/dist/esm/index.js +7 -0
  194. package/dist/esm/release-date.js +1 -1
  195. package/dist/esm/themes.d.ts +8 -0
  196. package/dist/esm/themes.d.ts.map +1 -1
  197. package/dist/esm/themes.js +20 -3
  198. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  199. package/dist/esm/types.d.ts +115 -5
  200. package/dist/esm/types.d.ts.map +1 -1
  201. package/dist/spreadsheet.min.css +1 -1
  202. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/goal-seek-dialog/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAMtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAAG;IAChC,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;CACxC,CAAC;AAKF,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,QAAQ,cAAc,GAAG,SAAS,KACjC,MAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,8EACyB,mBAAmB,uBAyJtE,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;CAkB9B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/goal-seek-dialog/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAM/E,OAAO,KAAK,EAEV,cAAc,EACd,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,mBAAmB,GAAG;IAChC,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACvC,mEAAmE;IACnE,QAAQ,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtD,CAAC;AAKF,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,QAAQ,cAAc,GAAG,SAAS,KACjC,MAKF,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,wBAAwB,GAAI,aAAa,MAAM,WACF,CAAC;AAW3D,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,GACvC,SAAS,MAAM,EACf,QAAQ,cAAc,GAAG,SAAS,EAClC,aAAa,MAAM,EACnB,SAAS,qBAAqB,KAC7B,MASF,CAAC;AAUF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,wFACmC,mBAAmB,uBA0ShF,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;CAqB9B,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { Cross1Icon } from "@rowsncolumns/icons";
2
2
  import { Button, Dialog, DialogBody, DialogCloseButton, DialogContent, DialogDescription, DialogTitle, Input, Separator, } from "@rowsncolumns/ui";
3
3
  import { addressToCell } from "@rowsncolumns/utils";
4
- import React, { memo, useCallback, useEffect, useState } from "react";
4
+ import React, { memo, useCallback, useEffect, useMemo, useState } from "react";
5
5
  import { useGoalSeekDialogState, useRegisterGoalSeekDialogHost, } from "../../atoms/goal-seek-dialog.atom";
6
6
  const isCellReference = (text) => /^\$?[A-Za-z]{1,3}\$?\d+$/.test(text.trim()) && addressToCell(text.trim()) != null;
7
7
  /** Excel's status sentence for a finished search. */
@@ -12,32 +12,65 @@ export const goalSeekStatusText = (setCell, result) => {
12
12
  ? `Goal Seeking with Cell ${setCell} found a solution.`
13
13
  : `Goal Seeking with Cell ${setCell} may not have found a solution.`;
14
14
  };
15
+ /** A one-sided or rounded search's default tolerance: 1% of the target (0.001 for zero). */
16
+ export const defaultGoalSeekTolerance = (targetValue) => targetValue === 0 ? 0.001 : Math.abs(targetValue) * 0.01;
17
+ /** A value as the status list shows it: up to 12 significant digits, no binary noise. */
18
+ const formatGoalSeekNumber = (value) => String(Number(value.toPrecision(12)));
19
+ const MODE_LABELS = {
20
+ exact: "exactly",
21
+ atLeast: "at least",
22
+ atMost: "at most",
23
+ };
24
+ /** The status sentence of a one-sided or rounded search. */
25
+ export const goalSeekCandidatesStatusText = (setCell, result, targetValue, options) => {
26
+ const count = result?.candidates?.length ?? 0;
27
+ const goal = `${MODE_LABELS[options.mode ?? "exact"]} ${formatGoalSeekNumber(targetValue)}`;
28
+ if (count === 0) {
29
+ const within = options.tolerance ?? defaultGoalSeekTolerance(targetValue);
30
+ return `Goal Seeking with Cell ${setCell} found no value ${goal} within ${formatGoalSeekNumber(within)}.`;
31
+ }
32
+ const answers = count === 1 ? "1 value" : `${count} values`;
33
+ return `Goal Seeking with Cell ${setCell} found ${answers} ${goal}.`;
34
+ };
15
35
  /**
16
36
  * Excel's Goal Seek dialog: Set cell / To value / By changing cell, then the
17
- * Goal Seek Status sentence with target and current value.
37
+ * Goal Seek Status sentence with target and current value. Beyond Excel, the
38
+ * target can be one-sided ("at least" / "at most") and the answer rounded to a
39
+ * natural number within a tolerance: the status then lists up to three
40
+ * candidates, and the picked one is applied.
18
41
  */
19
- export const GoalSeekDialog = memo(({ open, onOpenChange, initialSetCell, onApply }) => {
42
+ export const GoalSeekDialog = memo(({ open, onOpenChange, initialSetCell, onApply, onCommit }) => {
20
43
  const [setCell, setSetCell] = useState(initialSetCell);
21
44
  const [target, setTarget] = useState("");
22
45
  const [changingCell, setChangingCell] = useState("");
46
+ const [mode, setMode] = useState("exact");
47
+ const [round, setRound] = useState(false);
48
+ const [tolerance, setTolerance] = useState("");
23
49
  const [error, setError] = useState(undefined);
24
50
  const [busy, setBusy] = useState(false);
25
- const [result, setResult] = useState(undefined);
51
+ const [outcome, setOutcome] = useState(undefined);
52
+ const [picked, setPicked] = useState(0);
26
53
  useEffect(() => {
27
54
  if (open) {
28
55
  setSetCell(initialSetCell);
29
56
  setTarget("");
30
57
  setChangingCell("");
58
+ setMode("exact");
59
+ setRound(false);
60
+ setTolerance("");
31
61
  setError(undefined);
32
62
  setBusy(false);
33
- setResult(undefined);
63
+ setOutcome(undefined);
64
+ setPicked(0);
34
65
  }
35
66
  // Only reset on open transitions.
36
67
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
68
  }, [open]);
69
+ const beyondExcel = round || mode !== "exact";
38
70
  const submit = useCallback(async (event) => {
39
71
  event.preventDefault();
40
72
  const targetValue = Number(target.trim());
73
+ const toleranceValue = Number(tolerance.trim());
41
74
  if (!isCellReference(setCell)) {
42
75
  setError("Set cell must be a single cell reference, e.g. B4.");
43
76
  return;
@@ -50,11 +83,29 @@ export const GoalSeekDialog = memo(({ open, onOpenChange, initialSetCell, onAppl
50
83
  setError("By changing cell must be a single cell reference, e.g. A1.");
51
84
  return;
52
85
  }
86
+ if (beyondExcel &&
87
+ tolerance.trim() !== "" &&
88
+ (!Number.isFinite(toleranceValue) || toleranceValue < 0)) {
89
+ setError("Within must be a positive number.");
90
+ return;
91
+ }
92
+ const options = beyondExcel
93
+ ? {
94
+ mode,
95
+ round,
96
+ ...(tolerance.trim() !== "" ? { tolerance: toleranceValue } : {}),
97
+ }
98
+ : undefined;
99
+ const set = setCell.trim().toUpperCase();
100
+ const changing = changingCell.trim().toUpperCase();
53
101
  setError(undefined);
54
102
  setBusy(true);
55
103
  try {
56
- const outcome = await onApply(setCell.trim().toUpperCase(), targetValue, changingCell.trim().toUpperCase());
57
- setResult({ setCell: setCell.trim().toUpperCase(), result: outcome });
104
+ const result = options
105
+ ? await onApply(set, targetValue, changing, options)
106
+ : await onApply(set, targetValue, changing);
107
+ setPicked(0);
108
+ setOutcome({ setCell: set, changingCell: changing, targetValue, options, result });
58
109
  }
59
110
  catch (err) {
60
111
  setError(err instanceof Error ? err.message : "Goal Seek failed.");
@@ -62,43 +113,87 @@ export const GoalSeekDialog = memo(({ open, onOpenChange, initialSetCell, onAppl
62
113
  finally {
63
114
  setBusy(false);
64
115
  }
65
- }, [setCell, target, changingCell, onApply]);
66
- return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange },
67
- React.createElement(DialogContent, { className: "w-[360px] max-w-[92vw] pointer-events-auto" },
68
- React.createElement(DialogTitle, null, result ? "Goal Seek Status" : "Goal Seek"),
69
- React.createElement(DialogDescription, null, "Find the input value that makes a formula reach a target."),
70
- React.createElement(DialogCloseButton, { onClick: () => onOpenChange(false) },
71
- React.createElement(Cross1Icon, null)),
72
- React.createElement(DialogBody, { className: "flex flex-col gap-3 font-system text-sm" }, result ? (React.createElement("div", { className: "flex flex-col gap-2", role: "status" },
73
- React.createElement("div", null, goalSeekStatusText(result.setCell, result.result)),
74
- result.result ? (React.createElement("dl", { className: "grid grid-cols-2 gap-1 text-xs" },
116
+ }, [setCell, target, changingCell, mode, round, tolerance, beyondExcel, onApply]);
117
+ const candidates = useMemo(() => (outcome?.options ? (outcome.result?.candidates ?? []) : []), [outcome]);
118
+ const applyPicked = useCallback(() => {
119
+ const candidate = candidates[picked];
120
+ if (outcome && candidate)
121
+ onCommit?.(outcome.changingCell, candidate.value);
122
+ onOpenChange(false);
123
+ }, [candidates, picked, outcome, onCommit, onOpenChange]);
124
+ const renderStatus = (current) => {
125
+ if (!current.options) {
126
+ return (React.createElement(React.Fragment, null,
127
+ React.createElement("div", null, goalSeekStatusText(current.setCell, current.result)),
128
+ current.result ? (React.createElement("dl", { className: "grid grid-cols-2 gap-1 text-xs" },
75
129
  React.createElement("dt", { className: "text-rnc-foreground/70" }, "Target value:"),
76
- React.createElement("dd", null, result.result.targetValue),
130
+ React.createElement("dd", null, current.result.targetValue),
77
131
  React.createElement("dt", { className: "text-rnc-foreground/70" }, "Current value:"),
78
- React.createElement("dd", null, result.result.currentValue === undefined
132
+ React.createElement("dd", null, current.result.currentValue === undefined
79
133
  ? "—"
80
- : String(result.result.currentValue)))) : null,
134
+ : String(current.result.currentValue)))) : null,
81
135
  React.createElement(Separator, null),
82
136
  React.createElement("div", { className: "flex justify-end gap-2" },
83
- React.createElement(Button, { size: "sm", type: "button", onClick: () => onOpenChange(false) }, "OK")))) : (React.createElement("form", { className: "flex flex-col gap-3", onSubmit: submit },
137
+ React.createElement(Button, { size: "sm", type: "button", onClick: () => onOpenChange(false) }, "OK"))));
138
+ }
139
+ return (React.createElement(React.Fragment, null,
140
+ React.createElement("div", null, goalSeekCandidatesStatusText(current.setCell, current.result, current.targetValue, current.options)),
141
+ candidates.length > 0 ? (React.createElement("fieldset", { className: "flex flex-col gap-1 min-w-0" },
142
+ React.createElement("legend", { className: "text-xs font-medium mb-1" },
143
+ "Value for ",
144
+ current.changingCell,
145
+ ":"),
146
+ candidates.map((candidate, index) => (React.createElement("label", { key: candidate.value, className: "flex items-center gap-2 text-xs py-0.5 cursor-pointer" },
147
+ React.createElement("input", { type: "radio", name: "goal-seek-candidate", value: index, checked: picked === index, onChange: () => setPicked(index) }),
148
+ React.createElement("span", null,
149
+ formatGoalSeekNumber(candidate.value),
150
+ React.createElement("span", { className: "text-rnc-foreground/70" },
151
+ " ",
152
+ "\u2192 ",
153
+ current.setCell,
154
+ " = ",
155
+ formatGoalSeekNumber(candidate.currentValue)))))))) : null,
156
+ React.createElement(Separator, null),
157
+ React.createElement("div", { className: "flex justify-end gap-2" }, candidates.length > 0 ? (React.createElement(React.Fragment, null,
158
+ React.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onOpenChange(false) }, "Cancel"),
159
+ React.createElement(Button, { size: "sm", type: "button", onClick: applyPicked }, "Apply"))) : (React.createElement(Button, { size: "sm", type: "button", onClick: () => onOpenChange(false) }, "OK")))));
160
+ };
161
+ return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange },
162
+ React.createElement(DialogContent, { className: "w-[360px] max-w-[92vw] pointer-events-auto" },
163
+ React.createElement(DialogTitle, null, outcome ? "Goal Seek Status" : "Goal Seek"),
164
+ React.createElement(DialogDescription, null, "Find the input value that makes a formula reach a target."),
165
+ React.createElement(DialogCloseButton, { onClick: () => onOpenChange(false) },
166
+ React.createElement(Cross1Icon, null)),
167
+ React.createElement(DialogBody, { className: "flex flex-col gap-3 font-system text-sm" }, outcome ? (React.createElement("div", { className: "flex flex-col gap-2", role: "status" }, renderStatus(outcome))) : (React.createElement("form", { className: "flex flex-col gap-3", onSubmit: submit },
84
168
  React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
85
169
  "Set cell:",
86
170
  React.createElement(Input, { autoFocus: true, value: setCell, "aria-label": "Set cell", onChange: (e) => {
87
171
  setSetCell(e.target.value);
88
172
  setError(undefined);
89
173
  } })),
90
- React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
174
+ React.createElement("div", { className: "flex flex-col gap-1 text-xs font-medium" },
91
175
  "To value:",
92
- React.createElement(Input, { value: target, "aria-label": "To value", inputMode: "decimal", onChange: (e) => {
93
- setTarget(e.target.value);
94
- setError(undefined);
95
- } })),
176
+ React.createElement("div", { className: "flex gap-2" },
177
+ React.createElement("select", { className: "h-8 rounded border border-rnc-border bg-transparent px-2 text-sm", value: mode, "aria-label": "Target", onChange: (e) => setMode(e.target.value) }, Object.keys(MODE_LABELS).map((key) => (React.createElement("option", { key: key, value: key }, MODE_LABELS[key])))),
178
+ React.createElement(Input, { value: target, "aria-label": "To value", inputMode: "decimal", onChange: (e) => {
179
+ setTarget(e.target.value);
180
+ setError(undefined);
181
+ } }))),
96
182
  React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
97
183
  "By changing cell:",
98
184
  React.createElement(Input, { value: changingCell, "aria-label": "By changing cell", onChange: (e) => {
99
185
  setChangingCell(e.target.value);
100
186
  setError(undefined);
101
187
  } })),
188
+ React.createElement("label", { className: "flex items-center gap-2 text-xs" },
189
+ React.createElement("input", { type: "checkbox", checked: round, onChange: (e) => setRound(e.target.checked) }),
190
+ "Round to a natural number"),
191
+ beyondExcel ? (React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
192
+ "Within:",
193
+ React.createElement(Input, { value: tolerance, "aria-label": "Within", inputMode: "decimal", placeholder: "1% of the target", onChange: (e) => {
194
+ setTolerance(e.target.value);
195
+ setError(undefined);
196
+ } }))) : null,
102
197
  error ? (React.createElement("div", { role: "alert", className: "text-xs text-rnc-destructive" }, error)) : null,
103
198
  React.createElement(Separator, null),
104
199
  React.createElement("div", { className: "flex justify-end gap-2" },
@@ -118,8 +213,10 @@ export const GoalSeekDialogHost = () => {
118
213
  return (React.createElement(GoalSeekDialog, { open: request != null, onOpenChange: (open) => {
119
214
  if (!open)
120
215
  close();
121
- }, initialSetCell: request?.setCell ?? "", onApply: (setCell, targetValue, changingCell) => request
122
- ? request.onApply(setCell, targetValue, changingCell)
123
- : Promise.resolve(undefined) }));
216
+ }, initialSetCell: request?.setCell ?? "", onApply: (setCell, targetValue, changingCell, options) => request
217
+ ? options
218
+ ? request.onApply(setCell, targetValue, changingCell, options)
219
+ : request.onApply(setCell, targetValue, changingCell)
220
+ : Promise.resolve(undefined), onCommit: (changingCell, value) => request?.onCommit?.(changingCell, value) }));
124
221
  };
125
222
  GoalSeekDialogHost.displayName = "GoalSeekDialogHost";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/hidden-markers/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,KAAK,UAAU,GAAG;IAChB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG;IAClD,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG;IAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,yDAIjC,wBAAwB,sBAyB1B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,sDAI9B,qBAAqB,sBA6BvB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/hidden-markers/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,KAAK,UAAU,GAAG;IAChB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG;IAClD,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG;IAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,yDAIjC,wBAAwB,sBA0B1B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,sDAI9B,qBAAqB,sBA8BvB,CAAC"}
@@ -1,7 +1,6 @@
1
1
  import { useGridContext } from "@rowsncolumns/grid";
2
- import { TriangleDownIcon, TriangleLeftIcon, TriangleRightIcon, TriangleUpIcon, } from "@rowsncolumns/icons";
3
2
  import React from "react";
4
- import { StyledHiddenMarkerButton } from "./style";
3
+ import { HiddenDoubleLine, StyledHiddenMarkerButton } from "./style";
5
4
  export const HiddenColumnMarkers = ({ hiddenDimensions, expandedDimensions, onShowColumn, }) => {
6
5
  const { getCellOffsetFromCoords } = useGridContext();
7
6
  const markers = hiddenDimensions.map((columnIndex, index) => {
@@ -9,12 +8,13 @@ export const HiddenColumnMarkers = ({ hiddenDimensions, expandedDimensions, onSh
9
8
  rowIndex: 0,
10
9
  columnIndex,
11
10
  });
12
- const { x = 0, y = 0 } = position ?? {};
11
+ const { x = 0, y = 0, height = 0 } = position ?? {};
12
+ // Excel marks hidden columns with a double line on the header boundary.
13
13
  return (React.createElement(StyledHiddenMarkerButton, { style: {
14
14
  transform: `translate3d(${x}px, ${y}px, 0)`,
15
+ height,
15
16
  }, key: columnIndex, onClick: () => onShowColumn?.(expandedDimensions[index]), "aria-label": "Show column", tooltip: "Show column" },
16
- React.createElement(TriangleLeftIcon, null),
17
- React.createElement(TriangleRightIcon, null)));
17
+ React.createElement(HiddenDoubleLine, null)));
18
18
  });
19
19
  return React.createElement(React.Fragment, null, markers);
20
20
  };
@@ -25,15 +25,16 @@ export const HiddenRowMarkers = ({ hiddenDimensions, expandedDimensions, onShowR
25
25
  rowIndex,
26
26
  columnIndex: 0,
27
27
  });
28
- const { x = 0, y = 0 } = position ?? {};
28
+ const { x = 0, y = 0, width = 0 } = position ?? {};
29
+ // Excel marks hidden rows with a double line on the header boundary.
29
30
  return (React.createElement(StyledHiddenMarkerButton, { style: {
30
31
  position: "absolute",
31
32
  left: x,
32
33
  top: y,
34
+ width,
33
35
  pointerEvents: "all",
34
36
  }, key: rowIndex, onClick: () => onShowRow?.(expandedDimensions[index]), isVertical: true, "aria-label": "Show row", tooltip: "Show row" },
35
- React.createElement(TriangleUpIcon, null),
36
- React.createElement(TriangleDownIcon, null)));
37
+ React.createElement(HiddenDoubleLine, { isVertical: true })));
37
38
  });
38
39
  return React.createElement(React.Fragment, null, markers);
39
40
  };
@@ -2,4 +2,11 @@ import React from "react";
2
2
  export declare const StyledHiddenMarkerButton: React.ForwardRefExoticComponent<Omit<import("@rowsncolumns/ui").IconButtonProps & React.RefAttributes<HTMLButtonElement>, "ref"> & {
3
3
  isVertical?: boolean;
4
4
  } & React.RefAttributes<HTMLButtonElement>>;
5
+ /**
6
+ * Two 1 px lines with a 1 px gap — Excel's marker for hidden rows / columns
7
+ * in the headers.
8
+ */
9
+ export declare const HiddenDoubleLine: ({ isVertical }: {
10
+ isVertical?: boolean;
11
+ }) => React.JSX.Element;
5
12
  //# sourceMappingURL=style.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../../../components/hidden-markers/style.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,eAAO,MAAM,wBAAwB;iBAEgC,OAAO;2CAY1E,CAAC"}
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../../../components/hidden-markers/style.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,eAAO,MAAM,wBAAwB;iBAEgC,OAAO;2CAY1E,CAAC;AAIH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,gBAAgB;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,sBAWxE,CAAC"}
@@ -1,4 +1,11 @@
1
1
  import { cn, IconButton } from "@rowsncolumns/ui";
2
2
  import React from "react";
3
- export const StyledHiddenMarkerButton = React.forwardRef(({ className, isVertical, ...props }, ref) => (React.createElement(IconButton, { ref: ref, className: cn("flex absolute border-none p-0 rounded-sm bg-none left-0 top-0 pointer-events-auto w-[30px] h-[15px] mt-[5px] -ml-[15px]", "bg-transparent text-rnc-foreground hover:bg-rnc-background hover:shadow-md print:hidden min-h-0 min-w-0", isVertical ? "w-[15px] h-[30px] ml-px -mt-[15px] flex-col" : undefined, className), ...props })));
3
+ export const StyledHiddenMarkerButton = React.forwardRef(({ className, isVertical, ...props }, ref) => (React.createElement(IconButton, { ref: ref, className: cn("flex absolute border-none p-0 rounded-none left-0 top-0 pointer-events-auto w-[5px] -ml-[2px] justify-center items-stretch", "bg-transparent hover:bg-transparent print:hidden min-h-0 min-w-0", isVertical ? "h-[5px] -mt-[2px] ml-0 flex-col" : undefined, className), ...props })));
4
4
  StyledHiddenMarkerButton.displayName = "StyledHiddenMarkerButton";
5
+ /**
6
+ * Two 1 px lines with a 1 px gap — Excel's marker for hidden rows / columns
7
+ * in the headers.
8
+ */
9
+ export const HiddenDoubleLine = ({ isVertical }) => (React.createElement("span", { "aria-hidden": true, "data-hidden-double-line": "", className: cn("block border-rnc-foreground/60", isVertical
10
+ ? "w-full h-[3px] border-y border-solid"
11
+ : "h-full w-[3px] border-x border-solid") }));
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import type { SheetProtection } from "../../types";
3
+ /** An OOXML `CT_SheetProtection` action flag the Protect Sheet dialog offers. */
4
+ export type SheetProtectionAllowFlag = "selectLockedCells" | "selectUnlockedCells" | "formatCells" | "formatColumns" | "formatRows" | "insertColumns" | "insertRows" | "insertHyperlinks" | "deleteColumns" | "deleteRows" | "sort" | "autoFilter" | "pivotTables" | "objects" | "scenarios";
5
+ /**
6
+ * Excel's "Allow all users of this worksheet to:" list, in the dialog's order
7
+ * (support.microsoft.com "Protect a worksheet"), with the dialog defaults:
8
+ * only selecting locked and unlocked cells is ticked.
9
+ */
10
+ export declare const SHEET_PROTECTION_ALLOW_OPTIONS: ReadonlyArray<{
11
+ flag: SheetProtectionAllowFlag;
12
+ label: string;
13
+ defaultAllowed: boolean;
14
+ }>;
15
+ export type SheetProtectionAllowState = Record<SheetProtectionAllowFlag, boolean>;
16
+ /** The dialog's ticked boxes for a sheet's last protection (or the defaults). */
17
+ export declare const sheetProtectionToAllowState: (protection?: SheetProtection | null) => SheetProtectionAllowState;
18
+ /**
19
+ * The ticked boxes → OOXML flags, where `true` PROHIBITS the action. Every
20
+ * flag is written explicitly so the stored protection never depends on a
21
+ * schema default.
22
+ */
23
+ export declare const allowStateToSheetProtection: (state: SheetProtectionAllowState) => Omit<SheetProtection, "password" | "algorithmName" | "hashValue" | "saltValue" | "spinCount">;
24
+ /** Excel's text when the re-entered password differs. */
25
+ export declare const CONFIRM_PASSWORD_MISMATCH = "Confirmation password is not identical.";
26
+ export type ProtectSheetDialogProps = {
27
+ open: boolean;
28
+ onOpenChange(open: boolean): void;
29
+ /** The sheet's previous protection, to pre-tick its choices. */
30
+ initial?: SheetProtection | null;
31
+ /**
32
+ * OK: the OOXML flags and the plaintext password (empty = none). rnc engine:
33
+ * `onProtectSheet(sheetId, { sheetProtection, password })`, which stores only
34
+ * the password's hash.
35
+ */
36
+ onConfirm(options: {
37
+ sheetProtection: ReturnType<typeof allowStateToSheetProtection>;
38
+ password?: string;
39
+ }): void;
40
+ };
41
+ /** Review ▸ Protect Sheet (support.microsoft.com "Protect a worksheet"). */
42
+ export declare const ProtectSheetDialog: React.MemoExoticComponent<({ open, onOpenChange, initial, onConfirm }: ProtectSheetDialogProps) => React.JSX.Element>;
43
+ export type ProtectWorkbookDialogProps = {
44
+ open: boolean;
45
+ onOpenChange(open: boolean): void;
46
+ /** OK: rnc engine `onProtectWorkbook({ lockStructure, password })`. */
47
+ onConfirm(options: {
48
+ lockStructure: boolean;
49
+ password?: string;
50
+ }): void;
51
+ };
52
+ /** Review ▸ Protect Workbook (support.microsoft.com "Protect a workbook"). */
53
+ export declare const ProtectWorkbookDialog: React.MemoExoticComponent<({ open, onOpenChange, onConfirm }: ProtectWorkbookDialogProps) => React.JSX.Element>;
54
+ export type UnprotectDialogProps = {
55
+ open: boolean;
56
+ onOpenChange(open: boolean): void;
57
+ /** `"Unprotect Sheet"` or `"Unprotect Workbook"`. */
58
+ title: string;
59
+ /**
60
+ * OK with the typed password. The engine verifies it and refuses a wrong one
61
+ * (`incorrect-password`, shown by the host's rejection dialog).
62
+ */
63
+ onConfirm(password: string): void;
64
+ };
65
+ /** The password prompt Excel shows for Unprotect Sheet / Unprotect Workbook. */
66
+ export declare const UnprotectDialog: React.MemoExoticComponent<({ open, onOpenChange, title, onConfirm }: UnprotectDialogProps) => React.JSX.Element>;
67
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/protect-sheet-dialog/index.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,iFAAiF;AACjF,MAAM,MAAM,wBAAwB,GAChC,mBAAmB,GACnB,qBAAqB,GACrB,aAAa,GACb,eAAe,GACf,YAAY,GACZ,eAAe,GACf,YAAY,GACZ,kBAAkB,GAClB,eAAe,GACf,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,aAAa,GACb,SAAS,GACT,WAAW,CAAC;AAEhB;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,aAAa,CAAC;IACzD,IAAI,EAAE,wBAAwB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB,CAgBA,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;AAElF,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,GACtC,aAAa,eAAe,GAAG,IAAI,KAClC,yBAOF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,yBAAyB,KAC/B,IAAI,CACL,eAAe,EACf,UAAU,GAAG,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAOvE,CAAC;AAEF,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AA4DnF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,gEAAgE;IAChE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE;QACjB,eAAe,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;QAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;CACV,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,yEACgB,uBAAuB,uBAoErE,CAAC;AAIF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,uEAAuE;IACvE,SAAS,CAAC,OAAO,EAAE;QAAE,aAAa,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACzE,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,gEACI,0BAA0B,uBAwD/D,CAAC;AAIF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,eAAe,uEACiB,oBAAoB,uBAmChE,CAAC"}
@@ -0,0 +1,154 @@
1
+ import { Button, Dialog, DialogBody, DialogCloseButton, DialogContent, DialogTitle, Input, Separator, } from "@rowsncolumns/ui";
2
+ import React, { memo, useState } from "react";
3
+ /**
4
+ * Excel's "Allow all users of this worksheet to:" list, in the dialog's order
5
+ * (support.microsoft.com "Protect a worksheet"), with the dialog defaults:
6
+ * only selecting locked and unlocked cells is ticked.
7
+ */
8
+ export const SHEET_PROTECTION_ALLOW_OPTIONS = [
9
+ { flag: "selectLockedCells", label: "Select locked cells", defaultAllowed: true },
10
+ { flag: "selectUnlockedCells", label: "Select unlocked cells", defaultAllowed: true },
11
+ { flag: "formatCells", label: "Format cells", defaultAllowed: false },
12
+ { flag: "formatColumns", label: "Format columns", defaultAllowed: false },
13
+ { flag: "formatRows", label: "Format rows", defaultAllowed: false },
14
+ { flag: "insertColumns", label: "Insert columns", defaultAllowed: false },
15
+ { flag: "insertRows", label: "Insert rows", defaultAllowed: false },
16
+ { flag: "insertHyperlinks", label: "Insert hyperlinks", defaultAllowed: false },
17
+ { flag: "deleteColumns", label: "Delete columns", defaultAllowed: false },
18
+ { flag: "deleteRows", label: "Delete rows", defaultAllowed: false },
19
+ { flag: "sort", label: "Sort", defaultAllowed: false },
20
+ { flag: "autoFilter", label: "Use AutoFilter", defaultAllowed: false },
21
+ { flag: "pivotTables", label: "Use PivotTable & PivotChart", defaultAllowed: false },
22
+ { flag: "objects", label: "Edit objects", defaultAllowed: false },
23
+ { flag: "scenarios", label: "Edit scenarios", defaultAllowed: false },
24
+ ];
25
+ /** The dialog's ticked boxes for a sheet's last protection (or the defaults). */
26
+ export const sheetProtectionToAllowState = (protection) => {
27
+ const state = {};
28
+ for (const { flag, defaultAllowed } of SHEET_PROTECTION_ALLOW_OPTIONS) {
29
+ const prohibited = protection?.[flag];
30
+ state[flag] = typeof prohibited === "boolean" ? !prohibited : defaultAllowed;
31
+ }
32
+ return state;
33
+ };
34
+ /**
35
+ * The ticked boxes → OOXML flags, where `true` PROHIBITS the action. Every
36
+ * flag is written explicitly so the stored protection never depends on a
37
+ * schema default.
38
+ */
39
+ export const allowStateToSheetProtection = (state) => {
40
+ const protection = { sheet: true };
41
+ for (const { flag } of SHEET_PROTECTION_ALLOW_OPTIONS) {
42
+ protection[flag] = !state[flag];
43
+ }
44
+ return protection;
45
+ };
46
+ /** Excel's text when the re-entered password differs. */
47
+ export const CONFIRM_PASSWORD_MISMATCH = "Confirmation password is not identical.";
48
+ /** Password + "Reenter password to proceed", as Excel's Confirm Password step. */
49
+ const PasswordFields = ({ label, password, confirm, mismatch, onPassword, onConfirm, }) => (React.createElement(React.Fragment, null,
50
+ React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
51
+ label,
52
+ React.createElement(Input, { type: "password", autoComplete: "new-password", value: password, onChange: (e) => onPassword(e.target.value), "data-testid": "protection-password" })),
53
+ password !== "" && (React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
54
+ "Reenter password to proceed:",
55
+ React.createElement(Input, { type: "password", autoComplete: "new-password", value: confirm, onChange: (e) => onConfirm(e.target.value), "data-testid": "protection-password-confirm" }))),
56
+ mismatch && (React.createElement("p", { role: "alert", className: "text-xs text-red-600" }, CONFIRM_PASSWORD_MISMATCH))));
57
+ const DialogButtons = ({ onCancel }) => (React.createElement("div", { className: "flex justify-end gap-2" },
58
+ React.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: onCancel }, "Cancel"),
59
+ React.createElement(Button, { type: "submit", size: "sm" }, "OK")));
60
+ /** Review ▸ Protect Sheet (support.microsoft.com "Protect a worksheet"). */
61
+ export const ProtectSheetDialog = memo(({ open, onOpenChange, initial, onConfirm }) => {
62
+ const [allow, setAllow] = useState(() => sheetProtectionToAllowState(initial));
63
+ const [password, setPassword] = useState("");
64
+ const [confirm, setConfirm] = useState("");
65
+ const [mismatch, setMismatch] = useState(false);
66
+ return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
67
+ React.createElement(DialogContent, { className: "w-[360px] max-w-[92vw] pointer-events-auto" },
68
+ React.createElement(DialogTitle, null, "Protect Sheet"),
69
+ React.createElement(DialogCloseButton, null),
70
+ React.createElement(DialogBody, null,
71
+ React.createElement("form", { className: "flex flex-col gap-3", onSubmit: (e) => {
72
+ e.preventDefault();
73
+ if (password !== "" && password !== confirm) {
74
+ setMismatch(true);
75
+ return;
76
+ }
77
+ onConfirm({
78
+ sheetProtection: allowStateToSheetProtection(allow),
79
+ ...(password ? { password } : {}),
80
+ });
81
+ onOpenChange(false);
82
+ } },
83
+ React.createElement(PasswordFields, { label: "Password to unprotect sheet:", password: password, confirm: confirm, mismatch: mismatch, onPassword: (v) => {
84
+ setPassword(v);
85
+ setMismatch(false);
86
+ }, onConfirm: (v) => {
87
+ setConfirm(v);
88
+ setMismatch(false);
89
+ } }),
90
+ React.createElement("fieldset", { className: "flex flex-col gap-1" },
91
+ React.createElement("legend", { className: "mb-1 text-xs font-medium" }, "Allow all users of this worksheet to:"),
92
+ React.createElement("div", { className: "flex max-h-56 flex-col gap-1 overflow-y-auto rounded border p-2" }, SHEET_PROTECTION_ALLOW_OPTIONS.map(({ flag, label }) => (React.createElement("label", { key: flag, className: "flex items-center gap-2 text-xs" },
93
+ React.createElement("input", { type: "checkbox", checked: allow[flag], "data-testid": `protect-allow-${flag}`, onChange: (e) => setAllow((prev) => ({ ...prev, [flag]: e.target.checked })) }),
94
+ label))))),
95
+ React.createElement(Separator, null),
96
+ React.createElement(DialogButtons, { onCancel: () => onOpenChange(false) }))))));
97
+ });
98
+ ProtectSheetDialog.displayName = "ProtectSheetDialog";
99
+ /** Review ▸ Protect Workbook (support.microsoft.com "Protect a workbook"). */
100
+ export const ProtectWorkbookDialog = memo(({ open, onOpenChange, onConfirm }) => {
101
+ const [structure, setStructure] = useState(true);
102
+ const [password, setPassword] = useState("");
103
+ const [confirm, setConfirm] = useState("");
104
+ const [mismatch, setMismatch] = useState(false);
105
+ return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
106
+ React.createElement(DialogContent, { className: "w-[340px] max-w-[92vw] pointer-events-auto" },
107
+ React.createElement(DialogTitle, null, "Protect Structure and Windows"),
108
+ React.createElement(DialogCloseButton, null),
109
+ React.createElement(DialogBody, null,
110
+ React.createElement("form", { className: "flex flex-col gap-3", onSubmit: (e) => {
111
+ e.preventDefault();
112
+ if (password !== "" && password !== confirm) {
113
+ setMismatch(true);
114
+ return;
115
+ }
116
+ if (structure) {
117
+ onConfirm({ lockStructure: true, ...(password ? { password } : {}) });
118
+ }
119
+ onOpenChange(false);
120
+ } },
121
+ React.createElement("label", { className: "flex items-center gap-2 text-xs font-medium" },
122
+ React.createElement("input", { type: "checkbox", checked: structure, "data-testid": "protect-workbook-structure", onChange: (e) => setStructure(e.target.checked) }),
123
+ "Structure"),
124
+ React.createElement(PasswordFields, { label: "Password (optional):", password: password, confirm: confirm, mismatch: mismatch, onPassword: (v) => {
125
+ setPassword(v);
126
+ setMismatch(false);
127
+ }, onConfirm: (v) => {
128
+ setConfirm(v);
129
+ setMismatch(false);
130
+ } }),
131
+ React.createElement(Separator, null),
132
+ React.createElement(DialogButtons, { onCancel: () => onOpenChange(false) }))))));
133
+ });
134
+ ProtectWorkbookDialog.displayName = "ProtectWorkbookDialog";
135
+ /** The password prompt Excel shows for Unprotect Sheet / Unprotect Workbook. */
136
+ export const UnprotectDialog = memo(({ open, onOpenChange, title, onConfirm }) => {
137
+ const [password, setPassword] = useState("");
138
+ return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
139
+ React.createElement(DialogContent, { className: "w-[320px] max-w-[92vw] pointer-events-auto" },
140
+ React.createElement(DialogTitle, null, title),
141
+ React.createElement(DialogCloseButton, null),
142
+ React.createElement(DialogBody, null,
143
+ React.createElement("form", { className: "flex flex-col gap-3", onSubmit: (e) => {
144
+ e.preventDefault();
145
+ onConfirm(password);
146
+ onOpenChange(false);
147
+ } },
148
+ React.createElement("label", { className: "flex flex-col gap-1 text-xs font-medium" },
149
+ "Password:",
150
+ React.createElement(Input, { type: "password", autoComplete: "current-password", autoFocus: true, value: password, onChange: (e) => setPassword(e.target.value), "data-testid": "unprotect-password" })),
151
+ React.createElement(Separator, null),
152
+ React.createElement(DialogButtons, { onCancel: () => onOpenChange(false) }))))));
153
+ });
154
+ UnprotectDialog.displayName = "UnprotectDialog";
@@ -1,2 +1,2 @@
1
- export type BorderLocation = "all" | "outer" | "inner" | "vertical" | "horizontal" | "left" | "right" | "bottom" | "top";
1
+ export type BorderLocation = "all" | "outer" | "inner" | "vertical" | "horizontal" | "left" | "right" | "bottom" | "top" | "diagonalUp" | "diagonalDown" | "none";
2
2
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,OAAO,GACP,OAAO,GACP,UAAU,GACV,YAAY,GACZ,MAAM,GACN,OAAO,GACP,QAAQ,GACR,KAAK,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,OAAO,GACP,OAAO,GACP,UAAU,GACV,YAAY,GACZ,MAAM,GACN,OAAO,GACP,QAAQ,GACR,KAAK,GAGL,YAAY,GACZ,cAAc,GACd,MAAM,CAAC"}
@@ -19,7 +19,19 @@ export declare const COLUMN_HEADER_HEIGHT = 24;
19
19
  export declare const OUTLINE_GUTTER_LEVEL_PX = 17;
20
20
  export declare const OUTLINE_GUTTER_PAD_PX = 4;
21
21
  export declare const DEFAULT_CELL_SPACING = 2.5;
22
- export declare const INDENT_SPACING = 4;
22
+ /**
23
+ * One indent level (Format Cells ▸ Alignment ▸ Indent) at 100 % zoom. ECMA-376
24
+ * §18.8.1 defines a level as three spaces of the Normal font — 3 × 3.3 px for
25
+ * 11 pt Calibri at 96 dpi. Callers multiply it by the zoom scale.
26
+ */
27
+ export declare const INDENT_SPACING = 10;
28
+ /**
29
+ * Excel's AutoFilter / table header button: a light grey square with a grey
30
+ * border and a dark grey glyph.
31
+ */
32
+ export declare const FILTER_BUTTON_GLYPH_COLOR = "#444444";
33
+ export declare const FILTER_BUTTON_BORDER_COLOR = "#ABABAB";
34
+ export declare const FILTER_BUTTON_FILL_COLOR = "#F3F3F3";
23
35
  export declare const MIN_COLUMN_WIDTH = 24;
24
36
  export declare const CELL_VERTICAL_ALIGNMENT = "bottom";
25
37
  export declare const CELL_HORIZONTAL_ALIGNMENT = "left";
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}