@rowsncolumns/spreadsheet 10.2.73 → 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.
Files changed (270) hide show
  1. package/dist/cjs/api/evaluate-formula.d.ts +17 -0
  2. package/dist/cjs/api/evaluate-formula.d.ts.map +1 -0
  3. package/dist/cjs/api/evaluate-formula.js +141 -0
  4. package/dist/cjs/api/excel-validation-kinds.d.ts +71 -0
  5. package/dist/cjs/api/excel-validation-kinds.d.ts.map +1 -0
  6. package/dist/cjs/api/excel-validation-kinds.js +304 -0
  7. package/dist/cjs/api/go-to-special.d.ts +32 -0
  8. package/dist/cjs/api/go-to-special.d.ts.map +1 -0
  9. package/dist/cjs/api/go-to-special.js +111 -0
  10. package/dist/cjs/api/go-to.d.ts +19 -0
  11. package/dist/cjs/api/go-to.d.ts.map +1 -0
  12. package/dist/cjs/api/go-to.js +49 -0
  13. package/dist/cjs/api/validation-tooltip.d.ts +17 -0
  14. package/dist/cjs/api/validation-tooltip.d.ts.map +1 -0
  15. package/dist/cjs/api/validation-tooltip.js +42 -0
  16. package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts +28 -0
  17. package/dist/cjs/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
  18. package/dist/cjs/atoms/advanced-filter-dialog.atom.js +32 -0
  19. package/dist/cjs/atoms/evaluate-formula-dialog.atom.d.ts +36 -0
  20. package/dist/cjs/atoms/evaluate-formula-dialog.atom.d.ts.map +1 -0
  21. package/dist/cjs/atoms/evaluate-formula-dialog.atom.js +36 -0
  22. package/dist/cjs/atoms/go-to-dialog.atom.d.ts +46 -0
  23. package/dist/cjs/atoms/go-to-dialog.atom.d.ts.map +1 -0
  24. package/dist/cjs/atoms/go-to-dialog.atom.js +34 -0
  25. package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts +29 -0
  26. package/dist/cjs/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
  27. package/dist/cjs/atoms/goal-seek-dialog.atom.js +30 -0
  28. package/dist/cjs/atoms/index.d.ts +5 -0
  29. package/dist/cjs/atoms/index.d.ts.map +1 -1
  30. package/dist/cjs/atoms/index.js +5 -0
  31. package/dist/cjs/atoms/paste-special-dialog.atom.d.ts +29 -0
  32. package/dist/cjs/atoms/paste-special-dialog.atom.d.ts.map +1 -0
  33. package/dist/cjs/atoms/paste-special-dialog.atom.js +34 -0
  34. package/dist/cjs/atoms/remove-duplicates-dialog.atom.d.ts +45 -0
  35. package/dist/cjs/atoms/remove-duplicates-dialog.atom.d.ts.map +1 -0
  36. package/dist/cjs/atoms/remove-duplicates-dialog.atom.js +37 -0
  37. package/dist/cjs/atoms/subtotal-dialog.atom.d.ts +39 -0
  38. package/dist/cjs/atoms/subtotal-dialog.atom.d.ts.map +1 -0
  39. package/dist/cjs/atoms/subtotal-dialog.atom.js +30 -0
  40. package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts +34 -0
  41. package/dist/cjs/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
  42. package/dist/cjs/atoms/text-to-columns-dialog.atom.js +36 -0
  43. package/dist/cjs/atoms/watch-window.atom.d.ts +37 -0
  44. package/dist/cjs/atoms/watch-window.atom.d.ts.map +1 -0
  45. package/dist/cjs/atoms/watch-window.atom.js +61 -0
  46. package/dist/cjs/components/advanced-filter-dialog/index.d.ts +28 -0
  47. package/dist/cjs/components/advanced-filter-dialog/index.d.ts.map +1 -0
  48. package/dist/cjs/components/advanced-filter-dialog/index.js +134 -0
  49. package/dist/cjs/components/calculation-options-dialog/index.d.ts +33 -0
  50. package/dist/cjs/components/calculation-options-dialog/index.d.ts.map +1 -0
  51. package/dist/cjs/components/calculation-options-dialog/index.js +69 -0
  52. package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
  53. package/dist/cjs/components/canvas-grid/index.js +208 -19
  54. package/dist/cjs/components/canvas-grid/typings.d.ts +72 -9
  55. package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
  56. package/dist/cjs/components/cell/alignment.d.ts +30 -0
  57. package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
  58. package/dist/cjs/components/cell/alignment.js +56 -1
  59. package/dist/cjs/components/cell/index.d.ts.map +1 -1
  60. package/dist/cjs/components/cell/index.js +15 -7
  61. package/dist/cjs/components/cell-diff/index.d.ts +3 -2
  62. package/dist/cjs/components/cell-diff/index.d.ts.map +1 -1
  63. package/dist/cjs/components/cell-editor/index.d.ts.map +1 -1
  64. package/dist/cjs/components/cell-editor/index.js +2 -1
  65. package/dist/cjs/components/context-menu/index.d.ts +21 -3
  66. package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
  67. package/dist/cjs/components/context-menu/index.js +256 -6
  68. package/dist/cjs/components/context-menu/total-row-functions.d.ts +32 -0
  69. package/dist/cjs/components/context-menu/total-row-functions.d.ts.map +1 -0
  70. package/dist/cjs/components/context-menu/total-row-functions.js +62 -0
  71. package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
  72. package/dist/cjs/components/embeds/embed-component.js +16 -1
  73. package/dist/cjs/components/evaluate-formula-dialog/index.d.ts +34 -0
  74. package/dist/cjs/components/evaluate-formula-dialog/index.d.ts.map +1 -0
  75. package/dist/cjs/components/evaluate-formula-dialog/index.js +106 -0
  76. package/dist/cjs/components/go-to-dialog/index.d.ts +45 -0
  77. package/dist/cjs/components/go-to-dialog/index.d.ts.map +1 -0
  78. package/dist/cjs/components/go-to-dialog/index.js +136 -0
  79. package/dist/cjs/components/goal-seek-dialog/index.d.ts +32 -0
  80. package/dist/cjs/components/goal-seek-dialog/index.d.ts.map +1 -0
  81. package/dist/cjs/components/goal-seek-dialog/index.js +149 -0
  82. package/dist/cjs/components/paste-special-dialog/index.d.ts +29 -0
  83. package/dist/cjs/components/paste-special-dialog/index.d.ts.map +1 -0
  84. package/dist/cjs/components/paste-special-dialog/index.js +99 -0
  85. package/dist/cjs/components/remove-duplicates-dialog/index.d.ts +37 -0
  86. package/dist/cjs/components/remove-duplicates-dialog/index.d.ts.map +1 -0
  87. package/dist/cjs/components/remove-duplicates-dialog/index.js +91 -0
  88. package/dist/cjs/components/subtotal-dialog/index.d.ts +30 -0
  89. package/dist/cjs/components/subtotal-dialog/index.d.ts.map +1 -0
  90. package/dist/cjs/components/subtotal-dialog/index.js +126 -0
  91. package/dist/cjs/components/text-to-columns-dialog/index.d.ts +37 -0
  92. package/dist/cjs/components/text-to-columns-dialog/index.d.ts.map +1 -0
  93. package/dist/cjs/components/text-to-columns-dialog/index.js +166 -0
  94. package/dist/cjs/components/toolbar/background-color-selector.d.ts +13 -1
  95. package/dist/cjs/components/toolbar/background-color-selector.d.ts.map +1 -1
  96. package/dist/cjs/components/toolbar/background-color-selector.js +57 -11
  97. package/dist/cjs/components/toolbar/cell-style-selector.d.ts +15 -2
  98. package/dist/cjs/components/toolbar/cell-style-selector.d.ts.map +1 -1
  99. package/dist/cjs/components/toolbar/cell-style-selector.js +56 -3
  100. package/dist/cjs/components/toolbar/fill-selectors.d.ts +49 -0
  101. package/dist/cjs/components/toolbar/fill-selectors.d.ts.map +1 -0
  102. package/dist/cjs/components/toolbar/fill-selectors.js +196 -0
  103. package/dist/cjs/components/toolbar/index.d.ts +2 -0
  104. package/dist/cjs/components/toolbar/index.d.ts.map +1 -1
  105. package/dist/cjs/components/toolbar/index.js +2 -0
  106. package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
  107. package/dist/cjs/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
  108. package/dist/cjs/components/toolbar/text-horizontal-align-selector.js +23 -4
  109. package/dist/cjs/components/toolbar/text-rotation-selector.d.ts +29 -0
  110. package/dist/cjs/components/toolbar/text-rotation-selector.d.ts.map +1 -0
  111. package/dist/cjs/components/toolbar/text-rotation-selector.js +78 -0
  112. package/dist/cjs/components/toolbar/text-wrap-selector.d.ts +7 -1
  113. package/dist/cjs/components/toolbar/text-wrap-selector.d.ts.map +1 -1
  114. package/dist/cjs/components/toolbar/text-wrap-selector.js +10 -3
  115. package/dist/cjs/components/watch-window/index.d.ts +25 -0
  116. package/dist/cjs/components/watch-window/index.d.ts.map +1 -0
  117. package/dist/cjs/components/watch-window/index.js +63 -0
  118. package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
  119. package/dist/cjs/hooks/use-cell-renderer.js +17 -6
  120. package/dist/cjs/hooks/use-copy-paste-cut.d.ts +20 -1
  121. package/dist/cjs/hooks/use-copy-paste-cut.d.ts.map +1 -1
  122. package/dist/cjs/hooks/use-copy-paste-cut.js +25 -20
  123. package/dist/cjs/hooks/use-get-tooltip.d.ts.map +1 -1
  124. package/dist/cjs/hooks/use-get-tooltip.js +7 -2
  125. package/dist/cjs/hooks/use-keyboard-handler.d.ts +17 -2
  126. package/dist/cjs/hooks/use-keyboard-handler.d.ts.map +1 -1
  127. package/dist/cjs/hooks/use-keyboard-handler.js +206 -31
  128. package/dist/cjs/index.d.ts +14 -1
  129. package/dist/cjs/index.d.ts.map +1 -1
  130. package/dist/cjs/index.js +15 -1
  131. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  132. package/dist/cjs/types.d.ts +289 -4
  133. package/dist/cjs/types.d.ts.map +1 -1
  134. package/dist/cjs/types.js +19 -1
  135. package/dist/esm/api/evaluate-formula.d.ts +17 -0
  136. package/dist/esm/api/evaluate-formula.d.ts.map +1 -0
  137. package/dist/esm/api/evaluate-formula.js +109 -0
  138. package/dist/esm/api/excel-validation-kinds.d.ts +71 -0
  139. package/dist/esm/api/excel-validation-kinds.d.ts.map +1 -0
  140. package/dist/esm/api/excel-validation-kinds.js +287 -0
  141. package/dist/esm/api/go-to-special.d.ts +32 -0
  142. package/dist/esm/api/go-to-special.d.ts.map +1 -0
  143. package/dist/esm/api/go-to-special.js +96 -0
  144. package/dist/esm/api/go-to.d.ts +19 -0
  145. package/dist/esm/api/go-to.d.ts.map +1 -0
  146. package/dist/esm/api/go-to.js +41 -0
  147. package/dist/esm/api/validation-tooltip.d.ts +17 -0
  148. package/dist/esm/api/validation-tooltip.d.ts.map +1 -0
  149. package/dist/esm/api/validation-tooltip.js +34 -0
  150. package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts +28 -0
  151. package/dist/esm/atoms/advanced-filter-dialog.atom.d.ts.map +1 -0
  152. package/dist/esm/atoms/advanced-filter-dialog.atom.js +21 -0
  153. package/dist/esm/atoms/evaluate-formula-dialog.atom.d.ts +36 -0
  154. package/dist/esm/atoms/evaluate-formula-dialog.atom.d.ts.map +1 -0
  155. package/dist/esm/atoms/evaluate-formula-dialog.atom.js +25 -0
  156. package/dist/esm/atoms/go-to-dialog.atom.d.ts +46 -0
  157. package/dist/esm/atoms/go-to-dialog.atom.d.ts.map +1 -0
  158. package/dist/esm/atoms/go-to-dialog.atom.js +25 -0
  159. package/dist/esm/atoms/goal-seek-dialog.atom.d.ts +29 -0
  160. package/dist/esm/atoms/goal-seek-dialog.atom.d.ts.map +1 -0
  161. package/dist/esm/atoms/goal-seek-dialog.atom.js +21 -0
  162. package/dist/esm/atoms/index.d.ts +5 -0
  163. package/dist/esm/atoms/index.d.ts.map +1 -1
  164. package/dist/esm/atoms/index.js +5 -0
  165. package/dist/esm/atoms/paste-special-dialog.atom.d.ts +29 -0
  166. package/dist/esm/atoms/paste-special-dialog.atom.d.ts.map +1 -0
  167. package/dist/esm/atoms/paste-special-dialog.atom.js +25 -0
  168. package/dist/esm/atoms/remove-duplicates-dialog.atom.d.ts +45 -0
  169. package/dist/esm/atoms/remove-duplicates-dialog.atom.d.ts.map +1 -0
  170. package/dist/esm/atoms/remove-duplicates-dialog.atom.js +26 -0
  171. package/dist/esm/atoms/subtotal-dialog.atom.d.ts +39 -0
  172. package/dist/esm/atoms/subtotal-dialog.atom.d.ts.map +1 -0
  173. package/dist/esm/atoms/subtotal-dialog.atom.js +21 -0
  174. package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts +34 -0
  175. package/dist/esm/atoms/text-to-columns-dialog.atom.d.ts.map +1 -0
  176. package/dist/esm/atoms/text-to-columns-dialog.atom.js +25 -0
  177. package/dist/esm/atoms/watch-window.atom.d.ts +37 -0
  178. package/dist/esm/atoms/watch-window.atom.d.ts.map +1 -0
  179. package/dist/esm/atoms/watch-window.atom.js +37 -0
  180. package/dist/esm/components/advanced-filter-dialog/index.d.ts +28 -0
  181. package/dist/esm/components/advanced-filter-dialog/index.d.ts.map +1 -0
  182. package/dist/esm/components/advanced-filter-dialog/index.js +125 -0
  183. package/dist/esm/components/calculation-options-dialog/index.d.ts +33 -0
  184. package/dist/esm/components/calculation-options-dialog/index.d.ts.map +1 -0
  185. package/dist/esm/components/calculation-options-dialog/index.js +61 -0
  186. package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
  187. package/dist/esm/components/canvas-grid/index.js +202 -19
  188. package/dist/esm/components/canvas-grid/typings.d.ts +72 -9
  189. package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
  190. package/dist/esm/components/cell/alignment.d.ts +30 -0
  191. package/dist/esm/components/cell/alignment.d.ts.map +1 -1
  192. package/dist/esm/components/cell/alignment.js +51 -0
  193. package/dist/esm/components/cell/index.d.ts.map +1 -1
  194. package/dist/esm/components/cell/index.js +16 -8
  195. package/dist/esm/components/cell-diff/index.d.ts +3 -2
  196. package/dist/esm/components/cell-diff/index.d.ts.map +1 -1
  197. package/dist/esm/components/cell-editor/index.d.ts.map +1 -1
  198. package/dist/esm/components/cell-editor/index.js +2 -1
  199. package/dist/esm/components/context-menu/index.d.ts +21 -3
  200. package/dist/esm/components/context-menu/index.d.ts.map +1 -1
  201. package/dist/esm/components/context-menu/index.js +251 -9
  202. package/dist/esm/components/context-menu/total-row-functions.d.ts +32 -0
  203. package/dist/esm/components/context-menu/total-row-functions.d.ts.map +1 -0
  204. package/dist/esm/components/context-menu/total-row-functions.js +50 -0
  205. package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
  206. package/dist/esm/components/embeds/embed-component.js +17 -1
  207. package/dist/esm/components/evaluate-formula-dialog/index.d.ts +34 -0
  208. package/dist/esm/components/evaluate-formula-dialog/index.d.ts.map +1 -0
  209. package/dist/esm/components/evaluate-formula-dialog/index.js +86 -0
  210. package/dist/esm/components/go-to-dialog/index.d.ts +45 -0
  211. package/dist/esm/components/go-to-dialog/index.d.ts.map +1 -0
  212. package/dist/esm/components/go-to-dialog/index.js +124 -0
  213. package/dist/esm/components/goal-seek-dialog/index.d.ts +32 -0
  214. package/dist/esm/components/goal-seek-dialog/index.d.ts.map +1 -0
  215. package/dist/esm/components/goal-seek-dialog/index.js +125 -0
  216. package/dist/esm/components/paste-special-dialog/index.d.ts +29 -0
  217. package/dist/esm/components/paste-special-dialog/index.d.ts.map +1 -0
  218. package/dist/esm/components/paste-special-dialog/index.js +90 -0
  219. package/dist/esm/components/remove-duplicates-dialog/index.d.ts +37 -0
  220. package/dist/esm/components/remove-duplicates-dialog/index.d.ts.map +1 -0
  221. package/dist/esm/components/remove-duplicates-dialog/index.js +82 -0
  222. package/dist/esm/components/subtotal-dialog/index.d.ts +30 -0
  223. package/dist/esm/components/subtotal-dialog/index.d.ts.map +1 -0
  224. package/dist/esm/components/subtotal-dialog/index.js +115 -0
  225. package/dist/esm/components/text-to-columns-dialog/index.d.ts +37 -0
  226. package/dist/esm/components/text-to-columns-dialog/index.d.ts.map +1 -0
  227. package/dist/esm/components/text-to-columns-dialog/index.js +147 -0
  228. package/dist/esm/components/toolbar/background-color-selector.d.ts +13 -1
  229. package/dist/esm/components/toolbar/background-color-selector.d.ts.map +1 -1
  230. package/dist/esm/components/toolbar/background-color-selector.js +61 -11
  231. package/dist/esm/components/toolbar/cell-style-selector.d.ts +15 -2
  232. package/dist/esm/components/toolbar/cell-style-selector.d.ts.map +1 -1
  233. package/dist/esm/components/toolbar/cell-style-selector.js +46 -2
  234. package/dist/esm/components/toolbar/fill-selectors.d.ts +49 -0
  235. package/dist/esm/components/toolbar/fill-selectors.d.ts.map +1 -0
  236. package/dist/esm/components/toolbar/fill-selectors.js +162 -0
  237. package/dist/esm/components/toolbar/index.d.ts +2 -0
  238. package/dist/esm/components/toolbar/index.d.ts.map +1 -1
  239. package/dist/esm/components/toolbar/index.js +2 -0
  240. package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts +8 -0
  241. package/dist/esm/components/toolbar/text-horizontal-align-selector.d.ts.map +1 -1
  242. package/dist/esm/components/toolbar/text-horizontal-align-selector.js +21 -5
  243. package/dist/esm/components/toolbar/text-rotation-selector.d.ts +29 -0
  244. package/dist/esm/components/toolbar/text-rotation-selector.d.ts.map +1 -0
  245. package/dist/esm/components/toolbar/text-rotation-selector.js +71 -0
  246. package/dist/esm/components/toolbar/text-wrap-selector.d.ts +7 -1
  247. package/dist/esm/components/toolbar/text-wrap-selector.d.ts.map +1 -1
  248. package/dist/esm/components/toolbar/text-wrap-selector.js +10 -3
  249. package/dist/esm/components/watch-window/index.d.ts +25 -0
  250. package/dist/esm/components/watch-window/index.d.ts.map +1 -0
  251. package/dist/esm/components/watch-window/index.js +54 -0
  252. package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
  253. package/dist/esm/hooks/use-cell-renderer.js +17 -6
  254. package/dist/esm/hooks/use-copy-paste-cut.d.ts +20 -1
  255. package/dist/esm/hooks/use-copy-paste-cut.d.ts.map +1 -1
  256. package/dist/esm/hooks/use-copy-paste-cut.js +8 -3
  257. package/dist/esm/hooks/use-get-tooltip.d.ts.map +1 -1
  258. package/dist/esm/hooks/use-get-tooltip.js +7 -2
  259. package/dist/esm/hooks/use-keyboard-handler.d.ts +17 -2
  260. package/dist/esm/hooks/use-keyboard-handler.d.ts.map +1 -1
  261. package/dist/esm/hooks/use-keyboard-handler.js +193 -31
  262. package/dist/esm/index.d.ts +14 -1
  263. package/dist/esm/index.d.ts.map +1 -1
  264. package/dist/esm/index.js +13 -0
  265. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  266. package/dist/esm/types.d.ts +289 -4
  267. package/dist/esm/types.d.ts.map +1 -1
  268. package/dist/esm/types.js +18 -0
  269. package/dist/spreadsheet.min.css +1 -1
  270. package/package.json +6 -6
@@ -0,0 +1,162 @@
1
+ import { Button, ColorPicker, HexColorPicker, Stack } from "@rowsncolumns/ui";
2
+ import React, { useMemo, useState } from "react";
3
+ import { getPatternCanvas } from "../../api/fill-patterns";
4
+ import { getStringifiedColor, STANDARD_COLORS } from "../../themes";
5
+ /** Excel's Format Cells → Fill → Pattern Style menu, in Excel's order. */
6
+ export const FILL_PATTERN_OPTIONS = [
7
+ { pattern: "gray0625", label: "6.25% Gray" },
8
+ { pattern: "gray125", label: "12.5% Gray" },
9
+ { pattern: "lightGray", label: "25% Gray" },
10
+ { pattern: "mediumGray", label: "50% Gray" },
11
+ { pattern: "darkGray", label: "75% Gray" },
12
+ { pattern: "darkHorizontal", label: "Horizontal Stripe" },
13
+ { pattern: "darkVertical", label: "Vertical Stripe" },
14
+ { pattern: "darkDown", label: "Reverse Diagonal Stripe" },
15
+ { pattern: "darkUp", label: "Diagonal Stripe" },
16
+ { pattern: "darkGrid", label: "Diagonal Crosshatch" },
17
+ { pattern: "darkTrellis", label: "Thick Diagonal Crosshatch" },
18
+ { pattern: "lightHorizontal", label: "Thin Horizontal Stripe" },
19
+ { pattern: "lightVertical", label: "Thin Vertical Stripe" },
20
+ { pattern: "lightDown", label: "Thin Reverse Diagonal Stripe" },
21
+ { pattern: "lightUp", label: "Thin Diagonal Stripe" },
22
+ { pattern: "lightGrid", label: "Thin Horizontal Crosshatch" },
23
+ { pattern: "lightTrellis", label: "Thin Diagonal Crosshatch" },
24
+ ];
25
+ /**
26
+ * Excel's Fill Effects → Shading styles. `degree` follows OOXML
27
+ * `<gradientFill degree>`: 0 runs left → right, 90 top → bottom.
28
+ */
29
+ export const GRADIENT_PRESETS = [
30
+ { label: "Horizontal", gradient: { type: "linear", degree: 90 } },
31
+ { label: "Vertical", gradient: { type: "linear", degree: 0 } },
32
+ { label: "Diagonal up", gradient: { type: "linear", degree: 45 } },
33
+ { label: "Diagonal down", gradient: { type: "linear", degree: 135 } },
34
+ {
35
+ label: "From center",
36
+ gradient: { type: "path", left: 0.5, right: 0.5, top: 0.5, bottom: 0.5 },
37
+ },
38
+ ];
39
+ /** Two-stop gradient from a preset and two colours. */
40
+ export const buildGradientFill = (preset, color1, color2) => ({
41
+ ...preset,
42
+ stops: [
43
+ { position: 0, color: color1 },
44
+ { position: 1, color: color2 },
45
+ ],
46
+ });
47
+ /**
48
+ * CSS preview of a gradient fill. OOXML measures the linear angle from the
49
+ * left→right axis; CSS from the bottom→top axis, so CSS = degree + 90.
50
+ */
51
+ export const gradientToCss = (gradient) => {
52
+ const stops = gradient.stops
53
+ .slice()
54
+ .sort((a, b) => a.position - b.position)
55
+ .map((stop) => `${stop.color} ${Math.round(stop.position * 100)}%`)
56
+ .join(", ");
57
+ if (gradient.type === "path") {
58
+ return `radial-gradient(circle, ${stops})`;
59
+ }
60
+ return `linear-gradient(${((gradient.degree ?? 0) + 90) % 360}deg, ${stops})`;
61
+ };
62
+ const DEFAULT_PATTERN_COLOR = "#000000";
63
+ const PatternSwatch = ({ pattern, foregroundColor, backgroundColor, label, }) => {
64
+ const url = useMemo(() => {
65
+ const canvas = getPatternCanvas(pattern, foregroundColor, backgroundColor);
66
+ try {
67
+ return canvas?.toDataURL() ?? undefined;
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ }, [pattern, foregroundColor, backgroundColor]);
73
+ return (React.createElement("span", { "aria-hidden": "true", className: "block w-full h-full border border-solid border-rnc-border", style: url
74
+ ? { backgroundImage: `url(${url})`, backgroundRepeat: "repeat" }
75
+ : { backgroundColor }, title: label }));
76
+ };
77
+ /**
78
+ * Pattern fill authoring (Excel: Format Cells → Fill → Pattern Color /
79
+ * Pattern Style). The pattern paints in `foregroundColor` over the solid
80
+ * `backgroundColor`; a gradient on the cell is cleared, as Excel treats the
81
+ * two as exclusive.
82
+ */
83
+ export const PatternFillPicker = ({ fill, theme, onChangeFill, }) => {
84
+ const background = fill?.backgroundColor
85
+ ? getStringifiedColor(fill.backgroundColor, theme)
86
+ : undefined;
87
+ const patternColor = fill?.foregroundColor
88
+ ? getStringifiedColor(fill.foregroundColor, theme)
89
+ : DEFAULT_PATTERN_COLOR;
90
+ const activePattern = fill?.fillPattern &&
91
+ fill.fillPattern !== "none" &&
92
+ fill.fillPattern !== "solid"
93
+ ? fill.fillPattern
94
+ : undefined;
95
+ const apply = (pattern, foregroundColor) => onChangeFill({
96
+ backgroundColor: fill?.backgroundColor ?? undefined,
97
+ fillPattern: pattern,
98
+ foregroundColor: pattern ? foregroundColor : undefined,
99
+ gradient: undefined,
100
+ });
101
+ return (React.createElement(Stack, { direction: "vertical", className: "gap-1" },
102
+ React.createElement(Button, { variant: "outline", size: "xs", tabIndex: -1, className: "justify-center py-1", onClick: () => apply(undefined, undefined) }, "No pattern"),
103
+ React.createElement("div", { className: "pt-2 text-xs text-left" }, "Pattern style"),
104
+ React.createElement("div", { role: "group", "aria-label": "Pattern style", className: "grid grid-cols-6 gap-1" }, FILL_PATTERN_OPTIONS.map(({ pattern, label }) => (React.createElement("button", { type: "button", key: pattern, "aria-label": label, "aria-pressed": activePattern === pattern, className: `w-6 h-6 p-0 border-2 border-solid rounded-sm bg-transparent cursor-pointer ${activePattern === pattern
105
+ ? "border-rnc-primary"
106
+ : "border-transparent"}`, onClick: () => apply(pattern, fill?.foregroundColor ? patternColor : undefined) },
107
+ React.createElement(PatternSwatch, { pattern: pattern, foregroundColor: patternColor, backgroundColor: background, label: label }))))),
108
+ React.createElement("div", { className: "pt-2 text-xs text-left" }, "Pattern color"),
109
+ React.createElement(ColorPicker, { value: patternColor, colors: STANDARD_COLORS, onChange: (color) => apply(activePattern ?? "darkGray", color), className: "gap-1" }),
110
+ React.createElement("div", { className: "flex items-center gap-1" },
111
+ React.createElement(HexColorPicker, { value: patternColor, onChange: (color) => apply(activePattern ?? "darkGray", color) }))));
112
+ };
113
+ /**
114
+ * Gradient fill authoring (Excel: Fill Effects → Gradient, two colours +
115
+ * shading style). Picking a style applies immediately with the two colours;
116
+ * changing a colour re-applies when the cell already has a gradient.
117
+ */
118
+ export const GradientFillPicker = ({ fill, theme, onChangeFill, }) => {
119
+ const existing = fill?.gradient ?? undefined;
120
+ const [color1, setColor1] = useState(() => existing?.stops?.[0]?.color ??
121
+ (fill?.backgroundColor
122
+ ? getStringifiedColor(fill.backgroundColor, theme)
123
+ : "#FFFFFF"));
124
+ const [color2, setColor2] = useState(() => existing?.stops?.[existing.stops.length - 1]?.color ?? "#4472C4");
125
+ const apply = (preset, c1 = color1, c2 = color2) => onChangeFill({
126
+ backgroundColor: undefined,
127
+ fillPattern: undefined,
128
+ foregroundColor: undefined,
129
+ gradient: buildGradientFill(preset, c1, c2),
130
+ });
131
+ const reapply = (c1, c2) => {
132
+ if (!existing)
133
+ return;
134
+ const { stops: _stops, ...preset } = existing;
135
+ apply(preset, c1, c2);
136
+ };
137
+ const isActive = (preset) => !!existing &&
138
+ existing.type === preset.type &&
139
+ (preset.type === "path" || (existing.degree ?? 0) === (preset.degree ?? 0));
140
+ return (React.createElement(Stack, { direction: "vertical", className: "gap-1" },
141
+ React.createElement(Button, { variant: "outline", size: "xs", tabIndex: -1, className: "justify-center py-1", onClick: () => onChangeFill({
142
+ backgroundColor: fill?.backgroundColor ?? undefined,
143
+ fillPattern: fill?.fillPattern ?? undefined,
144
+ foregroundColor: fill?.foregroundColor ?? undefined,
145
+ gradient: undefined,
146
+ }) }, "No gradient"),
147
+ React.createElement("div", { className: "grid grid-cols-[auto_1fr] items-center gap-2 pt-2 text-xs" },
148
+ React.createElement("span", null, "Color 1"),
149
+ React.createElement(HexColorPicker, { value: color1, onChange: (color) => {
150
+ setColor1(color);
151
+ reapply(color, color2);
152
+ } }),
153
+ React.createElement("span", null, "Color 2"),
154
+ React.createElement(HexColorPicker, { value: color2, onChange: (color) => {
155
+ setColor2(color);
156
+ reapply(color1, color);
157
+ } })),
158
+ React.createElement("div", { className: "pt-2 text-xs text-left" }, "Shading style"),
159
+ React.createElement("div", { role: "group", "aria-label": "Shading style", className: "grid grid-cols-5 gap-1" }, GRADIENT_PRESETS.map(({ label, gradient }) => (React.createElement("button", { type: "button", key: label, "aria-label": label, "aria-pressed": isActive(gradient), className: `w-8 h-6 p-0 border-2 border-solid rounded-sm cursor-pointer ${isActive(gradient) ? "border-rnc-primary" : "border-rnc-border"}`, style: {
160
+ backgroundImage: gradientToCss(buildGradientFill(gradient, color1, color2)),
161
+ }, onClick: () => apply(gradient) }))))));
162
+ };
@@ -4,12 +4,14 @@ export * from "./scale-selector";
4
4
  export * from "./font-size-selector";
5
5
  export * from "./text-color-selector";
6
6
  export * from "./background-color-selector";
7
+ export * from "./fill-selectors";
7
8
  export * from "./merge-cells-selector";
8
9
  export * from "./border-selector";
9
10
  export * from "./font-family-selector";
10
11
  export * from "./text-horizontal-align-selector";
11
12
  export * from "./text-vertical-align-selector";
12
13
  export * from "./text-wrap-selector";
14
+ export * from "./text-rotation-selector";
13
15
  export * from "./text-format-selector";
14
16
  export * from "./theme-selector";
15
17
  export * from "./table-actions";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/index.tsx"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/index.tsx"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
@@ -4,12 +4,14 @@ export * from "./scale-selector";
4
4
  export * from "./font-size-selector";
5
5
  export * from "./text-color-selector";
6
6
  export * from "./background-color-selector";
7
+ export * from "./fill-selectors";
7
8
  export * from "./merge-cells-selector";
8
9
  export * from "./border-selector";
9
10
  export * from "./font-family-selector";
10
11
  export * from "./text-horizontal-align-selector";
11
12
  export * from "./text-vertical-align-selector";
12
13
  export * from "./text-wrap-selector";
14
+ export * from "./text-rotation-selector";
13
15
  export * from "./text-format-selector";
14
16
  export * from "./theme-selector";
15
17
  export * from "./table-actions";
@@ -5,6 +5,14 @@ type TextHorizontalAlignSelectorProps = {
5
5
  value: HorizontalAlign | undefined | null;
6
6
  onChange(value: HorizontalAlign): void;
7
7
  };
8
+ /**
9
+ * Excel's remaining Format Cells → Alignment → Horizontal entries. They have
10
+ * no toolbar glyph, so they render as labelled buttons under the icon row.
11
+ */
12
+ export declare const EXTENDED_HORIZONTAL_ALIGNMENTS: ReadonlyArray<{
13
+ value: HorizontalAlign;
14
+ label: string;
15
+ }>;
8
16
  export declare const TextHorizontalAlignSelector: ({ value, onChange, disabled, }: TextHorizontalAlignSelectorProps) => React.JSX.Element;
9
17
  export {};
10
18
  //# sourceMappingURL=text-horizontal-align-selector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-horizontal-align-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-horizontal-align-selector.tsx"],"names":[],"mappings":"AASA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,gCAAgC,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACxC,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAI,gCAIzC,gCAAgC,sBA0DlC,CAAC"}
1
+ {"version":3,"file":"text-horizontal-align-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-horizontal-align-selector.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,gCAAgC,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,aAAa,CAAC;IACzD,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,CAGA,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAI,gCAIzC,gCAAgC,sBA6FlC,CAAC"}
@@ -1,17 +1,30 @@
1
- import { MdFormatAlignCenter, MdFormatAlignLeft, MdFormatAlignRight, MdOutlineArrowDropDown, } from "@rowsncolumns/icons";
2
- import { IconButton } from "@rowsncolumns/ui";
1
+ import { MdFormatAlignCenter, MdFormatAlignJustify, MdFormatAlignLeft, MdFormatAlignRight, MdOutlineArrowDropDown, } from "@rowsncolumns/icons";
2
+ import { Button, IconButton } from "@rowsncolumns/ui";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "@rowsncolumns/ui";
4
4
  import { Stack } from "@rowsncolumns/ui";
5
5
  import React, { useState } from "react";
6
6
  import { useFocusSheet } from "../../atoms";
7
+ /**
8
+ * Excel's remaining Format Cells → Alignment → Horizontal entries. They have
9
+ * no toolbar glyph, so they render as labelled buttons under the icon row.
10
+ */
11
+ export const EXTENDED_HORIZONTAL_ALIGNMENTS = [
12
+ { value: "distributed", label: "Distributed" },
13
+ { value: "fill", label: "Fill" },
14
+ ];
7
15
  export const TextHorizontalAlignSelector = ({ value = "left", onChange, disabled, }) => {
8
16
  const focusSheet = useFocusSheet();
9
17
  const [isOpen, onOpenChange] = useState(false);
10
18
  const close = () => onOpenChange(false);
19
+ const select = (next) => {
20
+ onChange(next);
21
+ focusSheet?.();
22
+ close();
23
+ };
11
24
  return (React.createElement(Popover, { open: isOpen, onOpenChange: onOpenChange },
12
25
  React.createElement(PopoverTrigger, { asChild: true },
13
- React.createElement(IconButton, { size: "lg", tooltip: "Horizontal align", disabled: disabled },
14
- value === "right" ? (React.createElement(MdFormatAlignRight, null)) : value === "center" ? (React.createElement(MdFormatAlignCenter, null)) : (React.createElement(MdFormatAlignLeft, null)),
26
+ React.createElement(IconButton, { size: "lg", tooltip: "Horizontal align", "aria-label": "Horizontal align", disabled: disabled },
27
+ value === "right" ? (React.createElement(MdFormatAlignRight, null)) : value === "center" || value === "centerContinuous" ? (React.createElement(MdFormatAlignCenter, null)) : value === "justify" || value === "distributed" ? (React.createElement(MdFormatAlignJustify, null)) : (React.createElement(MdFormatAlignLeft, null)),
15
28
  React.createElement(MdOutlineArrowDropDown, null))),
16
29
  React.createElement(PopoverContent, { align: "start" },
17
30
  React.createElement(Stack, { direction: "horizontal", className: "gap-1 max-w-[140px] flex-wrap" },
@@ -32,5 +45,8 @@ export const TextHorizontalAlignSelector = ({ value = "left", onChange, disabled
32
45
  focusSheet?.();
33
46
  close();
34
47
  } },
35
- React.createElement(MdFormatAlignRight, null))))));
48
+ React.createElement(MdFormatAlignRight, null)),
49
+ React.createElement(IconButton, { size: "lg", tooltip: "Justify", "aria-label": "Justify", isActive: value === "justify", onClick: () => select("justify") },
50
+ React.createElement(MdFormatAlignJustify, null))),
51
+ React.createElement(Stack, { direction: "horizontal", className: "gap-1 pt-1 max-w-[140px]" }, EXTENDED_HORIZONTAL_ALIGNMENTS.map(({ value: option, label }) => (React.createElement(Button, { key: option, type: "button", variant: value === option ? "secondary" : "ghost", size: "xs", "aria-pressed": value === option, onClick: () => select(option) }, label)))))));
36
52
  };
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import type { CellFormat } from "../../types";
3
+ export type TextRotationValue = NonNullable<CellFormat["textRotation"]>;
4
+ type TextRotationPreset = {
5
+ label: string;
6
+ value: TextRotationValue;
7
+ };
8
+ /**
9
+ * Excel's Orientation menu. Degrees are counter-clockwise positive (Excel's
10
+ * convention, what the renderer and the xlsx round-trip use), so "clockwise"
11
+ * presets are negative. `0` is horizontal — used instead of clearing the
12
+ * property so both engines apply it as a plain format write.
13
+ */
14
+ export declare const TEXT_ROTATION_PRESETS: TextRotationPreset[];
15
+ export declare const MIN_TEXT_ROTATION = -90;
16
+ export declare const MAX_TEXT_ROTATION = 90;
17
+ /**
18
+ * Coerce free-form degree input into the supported -90…90 range. Returns
19
+ * `undefined` for input that is not a finite number.
20
+ */
21
+ export declare const clampTextRotation: (input: string | number) => number | undefined;
22
+ type TextRotationSelectorProps = {
23
+ disabled?: boolean;
24
+ value?: CellFormat["textRotation"];
25
+ onChange(value: TextRotationValue): void;
26
+ };
27
+ export declare const TextRotationSelector: ({ value, onChange, disabled, }: TextRotationSelectorProps) => React.JSX.Element;
28
+ export {};
29
+ //# sourceMappingURL=text-rotation-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-rotation-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-rotation-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;AAExE,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,iBAAiB,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,EAOrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,GAAG,MAAM,uBAMvD,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,gCAIlC,yBAAyB,sBAoF3B,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { MdOutlineArrowDropDown, RotateCounterClockwiseIcon, } from "@rowsncolumns/icons";
2
+ import { Button, IconButton, Input, Popover, PopoverContent, PopoverTrigger, Stack, } from "@rowsncolumns/ui";
3
+ import React, { useEffect, useState } from "react";
4
+ import { useFocusSheet } from "../../atoms";
5
+ /**
6
+ * Excel's Orientation menu. Degrees are counter-clockwise positive (Excel's
7
+ * convention, what the renderer and the xlsx round-trip use), so "clockwise"
8
+ * presets are negative. `0` is horizontal — used instead of clearing the
9
+ * property so both engines apply it as a plain format write.
10
+ */
11
+ export const TEXT_ROTATION_PRESETS = [
12
+ { label: "Horizontal", value: 0 },
13
+ { label: "Angle counterclockwise", value: 45 },
14
+ { label: "Angle clockwise", value: -45 },
15
+ { label: "Vertical text", value: "vertical" },
16
+ { label: "Rotate text up", value: 90 },
17
+ { label: "Rotate text down", value: -90 },
18
+ ];
19
+ export const MIN_TEXT_ROTATION = -90;
20
+ export const MAX_TEXT_ROTATION = 90;
21
+ /**
22
+ * Coerce free-form degree input into the supported -90…90 range. Returns
23
+ * `undefined` for input that is not a finite number.
24
+ */
25
+ export const clampTextRotation = (input) => {
26
+ const value = typeof input === "number" ? input : Number(input);
27
+ if (!Number.isFinite(value)) {
28
+ return undefined;
29
+ }
30
+ return Math.max(MIN_TEXT_ROTATION, Math.min(MAX_TEXT_ROTATION, value));
31
+ };
32
+ export const TextRotationSelector = ({ value, onChange, disabled, }) => {
33
+ const focusSheet = useFocusSheet();
34
+ const [isOpen, onOpenChange] = useState(false);
35
+ const current = value ?? 0;
36
+ const [degrees, setDegrees] = useState(typeof current === "number" ? String(current) : "");
37
+ // Reflect the active cell's rotation in the custom-degrees field whenever
38
+ // the popover opens on a different value.
39
+ useEffect(() => {
40
+ if (isOpen) {
41
+ setDegrees(typeof current === "number" ? String(current) : "");
42
+ }
43
+ }, [isOpen, current]);
44
+ const close = () => onOpenChange(false);
45
+ const apply = (next) => {
46
+ onChange(next);
47
+ focusSheet?.();
48
+ close();
49
+ };
50
+ const applyDegrees = () => {
51
+ const next = clampTextRotation(degrees);
52
+ if (next !== undefined) {
53
+ apply(next);
54
+ }
55
+ };
56
+ return (React.createElement(Popover, { open: isOpen, onOpenChange: onOpenChange },
57
+ React.createElement(PopoverTrigger, { asChild: true },
58
+ React.createElement(IconButton, { size: "lg", tooltip: "Text orientation", "aria-label": "Text orientation", disabled: disabled },
59
+ React.createElement(RotateCounterClockwiseIcon, null),
60
+ React.createElement(MdOutlineArrowDropDown, null))),
61
+ React.createElement(PopoverContent, { align: "start" },
62
+ React.createElement(Stack, { direction: "vertical", className: "gap-1 min-w-[200px]" },
63
+ TEXT_ROTATION_PRESETS.map((preset) => (React.createElement(Button, { key: preset.label, size: "xs", variant: current === preset.value ? "primary" : "ghost", className: "justify-start", "aria-pressed": current === preset.value, onClick: () => apply(preset.value) }, preset.label))),
64
+ React.createElement("form", { className: "flex items-center gap-1 pt-1 m-0", onSubmit: (e) => {
65
+ e.preventDefault();
66
+ applyDegrees();
67
+ } },
68
+ React.createElement("label", { className: "font-system text-xs shrink-0", htmlFor: "rnc-text-rotation-degrees" }, "Degrees"),
69
+ React.createElement(Input, { id: "rnc-text-rotation-degrees", type: "number", min: MIN_TEXT_ROTATION, max: MAX_TEXT_ROTATION, step: 1, value: degrees, onChange: (e) => setDegrees(e.target.value), className: "w-[72px]" }),
70
+ React.createElement(Button, { size: "xs", type: "submit" }, "Apply"))))));
71
+ };
@@ -3,7 +3,13 @@ import type { WrapStrategy } from "../../types";
3
3
  type TextWrapSelectorProps = {
4
4
  value?: WrapStrategy | null;
5
5
  onChange(value: WrapStrategy): void;
6
+ /**
7
+ * Excel's "Shrink to fit": scale the font down so the text fits the cell.
8
+ * The toggle only renders when `onChangeShrinkToFit` is provided.
9
+ */
10
+ shrinkToFit?: boolean | null;
11
+ onChangeShrinkToFit?(value: boolean): void;
6
12
  };
7
- export declare const TextWrapSelector: ({ value, onChange, }: TextWrapSelectorProps) => React.JSX.Element;
13
+ export declare const TextWrapSelector: ({ value, onChange, shrinkToFit, onChangeShrinkToFit, }: TextWrapSelectorProps) => React.JSX.Element;
8
14
  export {};
9
15
  //# sourceMappingURL=text-wrap-selector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-wrap-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-wrap-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,qBAAqB,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,sBAG9B,qBAAqB,sBA8DvB,CAAC"}
1
+ {"version":3,"file":"text-wrap-selector.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/text-wrap-selector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,qBAAqB,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,mBAAmB,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,wDAK9B,qBAAqB,sBAoFvB,CAAC"}
@@ -3,13 +3,13 @@ import { MdOutlineArrowDropDown } from "@rowsncolumns/icons";
3
3
  import { IconButton, Popover, PopoverContent, PopoverTrigger, Stack, } from "@rowsncolumns/ui";
4
4
  import React, { useState } from "react";
5
5
  import { useFocusSheet } from "../../atoms";
6
- export const TextWrapSelector = ({ value = "overflow", onChange, }) => {
6
+ export const TextWrapSelector = ({ value = "overflow", onChange, shrinkToFit, onChangeShrinkToFit, }) => {
7
7
  const focusSheet = useFocusSheet();
8
8
  const [isOpen, onOpenChange] = useState(false);
9
9
  const close = () => onOpenChange(false);
10
10
  return (React.createElement(Popover, { open: isOpen, onOpenChange: onOpenChange },
11
11
  React.createElement(PopoverTrigger, { asChild: true },
12
- React.createElement(IconButton, { size: "lg", tooltip: "Text wrapping" },
12
+ React.createElement(IconButton, { size: "lg", tooltip: "Text wrapping", "aria-label": "Text wrapping" },
13
13
  value === "overflow" ? (React.createElement(TextOverflowIcon, null)) : value === "wrap" ? (React.createElement(TextOverflowWrapIcon, null)) : (React.createElement(TextoverflowClipIcon, null)),
14
14
  React.createElement(MdOutlineArrowDropDown, null))),
15
15
  React.createElement(PopoverContent, { align: "start" },
@@ -31,5 +31,12 @@ export const TextWrapSelector = ({ value = "overflow", onChange, }) => {
31
31
  focusSheet?.();
32
32
  close();
33
33
  } },
34
- React.createElement(TextoverflowClipIcon, null))))));
34
+ React.createElement(TextoverflowClipIcon, null)),
35
+ onChangeShrinkToFit ? (React.createElement(IconButton, { size: "lg", isActive: !!shrinkToFit, "aria-pressed": !!shrinkToFit, tooltip: "Shrink text to fit the cell", "aria-label": "Shrink text to fit the cell", className: "w-full justify-start gap-1", onClick: () => {
36
+ onChangeShrinkToFit(!shrinkToFit);
37
+ focusSheet?.();
38
+ close();
39
+ } },
40
+ React.createElement(TextoverflowClipIcon, null),
41
+ React.createElement("span", { className: "text-xs" }, "Shrink to fit"))) : null))));
35
42
  };
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import { type WatchWindowContext, type WatchWindowEntry } from "../../atoms/watch-window.atom";
3
+ import type { SheetCoordinate } from "../../types";
4
+ export type WatchWindowProps = {
5
+ entries: WatchWindowEntry[];
6
+ context: WatchWindowContext | null;
7
+ onRemove(cell: SheetCoordinate): void;
8
+ onClose(): void;
9
+ };
10
+ /**
11
+ * Excel's Watch Window: a floating list of cells with their sheet, address,
12
+ * current value and formula, refreshed with the grid. Double-click a row to
13
+ * jump to the cell. Presentational; {@link WatchWindowHost} wires the atoms.
14
+ */
15
+ export declare const WatchWindow: React.MemoExoticComponent<({ entries, context, onRemove, onClose }: WatchWindowProps) => React.JSX.Element>;
16
+ /**
17
+ * Host-mounted, atom-driven Watch Window. Mount once alongside the other
18
+ * dialogs; the grid's context menu offers "Add to Watch Window" while a
19
+ * host is mounted and republishes live getters on each render.
20
+ */
21
+ export declare const WatchWindowHost: {
22
+ (): React.JSX.Element | null;
23
+ displayName: string;
24
+ };
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/watch-window/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,sEACoB,gBAAgB,uBA6E3D,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;CAgB3B,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { Button, IconButton } from "@rowsncolumns/ui";
2
+ import { Cross1Icon } from "@rowsncolumns/icons";
3
+ import { cellToAddress } from "@rowsncolumns/utils";
4
+ import React, { memo, useCallback } from "react";
5
+ import { useRegisterWatchWindowHost, useRemoveWatch, useWatchWindowContext, useWatchWindowEntries, useWatchWindowOpen, } from "../../atoms/watch-window.atom";
6
+ /**
7
+ * Excel's Watch Window: a floating list of cells with their sheet, address,
8
+ * current value and formula, refreshed with the grid. Double-click a row to
9
+ * jump to the cell. Presentational; {@link WatchWindowHost} wires the atoms.
10
+ */
11
+ export const WatchWindow = memo(({ entries, context, onRemove, onClose }) => (React.createElement("div", { role: "dialog", "aria-label": "Watch Window", className: "fixed bottom-4 right-4 z-50 w-[420px] max-w-[92vw] rounded-md border border-rnc-border bg-rnc-background text-rnc-foreground shadow-lg pointer-events-auto" },
12
+ React.createElement("div", { className: "flex items-center justify-between px-3 py-2 border-b border-rnc-border" },
13
+ React.createElement("span", { className: "text-sm font-medium" }, "Watch Window"),
14
+ React.createElement(IconButton, { "aria-label": "Close Watch Window", onClick: onClose },
15
+ React.createElement(Cross1Icon, null))),
16
+ entries.length === 0 ? (React.createElement("div", { className: "px-3 py-4 text-xs text-rnc-foreground/60" }, "No cells are being watched. Right-click a cell \u2192 Add to Watch Window.")) : (React.createElement("table", { className: "w-full text-xs border-collapse" },
17
+ React.createElement("thead", null,
18
+ React.createElement("tr", { className: "text-left text-rnc-foreground/70" },
19
+ React.createElement("th", { className: "px-3 py-1 font-medium" }, "Sheet"),
20
+ React.createElement("th", { className: "px-2 py-1 font-medium" }, "Cell"),
21
+ React.createElement("th", { className: "px-2 py-1 font-medium" }, "Value"),
22
+ React.createElement("th", { className: "px-2 py-1 font-medium" }, "Formula"),
23
+ React.createElement("th", { className: "px-2 py-1" }))),
24
+ React.createElement("tbody", null, entries.map((entry) => {
25
+ const address = cellToAddress(entry) ?? "";
26
+ const sheetName = context?.getSheetName?.(entry.sheetId) ?? String(entry.sheetId);
27
+ const value = context?.getFormattedValue(entry.sheetId, entry.rowIndex, entry.columnIndex);
28
+ const formula = context?.getFormula(entry.sheetId, entry.rowIndex, entry.columnIndex);
29
+ return (React.createElement("tr", { key: `${entry.sheetId}:${entry.rowIndex}:${entry.columnIndex}`, className: "cursor-default hover:bg-rnc-accent", onDoubleClick: () => context?.onNavigate(entry), "data-testid": "watch-row" },
30
+ React.createElement("td", { className: "px-3 py-1 truncate max-w-[110px]" }, sheetName),
31
+ React.createElement("td", { className: "px-2 py-1 font-mono" }, address),
32
+ React.createElement("td", { className: "px-2 py-1 truncate max-w-[120px]" }, value ?? ""),
33
+ React.createElement("td", { className: "px-2 py-1 font-mono truncate max-w-[140px]" }, formula ?? ""),
34
+ React.createElement("td", { className: "px-2 py-1 text-right" },
35
+ React.createElement(Button, { variant: "ghost", size: "xs", "aria-label": `Remove watch ${sheetName}!${address}`, onClick: () => onRemove(entry) }, "Delete"))));
36
+ })))))));
37
+ WatchWindow.displayName = "WatchWindow";
38
+ /**
39
+ * Host-mounted, atom-driven Watch Window. Mount once alongside the other
40
+ * dialogs; the grid's context menu offers "Add to Watch Window" while a
41
+ * host is mounted and republishes live getters on each render.
42
+ */
43
+ export const WatchWindowHost = () => {
44
+ useRegisterWatchWindowHost();
45
+ const [entries] = useWatchWindowEntries();
46
+ const [open, setOpen] = useWatchWindowOpen();
47
+ const context = useWatchWindowContext();
48
+ const removeWatch = useRemoveWatch();
49
+ const close = useCallback(() => setOpen(false), [setOpen]);
50
+ if (!open)
51
+ return null;
52
+ return (React.createElement(WatchWindow, { entries: entries, context: context, onRemove: removeWatch, onClose: close }));
53
+ };
54
+ WatchWindowHost.displayName = "WatchWindowHost";
@@ -1 +1 @@
1
- {"version":3,"file":"use-cell-renderer.d.ts","sourceRoot":"","sources":["../../../hooks/use-cell-renderer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-cell-renderer.d.ts","sourceRoot":"","sources":["../../../hooks/use-cell-renderer.tsx"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { isNil } from "@rowsncolumns/utils";
3
3
  import React, { useCallback } from "react";
4
4
  import { getHeaderFill } from "../api/colors";
5
5
  import { showValidationDropdownArrow } from "../api/validation";
6
+ import { alignmentImpliesWrap, isFillAlignment, } from "../components/cell/alignment";
6
7
  import { CellBackground } from "../components/cell/cell-background";
7
8
  import { CellDiff as CellDiffRenderer } from "../components/cell-diff";
8
9
  import { CellOverlayRenderer } from "../components/cell-overlay";
@@ -50,10 +51,17 @@ export const useCellRenderer = ({ sheetId, scale, showGridLines, showZeros, isRo
50
51
  // Calculated column
51
52
  const isCalculatedColumn = isTableHeader(rowIndex, columnIndex) &&
52
53
  isCalculatedColumnHeader(rowIndex, columnIndex);
53
- // Cell overflow
54
+ // Cell overflow. Excel's justify / distributed alignments wrap inside
55
+ // the cell's own width even without `wrapStrategy: "wrap"`, and fill
56
+ // repeats the text to the cell edge — none of them ever spill into
57
+ // the neighbouring cells.
54
58
  const wrapStrategy = effectiveCellFormat?.wrapStrategy;
55
- const shouldWrap = wrapStrategy === "wrap";
56
- const isWrapEnabled = (wrapStrategy ?? "overflow") === "overflow";
59
+ const wrapsByAlignment = alignmentImpliesWrap(horizontalAlignment);
60
+ const fillsCell = isFillAlignment(horizontalAlignment);
61
+ const shouldWrap = wrapStrategy === "wrap" || wrapsByAlignment;
62
+ const isWrapEnabled = (wrapStrategy ?? "overflow") === "overflow" &&
63
+ !wrapsByAlignment &&
64
+ !fillsCell;
57
65
  // Normalize display value for both rendering and overflow calculation
58
66
  const displayFormattedValue = normalizeDisplayText(formattedValue, shouldWrap);
59
67
  const shouldRecalcTextWidth = isWrapEnabled || wrapStrategy === "clip";
@@ -105,9 +113,12 @@ export const useCellRenderer = ({ sheetId, scale, showGridLines, showZeros, isRo
105
113
  }
106
114
  // shrinkToFit needs the natural text width to compare against the
107
115
  // cell's available width; rotated text uses it to size the (unrotated)
108
- // text box that gets pivoted around the cell edge. Without this, the
109
- // Konva Text renders with width 0 and the text disappears.
110
- const textWidth = (overflowRight && shouldRecalcTextWidth) || containsTextInside
116
+ // text box that gets pivoted around the cell edge; fill alignment
117
+ // repeats the text `floor(cell / natural)` times. Without this, the
118
+ // Konva Text renders with width 0 (rotation) or a single unfilled copy.
119
+ const textWidth = (overflowRight && shouldRecalcTextWidth) ||
120
+ containsTextInside ||
121
+ fillsCell
111
122
  ? getTextDimension(sheetId, rowIndex, columnIndex, displayFormattedValue).width
112
123
  : 0;
113
124
  const enableOverflow = shouldOverflow && cellWidth > props.width;
@@ -31,11 +31,30 @@ export type UseCopyPasteCutProps = {
31
31
  onCut: CanvasGridProps["onCut"];
32
32
  };
33
33
  export type PasteSpecialType = "Transposed" | "Formatting" | "Value" | "Formula" | "Link";
34
+ /** Excel Paste Special → Operation: combine each pasted cell with the cell it lands on. */
35
+ export type PasteSpecialOperation = "add" | "subtract" | "multiply" | "divide";
36
+ /**
37
+ * Excel's Paste Special dialog as one request. `paste` picks what is
38
+ * pasted (all / values / formulas / formats), `operation` combines the pasted
39
+ * value with the destination (`=dest+source`, formulas stay formulas),
40
+ * `skipBlanks` leaves the destination untouched where the clipboard cell is
41
+ * empty and `transpose` swaps rows and columns. Every flag composes with the
42
+ * others; the legacy single-mode {@link PasteSpecialType} strings map onto
43
+ * the same fields.
44
+ */
45
+ export type PasteSpecialOptions = {
46
+ paste?: "all" | "values" | "formulas" | "formats";
47
+ operation?: PasteSpecialOperation;
48
+ skipBlanks?: boolean;
49
+ transpose?: boolean;
50
+ };
51
+ /** Argument accepted by `paste()` — a legacy single mode or the full dialog request. */
52
+ export type PasteSpecialRequest = PasteSpecialType | PasteSpecialOptions;
34
53
  export declare const useCopyPasteCut: ({ selections, activeCell, sheetId, onPaste, onCopy, onCut, focusSheet, }: UseCopyPasteCutProps) => {
35
54
  selections: SelectionArea[];
36
55
  copy: () => void;
37
56
  cut: () => void;
38
- paste: (pasteSpecialType?: PasteSpecialType) => Promise<void>;
57
+ paste: (pasteSpecial?: PasteSpecialRequest) => Promise<void>;
39
58
  copiedSelections: any[];
40
59
  clearCopiedSelections: () => void;
41
60
  onInternalCopy: (e: ClipboardEvent, isCutOperation?: boolean) => boolean;