@svgrid/enterprise 3.0.2 → 3.0.3

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 (364) hide show
  1. package/LICENSE +94 -24
  2. package/README.md +36 -4
  3. package/dist/SvAdvancedFilter.svelte +1 -1
  4. package/dist/SvAlertsManager.svelte +2 -1
  5. package/dist/SvBoard.svelte +2 -1
  6. package/dist/SvExportMenu.svelte +4 -1
  7. package/dist/SvExportMenu.svelte.d.ts +0 -13
  8. package/dist/SvExpressionEditor.svelte +3 -3
  9. package/dist/SvFormulaBar.svelte +94 -20
  10. package/dist/SvFormulaBar.svelte.d.ts +7 -0
  11. package/dist/SvGridAlerts.svelte +49 -9
  12. package/dist/SvGridBoard.svelte +13 -8
  13. package/dist/SvGridBulkEditDrawer.svelte +26 -2
  14. package/dist/SvGridEditPanel.svelte +3 -0
  15. package/dist/SvGridScheduler.svelte +69 -40
  16. package/dist/SvGridSelectionBar.svelte +10 -3
  17. package/dist/SvPivotDesigner.svelte +198 -40
  18. package/dist/SvPivotDesigner.svelte.d.ts +27 -20
  19. package/dist/SvSheet.svelte +4043 -243
  20. package/dist/SvSheet.svelte.d.ts +76 -3
  21. package/dist/SvSheetChartSetup.svelte +151 -0
  22. package/dist/SvSheetChartSetup.svelte.d.ts +18 -0
  23. package/dist/SvSheetComment.svelte +216 -31
  24. package/dist/SvSheetComment.svelte.d.ts +11 -11
  25. package/dist/SvSheetConditionalFormat.svelte +28 -31
  26. package/dist/SvSheetDataValidation.svelte +66 -42
  27. package/dist/SvSheetEditRanges.svelte +217 -0
  28. package/dist/SvSheetEditRanges.svelte.d.ts +15 -0
  29. package/dist/SvSheetErrors.svelte +125 -0
  30. package/dist/SvSheetErrors.svelte.d.ts +20 -0
  31. package/dist/SvSheetEvaluate.svelte +133 -0
  32. package/dist/SvSheetEvaluate.svelte.d.ts +15 -0
  33. package/dist/SvSheetFilterMenu.svelte +137 -34
  34. package/dist/SvSheetFilterMenu.svelte.d.ts +5 -0
  35. package/dist/SvSheetFindReplace.svelte +19 -19
  36. package/dist/SvSheetFormatCells.svelte +125 -81
  37. package/dist/SvSheetGoalSeek.svelte +17 -16
  38. package/dist/SvSheetInsertFunction.svelte +15 -13
  39. package/dist/SvSheetIteration.svelte +81 -0
  40. package/dist/SvSheetIteration.svelte.d.ts +10 -0
  41. package/dist/SvSheetLink.svelte +99 -0
  42. package/dist/SvSheetLink.svelte.d.ts +17 -0
  43. package/dist/SvSheetListPicker.svelte +4 -2
  44. package/dist/SvSheetListPicker.svelte.d.ts +0 -6
  45. package/dist/SvSheetManageRules.svelte +20 -18
  46. package/dist/SvSheetNameManager.svelte +17 -15
  47. package/dist/SvSheetPageSetup.svelte +159 -0
  48. package/dist/SvSheetPageSetup.svelte.d.ts +13 -0
  49. package/dist/SvSheetPasteSpecial.svelte +22 -20
  50. package/dist/SvSheetPivot.svelte +220 -0
  51. package/dist/SvSheetPivot.svelte.d.ts +21 -0
  52. package/dist/SvSheetProtectSheet.svelte +81 -0
  53. package/dist/SvSheetProtectSheet.svelte.d.ts +11 -0
  54. package/dist/SvSheetRemoveDuplicates.svelte +13 -10
  55. package/dist/SvSheetRibbon.svelte +104 -51
  56. package/dist/SvSheetSizeDialog.svelte +4 -2
  57. package/dist/SvSheetSort.svelte +218 -0
  58. package/dist/SvSheetSort.svelte.d.ts +28 -0
  59. package/dist/SvSheetSparklines.svelte +135 -0
  60. package/dist/SvSheetSparklines.svelte.d.ts +14 -0
  61. package/dist/SvSheetTable.svelte +160 -0
  62. package/dist/SvSheetTable.svelte.d.ts +24 -0
  63. package/dist/SvSheetTabs.svelte +161 -31
  64. package/dist/SvSheetTabs.svelte.d.ts +22 -8
  65. package/dist/SvSheetTextToColumns.svelte +20 -20
  66. package/dist/SvSheetValidationAlert.svelte +8 -6
  67. package/dist/advanced-filter-enable.js +1 -1
  68. package/dist/alerts/alert-engine.d.ts +6 -0
  69. package/dist/alerts/alert-engine.js +21 -4
  70. package/dist/alerts/alert-formats.js +2 -0
  71. package/dist/alerts/alert-types.d.ts +1 -1
  72. package/dist/alerts.d.ts +1 -1
  73. package/dist/alerts.js +2 -2
  74. package/dist/board.js +2 -2
  75. package/dist/bulk-edit.d.ts +3 -1
  76. package/dist/bulk-edit.js +1 -1
  77. package/dist/cdn/svgrid-enterprise.svelte-external.js +39827 -16429
  78. package/dist/designer/assets/GridMenus-8gTNssgV.js +8 -0
  79. package/dist/designer/assets/{SvChartMenu-D9hEeHar.js → SvChartMenu-BDoeqGz6.js} +1 -1
  80. package/dist/designer/assets/{SvDateTimePicker-DARvKUL2.js → SvDateTimePicker-DCPg7WGN.js} +1 -1
  81. package/dist/designer/assets/SvGridCellEditor-DMoWJjEI.js +4 -0
  82. package/dist/designer/assets/{SvGridChart-B8j1SSy3.js → SvGridChart-Cy1ATrWh.js} +2 -2
  83. package/dist/designer/assets/SvGridChartBuilder-eQ3mn0L9.js +1 -0
  84. package/dist/designer/assets/{SvGridChartPanel-pT6ITc2P.js → SvGridChartPanel-CATdVc9X.js} +3 -3
  85. package/dist/designer/assets/{SvGridChartView-bg281UA1.js → SvGridChartView-CmsZtCED.js} +1 -1
  86. package/dist/designer/assets/SvGridDropdown-Cjz0H-fI.js +1 -0
  87. package/dist/designer/assets/{SvTabs-UFCMTHXS.js → SvTabs-2mHC6oaz.js} +1 -1
  88. package/dist/designer/assets/cell-formatting-BbubJTVz.js +1 -0
  89. package/dist/designer/assets/{chart-cIPUsbM5.js → chart-Cn7MnNqA.js} +1 -1
  90. package/dist/designer/assets/{chart-panel-messages-DtTxpBUU.js → chart-panel-messages-Dz7PTIqT.js} +1 -1
  91. package/dist/designer/assets/{chart-summary-Czu_5epb.js → chart-summary-BD1qYB2R.js} +1 -1
  92. package/dist/designer/assets/{chart-trend-D5I5VEe8.js → chart-trend-B8xr5LuD.js} +1 -1
  93. package/dist/designer/assets/{dismissable-BO547MH9.js → dismissable-ChWRPkoa.js} +1 -1
  94. package/dist/designer/assets/{export-format-D-eP4b2l.js → export-format-DGckmgKr.js} +1 -1
  95. package/dist/designer/assets/{focus-trap-CAreAhlU.js → focus-trap-V2-6Fy9I.js} +1 -1
  96. package/dist/designer/assets/{index-DwU7CAMm.js → index-D5bVbuzI.js} +317 -315
  97. package/dist/designer/assets/index-_JVFxt02.css +1 -0
  98. package/dist/designer/assets/row-resize-BInFWCpr.js +1 -0
  99. package/dist/designer/assets/server-block-cache-CXJKkJpy.js +1 -0
  100. package/dist/designer/assets/src-DUQ4xE3x.js +58 -0
  101. package/dist/designer/assets/validate-C3B8QKsM.js +1 -0
  102. package/dist/designer/index.html +17 -16
  103. package/dist/export-pdf.d.ts +75 -0
  104. package/dist/export-pdf.js +216 -15
  105. package/dist/export.js +10 -2
  106. package/dist/gantt/SvGridGantt.svelte +2890 -0
  107. package/dist/gantt/SvGridGantt.svelte.d.ts +30 -0
  108. package/dist/gantt/gantt-config.d.ts +73 -0
  109. package/dist/gantt/gantt-config.js +1 -0
  110. package/dist/gantt/gantt-critical-path.d.ts +67 -0
  111. package/dist/gantt/gantt-critical-path.js +207 -0
  112. package/dist/gantt/gantt-model.d.ts +257 -0
  113. package/dist/gantt/gantt-model.js +651 -0
  114. package/dist/gantt/gantt-resources.d.ts +59 -0
  115. package/dist/gantt/gantt-resources.js +71 -0
  116. package/dist/gantt/gantt.d.ts +7 -0
  117. package/dist/gantt/gantt.js +36 -0
  118. package/dist/gantt/index.d.ts +15 -0
  119. package/dist/gantt/index.js +22 -0
  120. package/dist/gantt/timeline-arrows.d.ts +102 -0
  121. package/dist/gantt/timeline-arrows.js +181 -0
  122. package/dist/import.d.ts +2 -2
  123. package/dist/import.js +1 -1
  124. package/dist/index.d.ts +34 -7
  125. package/dist/index.js +44 -7
  126. package/dist/install.js +8 -1
  127. package/dist/license-core.d.ts +17 -1
  128. package/dist/license-core.js +16 -5
  129. package/dist/license.d.ts +18 -2
  130. package/dist/license.js +71 -6
  131. package/dist/node/studio.js +140 -8
  132. package/dist/pivot-enable.js +2 -2
  133. package/dist/scheduler-dependencies.d.ts +35 -0
  134. package/dist/scheduler-dependencies.js +36 -1
  135. package/dist/scheduler.js +2 -2
  136. package/dist/selection-bar.js +2 -2
  137. package/dist/server/SvGroupCell.svelte +213 -0
  138. package/dist/server/SvGroupCell.svelte.d.ts +62 -0
  139. package/dist/server/SvRowGroupPanel.svelte +322 -0
  140. package/dist/server/SvRowGroupPanel.svelte.d.ts +37 -0
  141. package/dist/server/enable.d.ts +17 -0
  142. package/dist/server/enable.js +49 -0
  143. package/dist/server/index.d.ts +22 -0
  144. package/dist/server/index.js +24 -0
  145. package/dist/server/messages.d.ts +45 -0
  146. package/dist/server/messages.js +35 -0
  147. package/dist/server/server-group-model.d.ts +104 -0
  148. package/dist/server/server-group-model.js +272 -0
  149. package/dist/server/server-pivot.d.ts +46 -0
  150. package/dist/server/server-pivot.js +57 -0
  151. package/dist/server/server-row-model.d.ts +547 -0
  152. package/dist/server/server-row-model.js +1551 -0
  153. package/dist/server/server-selection.d.ts +99 -0
  154. package/dist/server/server-selection.js +215 -0
  155. package/dist/server/svgrid-adapter.d.ts +155 -0
  156. package/dist/server/svgrid-adapter.js +202 -0
  157. package/dist/sheet/ast.d.ts +45 -1
  158. package/dist/sheet/ast.js +14 -0
  159. package/dist/sheet/auto-filter.d.ts +58 -4
  160. package/dist/sheet/auto-filter.js +159 -11
  161. package/dist/sheet/autocomplete.d.ts +9 -0
  162. package/dist/sheet/autocomplete.js +90 -4
  163. package/dist/sheet/cell-images.d.ts +39 -0
  164. package/dist/sheet/cell-images.js +90 -0
  165. package/dist/sheet/cell-types.d.ts +58 -0
  166. package/dist/sheet/cell-types.js +96 -0
  167. package/dist/sheet/coerce.d.ts +40 -0
  168. package/dist/sheet/coerce.js +170 -3
  169. package/dist/sheet/comments.d.ts +43 -9
  170. package/dist/sheet/comments.js +85 -27
  171. package/dist/sheet/conditional-formats.d.ts +29 -6
  172. package/dist/sheet/conditional-formats.js +37 -12
  173. package/dist/sheet/csv.d.ts +32 -0
  174. package/dist/sheet/csv.js +158 -0
  175. package/dist/sheet/culture.d.ts +87 -0
  176. package/dist/sheet/culture.js +247 -0
  177. package/dist/sheet/delta.d.ts +102 -0
  178. package/dist/sheet/delta.js +203 -0
  179. package/dist/sheet/deps.d.ts +45 -1
  180. package/dist/sheet/deps.js +77 -2
  181. package/dist/sheet/document.d.ts +106 -4
  182. package/dist/sheet/document.js +204 -3
  183. package/dist/sheet/engine.d.ts +67 -0
  184. package/dist/sheet/engine.js +19 -0
  185. package/dist/sheet/entry.d.ts +8 -4
  186. package/dist/sheet/entry.js +171 -21
  187. package/dist/sheet/error-check.d.ts +49 -0
  188. package/dist/sheet/error-check.js +83 -0
  189. package/dist/sheet/evaluate-steps.d.ts +48 -0
  190. package/dist/sheet/evaluate-steps.js +196 -0
  191. package/dist/sheet/evaluate.d.ts +67 -0
  192. package/dist/sheet/evaluate.js +725 -13
  193. package/dist/sheet/filter-files.d.ts +37 -0
  194. package/dist/sheet/filter-files.js +465 -0
  195. package/dist/sheet/format-store.d.ts +15 -0
  196. package/dist/sheet/format-store.js +11 -1
  197. package/dist/sheet/from-rows.d.ts +14 -0
  198. package/dist/sheet/from-rows.js +50 -0
  199. package/dist/sheet/function-catalog.d.ts +1 -1
  200. package/dist/sheet/function-catalog.js +296 -5
  201. package/dist/sheet/functions.js +363 -91
  202. package/dist/sheet/index.d.ts +35 -8
  203. package/dist/sheet/index.js +35 -8
  204. package/dist/sheet/links.d.ts +57 -0
  205. package/dist/sheet/links.js +174 -0
  206. package/dist/sheet/messages.d.ts +778 -0
  207. package/dist/sheet/messages.js +806 -0
  208. package/dist/sheet/number-format.d.ts +52 -5
  209. package/dist/sheet/number-format.js +465 -68
  210. package/dist/sheet/objects.d.ts +106 -0
  211. package/dist/sheet/objects.js +149 -0
  212. package/dist/sheet/ods-document.d.ts +44 -0
  213. package/dist/sheet/ods-document.js +1275 -0
  214. package/dist/sheet/open-file.d.ts +26 -0
  215. package/dist/sheet/open-file.js +56 -0
  216. package/dist/sheet/outline.d.ts +82 -0
  217. package/dist/sheet/outline.js +228 -0
  218. package/dist/sheet/packs/array.d.ts +16 -0
  219. package/dist/sheet/packs/array.js +172 -0
  220. package/dist/sheet/packs/database.d.ts +42 -0
  221. package/dist/sheet/packs/database.js +142 -0
  222. package/dist/sheet/packs/engineering.d.ts +2 -0
  223. package/dist/sheet/packs/engineering.js +296 -0
  224. package/dist/sheet/packs/financial.d.ts +5 -0
  225. package/dist/sheet/packs/financial.js +220 -0
  226. package/dist/sheet/packs/math-stats.d.ts +2 -0
  227. package/dist/sheet/packs/math-stats.js +372 -0
  228. package/dist/sheet/packs/special.d.ts +44 -0
  229. package/dist/sheet/packs/special.js +187 -0
  230. package/dist/sheet/packs/stats-dist.d.ts +2 -0
  231. package/dist/sheet/packs/stats-dist.js +785 -0
  232. package/dist/sheet/packs/text-date.d.ts +2 -0
  233. package/dist/sheet/packs/text-date.js +332 -0
  234. package/dist/sheet/packs/trig.d.ts +2 -0
  235. package/dist/sheet/packs/trig.js +73 -0
  236. package/dist/sheet/page-setup.d.ts +73 -0
  237. package/dist/sheet/page-setup.js +73 -0
  238. package/dist/sheet/parse.js +42 -10
  239. package/dist/sheet/paste-special.d.ts +22 -0
  240. package/dist/sheet/paste-special.js +14 -5
  241. package/dist/sheet/pivot-range.d.ts +160 -0
  242. package/dist/sheet/pivot-range.js +333 -0
  243. package/dist/sheet/presence.d.ts +73 -0
  244. package/dist/sheet/presence.js +79 -0
  245. package/dist/sheet/print.d.ts +104 -0
  246. package/dist/sheet/print.js +169 -0
  247. package/dist/sheet/protection.d.ts +55 -1
  248. package/dist/sheet/protection.js +55 -2
  249. package/dist/sheet/rects.d.ts +1 -1
  250. package/dist/sheet/refs.d.ts +48 -0
  251. package/dist/sheet/refs.js +155 -8
  252. package/dist/sheet/ribbon-icons.d.ts +1 -1
  253. package/dist/sheet/ribbon-icons.js +186 -0
  254. package/dist/sheet/ribbon.d.ts +1 -1
  255. package/dist/sheet/ribbon.js +324 -23
  256. package/dist/sheet/sheet-kinds.d.ts +74 -0
  257. package/dist/sheet/sheet-kinds.js +91 -0
  258. package/dist/sheet/shortcuts.d.ts +13 -3
  259. package/dist/sheet/shortcuts.js +63 -12
  260. package/dist/sheet/sort.d.ts +51 -0
  261. package/dist/sheet/sort.js +107 -0
  262. package/dist/sheet/sparklines.d.ts +111 -0
  263. package/dist/sheet/sparklines.js +204 -0
  264. package/dist/sheet/table-styles.d.ts +74 -0
  265. package/dist/sheet/table-styles.js +152 -0
  266. package/dist/sheet/tables.d.ts +19 -0
  267. package/dist/sheet/tables.js +31 -0
  268. package/dist/sheet/tokenize.d.ts +18 -1
  269. package/dist/sheet/tokenize.js +60 -9
  270. package/dist/sheet/validation.d.ts +16 -0
  271. package/dist/sheet/validation.js +37 -0
  272. package/dist/sheet/workbook.d.ts +148 -3
  273. package/dist/sheet/workbook.js +742 -31
  274. package/dist/sheet/xls-cfb.d.ts +32 -0
  275. package/dist/sheet/xls-cfb.js +290 -0
  276. package/dist/sheet/xls-document.d.ts +10 -0
  277. package/dist/sheet/xls-document.js +1243 -0
  278. package/dist/sheet/xls-formula.d.ts +40 -0
  279. package/dist/sheet/xls-formula.js +569 -0
  280. package/dist/sheet/xlsx-document.d.ts +108 -0
  281. package/dist/sheet/xlsx-document.js +1849 -0
  282. package/dist/sheet/xlsx-drawing.d.ts +99 -0
  283. package/dist/sheet/xlsx-drawing.js +420 -0
  284. package/dist/sheet-enable.js +6 -7
  285. package/dist/sheet-text.d.ts +7 -0
  286. package/dist/sheet-text.js +29 -0
  287. package/dist/sources/auth-supabase.js +2 -2
  288. package/dist/sources/realtime-supabase.js +2 -2
  289. package/dist/sources/rest.d.ts +6 -2
  290. package/dist/sources/rest.js +42 -7
  291. package/dist/sources/supabase.d.ts +10 -1
  292. package/dist/sources/supabase.js +73 -40
  293. package/dist/studio/emit-project.js +14 -1
  294. package/dist/studio/index.js +1 -1
  295. package/dist/studio/project.d.ts +11 -2
  296. package/dist/studio/project.js +5 -2
  297. package/dist/studio/ui-components.generated.js +107 -2
  298. package/dist/sveltekit/in-memory.d.ts +2 -2
  299. package/dist/sveltekit/in-memory.js +125 -4
  300. package/dist/sveltekit/index.d.ts +1 -1
  301. package/dist/sveltekit/index.js +1 -1
  302. package/dist/sveltekit/query-plan.d.ts +50 -4
  303. package/dist/sveltekit/query-plan.js +25 -3
  304. package/dist/sveltekit/sql-source.d.ts +6 -0
  305. package/dist/sveltekit/sql-source.js +34 -6
  306. package/dist/sveltekit/sql.d.ts +31 -0
  307. package/dist/sveltekit/sql.js +135 -25
  308. package/dist/sveltekit/transport.d.ts +10 -2
  309. package/dist/sveltekit/transport.js +30 -6
  310. package/dist/upgrade-prompt.d.ts +1 -1
  311. package/dist/version.d.ts +1 -1
  312. package/dist/version.js +1 -1
  313. package/dist/watermark.js +35 -2
  314. package/dist/wc/GridMenus-By9vPuq0.js +1088 -0
  315. package/dist/wc/SvChartMenu-CQNrQGT2.js +308 -0
  316. package/dist/wc/SvDateRangeInput-DR6m7bSM.js +199 -0
  317. package/dist/wc/SvDateTimePicker-D-vRnaMo.js +771 -0
  318. package/dist/wc/SvField-B_kurx_P.js +149 -0
  319. package/dist/wc/SvGrid.helpers-BdVDtjUm.js +135 -0
  320. package/dist/wc/SvGridCellEditor-Bnf1Kr5C.js +560 -0
  321. package/dist/wc/SvGridChart-BqUJh2pG.js +3572 -0
  322. package/dist/wc/SvGridChartBuilder-CGycC5cn.js +988 -0
  323. package/dist/wc/SvGridChartPanel-CRnizsHD.js +1746 -0
  324. package/dist/wc/SvGridChartView-D1pa_xMy.js +47 -0
  325. package/dist/wc/SvGridDropdown-45WsObLL.js +259 -0
  326. package/dist/wc/SvModal-DrfOYYyX.js +189 -0
  327. package/dist/wc/cell-formatting-CBfgahv1.js +86 -0
  328. package/dist/wc/chart-6OXlybVT.js +4341 -0
  329. package/dist/wc/chart-export-CeSADGfi.js +135 -0
  330. package/dist/wc/chart-export-pdf-BGko7MER.js +104 -0
  331. package/dist/wc/chart-summary-2m3ia1-n.js +137 -0
  332. package/dist/wc/chart-trend-CQOQtTMl.js +50 -0
  333. package/dist/wc/chart-validate-CD6ptiwg.js +180 -0
  334. package/dist/wc/client-DQIzjDBG.js +3064 -0
  335. package/dist/wc/column-resize-DUPgynvc.js +101 -0
  336. package/dist/wc/date-format-DS-z_h9v.js +1130 -0
  337. package/dist/wc/dismissable-CQ6XFXsk.js +45 -0
  338. package/dist/wc/editor-contract-peBDxir_.js +22 -0
  339. package/dist/wc/editor-registry-DxGKqcvx.js +24 -0
  340. package/dist/wc/focus-trap-Dbb2tJxb.js +65 -0
  341. package/dist/wc/list-option-DrRyivWp.js +82 -0
  342. package/dist/wc/popover-WsGlvtQv.js +69 -0
  343. package/dist/wc/positioning-BiKLfu1E.js +112 -0
  344. package/dist/wc/react.d.ts +41 -0
  345. package/dist/wc/react.js +90 -0
  346. package/dist/wc/rolldown-runtime-Dy4uBu1J.js +11 -0
  347. package/dist/wc/row-drag-touch-CycRR65n.js +93 -0
  348. package/dist/wc/row-resize-limG_N1z.js +111 -0
  349. package/dist/wc/sv-sheet-element.d.ts +97 -0
  350. package/dist/wc/sv-sheet-element.js +44213 -0
  351. package/dist/wc/svelte-virtualizer.svelte-CXtQy2TQ.js +530 -0
  352. package/dist/wc/validate-BqDthkNr.js +88 -0
  353. package/dist/wc/vue.d.ts +31 -0
  354. package/dist/wc/vue.js +75 -0
  355. package/package.json +30 -5
  356. package/dist/designer/assets/GridMenus-BlsvL_aU.js +0 -8
  357. package/dist/designer/assets/SvGridCellEditor-C7uQXYds.js +0 -4
  358. package/dist/designer/assets/SvGridChartBuilder-kvyIU_Rl.js +0 -1
  359. package/dist/designer/assets/SvGridDropdown-Bb3HH2Aj.js +0 -1
  360. package/dist/designer/assets/cell-formatting-wr5wm7US.js +0 -1
  361. package/dist/designer/assets/index-CMFgAoVV.css +0 -1
  362. package/dist/designer/assets/row-resize-DLktcqUK.js +0 -1
  363. package/dist/designer/assets/src-EdI3Xqds.js +0 -58
  364. package/dist/designer/assets/validate-h-aslRV9.js +0 -1
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Excel's Error Checking: the cells on this sheet worth a second look,
4
+ * walked one at a time. Each one says what the error means rather than
5
+ * leaving the code to be looked up, and Show Calculation Steps hands the
6
+ * cell to Evaluate Formula, which is where the answer usually is.
7
+ *
8
+ * The selection follows the walk, so the cell being talked about is the
9
+ * cell on screen.
10
+ */
11
+ import { untrack } from 'svelte'
12
+ import { SvModal } from '@svgrid/grid'
13
+ import { useSheetText } from './sheet-text'
14
+ import { colToLetters } from './sheet/address'
15
+ import { checkSheet, describeFinding, type ErrorFinding } from './sheet/error-check'
16
+ import type { Workbook } from './sheet/workbook'
17
+
18
+ type Props = {
19
+ open?: boolean
20
+ workbook: Workbook
21
+ sheet: string
22
+ /** Put the selection on a cell being looked at. */
23
+ onGoTo: (cell: { row: number; col: number }) => void
24
+ /** Open Evaluate Formula on the cell being looked at. */
25
+ onSteps: (cell: { row: number; col: number }) => void
26
+ onClose?: () => void
27
+ }
28
+
29
+ let { open = $bindable(false), workbook, sheet, onGoTo, onSteps, onClose }: Props = $props()
30
+ const t = useSheetText()
31
+
32
+ let findings = $state<ErrorFinding[]>([])
33
+ let at = $state(0)
34
+ let nextButton = $state<HTMLButtonElement | null>(null)
35
+
36
+ const current = $derived(findings[at] ?? null)
37
+ const address = $derived(current ? `${colToLetters(current.col)}${current.row + 1}` : '')
38
+
39
+ // Opening is the only thing this effect follows. Reading the workbook
40
+ // through it would tie it to every repaint, and moving the selection
41
+ // repaints, so the walk would restart itself forever.
42
+ $effect(() => {
43
+ if (!open) return
44
+ untrack(load)
45
+ })
46
+
47
+ function load() {
48
+ const found = checkSheet(sheet, {
49
+ rowCount: () => workbook.rowCount(sheet),
50
+ colCount: () => workbook.colCount(sheet),
51
+ getRaw: (row, col) => workbook.getRaw(sheet, row, col),
52
+ getValue: (row, col) => workbook.getValue(sheet, row, col),
53
+ })
54
+ findings = found
55
+ at = 0
56
+ show(found[0])
57
+ queueMicrotask(() => nextButton?.focus())
58
+ }
59
+
60
+ function show(finding: ErrorFinding | undefined) {
61
+ if (finding) onGoTo({ row: finding.row, col: finding.col })
62
+ }
63
+
64
+ function move(by: number) {
65
+ const list = findings
66
+ if (list.length === 0) return
67
+ at = (at + by + list.length) % list.length
68
+ show(list[at])
69
+ }
70
+
71
+ function close() {
72
+ open = false
73
+ onClose?.()
74
+ }
75
+ </script>
76
+
77
+ <SvModal bind:open onClose={onClose} title={t('errors.title')} size="sm">
78
+ <div class="sv-sheet-dialog">
79
+ {#if current}
80
+ <p class="position">{t('errors.position', { index: at + 1, count: findings.length })}</p>
81
+ <p class="headline">
82
+ <code>{address}</code>
83
+ <strong>{current.kind === 'error' ? current.error : t('errors.inconsistent')}</strong>
84
+ </p>
85
+ <pre class="formula">{current.text}</pre>
86
+ <p class="meaning">{describeFinding(current)}</p>
87
+ {:else}
88
+ <p class="none">{t('errors.none')}</p>
89
+ {/if}
90
+ <p class="hint">{t('errors.hint')}</p>
91
+ </div>
92
+ {#snippet footer()}
93
+ <div class="sv-sheet-dialog-buttons">
94
+ <button type="button" class="btn" onclick={() => move(-1)} disabled={findings.length < 2}>{t('errors.previous')}</button>
95
+ <button type="button" class="btn" bind:this={nextButton} onclick={() => move(1)} disabled={findings.length < 2}>{t('errors.next')}</button>
96
+ <span class="spacer"></span>
97
+ <button
98
+ type="button"
99
+ class="btn primary"
100
+ disabled={!current}
101
+ onclick={() => { if (current) { open = false; onSteps({ row: current.row, col: current.col }) } }}
102
+ >{t('errors.steps')}</button>
103
+ <button type="button" class="btn" onclick={close}>{t('close')}</button>
104
+ </div>
105
+ {/snippet}
106
+ </SvModal>
107
+
108
+ <style>
109
+ .spacer { flex: 1; }
110
+ .position { margin: 0 0 6px; color: var(--sg-muted, #64748b); font-size: 12px; }
111
+ .headline { margin: 0 0 8px; display: flex; align-items: baseline; gap: 8px; }
112
+ .formula {
113
+ margin: 0 0 8px;
114
+ padding: 8px 10px;
115
+ border: 1px solid var(--sg-border, #d4d4d8);
116
+ border-radius: 4px;
117
+ background: var(--sg-surface-2, #f8fafc);
118
+ font-family: var(--sg-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
119
+ font-size: 13px;
120
+ white-space: pre-wrap;
121
+ overflow-wrap: anywhere;
122
+ }
123
+ .meaning { margin: 0; }
124
+ .none { margin: 0 0 8px; }
125
+ </style>
@@ -0,0 +1,20 @@
1
+ import type { Workbook } from './sheet/workbook.js';
2
+ type Props = {
3
+ open?: boolean;
4
+ workbook: Workbook;
5
+ sheet: string;
6
+ /** Put the selection on a cell being looked at. */
7
+ onGoTo: (cell: {
8
+ row: number;
9
+ col: number;
10
+ }) => void;
11
+ /** Open Evaluate Formula on the cell being looked at. */
12
+ onSteps: (cell: {
13
+ row: number;
14
+ col: number;
15
+ }) => void;
16
+ onClose?: () => void;
17
+ };
18
+ declare const SvSheetErrors: import("svelte").Component<Props, {}, "open">;
19
+ type SvSheetErrors = ReturnType<typeof SvSheetErrors>;
20
+ export default SvSheetErrors;
@@ -0,0 +1,133 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Excel's Evaluate Formula: the active cell's formula with one part
4
+ * underlined, and a button that replaces that part with what it is worth.
5
+ * Keep clicking and the formula collapses into the cell's answer, which is
6
+ * the quickest way to see which half of a long formula is the wrong half.
7
+ *
8
+ * The walk itself is `evaluationSteps`; this shows it and lets the reader
9
+ * move back and forth through it.
10
+ */
11
+ import { untrack } from 'svelte'
12
+ import { SvModal } from '@svgrid/grid'
13
+ import { useSheetText } from './sheet-text'
14
+ import { colToLetters } from './sheet/address'
15
+ import { parseFormula } from './sheet/parse'
16
+ import { evaluationSteps, printValue, type EvaluationStep } from './sheet/evaluate-steps'
17
+ import type { Workbook } from './sheet/workbook'
18
+
19
+ type Props = {
20
+ open?: boolean
21
+ workbook: Workbook
22
+ sheet: string
23
+ /** The cell to walk, which is the active one when the dialog opens. */
24
+ cell: { row: number; col: number }
25
+ onClose?: () => void
26
+ }
27
+
28
+ let { open = $bindable(false), workbook, sheet, cell, onClose }: Props = $props()
29
+ const t = useSheetText()
30
+
31
+ let steps = $state<EvaluationStep[]>([])
32
+ let at = $state(0)
33
+ let problem = $state('')
34
+ let evaluateButton = $state<HTMLButtonElement | null>(null)
35
+
36
+ const address = $derived(`${sheet}!${colToLetters(cell.col)}${cell.row + 1}`)
37
+ /** The line on screen: the formula as it stands before the next step, or
38
+ * the final value once every part has been worked out. */
39
+ const current = $derived(steps[at] ?? null)
40
+ const finished = $derived(steps.length > 0 && at >= steps.length)
41
+ const answer = $derived(steps.length > 0 ? steps[steps.length - 1]!.value : null)
42
+
43
+ // The cell is followed, because Error Checking hands this dialog a new
44
+ // one while it is open; the workbook is not, or every repaint would
45
+ // restart the walk.
46
+ $effect(() => {
47
+ if (!open) return
48
+ const target = cell
49
+ untrack(() => walk(target))
50
+ })
51
+
52
+ function walk(target: { row: number; col: number }) {
53
+ at = 0
54
+ problem = ''
55
+ steps = []
56
+ const text = workbook.getRaw(sheet, target.row, target.col).trim()
57
+ if (!text.startsWith('=')) {
58
+ problem = t('evaluate.noFormula')
59
+ return
60
+ }
61
+ try {
62
+ steps = evaluationSteps(
63
+ parseFormula(text),
64
+ (formula) => workbook.evaluateText(sheet, formula, undefined, { row: target.row, col: target.col }),
65
+ )
66
+ } catch {
67
+ problem = t('evaluate.noFormula')
68
+ }
69
+ queueMicrotask(() => evaluateButton?.focus())
70
+ }
71
+
72
+ function close() {
73
+ open = false
74
+ onClose?.()
75
+ }
76
+ </script>
77
+
78
+ <SvModal bind:open onClose={onClose} title={t('evaluate.title')} size="md">
79
+ <div class="sv-sheet-dialog">
80
+ <p class="reference"><span class="label">{t('evaluate.reference')}</span> <code>{address}</code></p>
81
+ {#if problem}
82
+ <p class="error">{problem}</p>
83
+ {:else}
84
+ <p class="label">{t('evaluate.evaluation')}</p>
85
+ <pre class="evaluation" data-testid="sv-sheet-evaluation">{#if current}{current.formula.slice(0, current.from)}<mark>{current.formula.slice(current.from, current.to)}</mark>{current.formula.slice(current.to)}{:else}{printValue(answer ?? '')}{/if}</pre>
86
+ {#if finished}
87
+ <p class="done">{t('evaluate.done')} <strong>{printValue(answer ?? '')}</strong></p>
88
+ {/if}
89
+ <p class="hint">{t('evaluate.hint')}</p>
90
+ {/if}
91
+ </div>
92
+ {#snippet footer()}
93
+ <div class="sv-sheet-dialog-buttons">
94
+ <button type="button" class="btn" onclick={() => { at = Math.max(0, at - 1) }} disabled={at === 0 || !!problem}>{t('evaluate.stepBack')}</button>
95
+ <button type="button" class="btn" onclick={() => { at = 0 }} disabled={at === 0 || !!problem}>{t('evaluate.restart')}</button>
96
+ <span class="spacer"></span>
97
+ <button
98
+ type="button"
99
+ class="btn primary"
100
+ bind:this={evaluateButton}
101
+ onclick={() => { at = Math.min(steps.length, at + 1) }}
102
+ disabled={finished || !!problem}
103
+ >{t('evaluate.evaluate')}</button>
104
+ <button type="button" class="btn" onclick={close}>{t('close')}</button>
105
+ </div>
106
+ {/snippet}
107
+ </SvModal>
108
+
109
+ <style>
110
+ .spacer { flex: 1; }
111
+ .reference { margin: 0 0 8px; }
112
+ .label { color: var(--sg-muted, #64748b); font-size: 12px; }
113
+ .evaluation {
114
+ margin: 0;
115
+ padding: 10px 12px;
116
+ border: 1px solid var(--sg-border, #d4d4d8);
117
+ border-radius: 4px;
118
+ background: var(--sg-surface-2, #f8fafc);
119
+ font-family: var(--sg-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
120
+ font-size: 13px;
121
+ line-height: 1.6;
122
+ white-space: pre-wrap;
123
+ overflow-wrap: anywhere;
124
+ }
125
+ .evaluation mark {
126
+ background: var(--sg-accent-soft, #dbeafe);
127
+ color: inherit;
128
+ text-decoration: underline;
129
+ text-underline-offset: 3px;
130
+ }
131
+ .done { margin: 8px 0 0; }
132
+ .error { margin: 4px 0 0; color: var(--sg-danger, #c42b1c); font-size: 12px; }
133
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { Workbook } from './sheet/workbook.js';
2
+ type Props = {
3
+ open?: boolean;
4
+ workbook: Workbook;
5
+ sheet: string;
6
+ /** The cell to walk, which is the active one when the dialog opens. */
7
+ cell: {
8
+ row: number;
9
+ col: number;
10
+ };
11
+ onClose?: () => void;
12
+ };
13
+ declare const SvSheetEvaluate: import("svelte").Component<Props, {}, "open">;
14
+ type SvSheetEvaluate = ReturnType<typeof SvSheetEvaluate>;
15
+ export default SvSheetEvaluate;
@@ -7,8 +7,9 @@
7
7
  * column's values to tick, OK and Cancel. The shell mounts it inside the
8
8
  * popover anchored to the header cell.
9
9
  */
10
+ import { useSheetText } from './sheet-text'
10
11
  import type { ExcelFilterOperator } from '@svgrid/grid/filtering'
11
- import { valuesFilter, type ColumnFilter, type FilterValue, type FilterCondition } from './sheet/auto-filter'
12
+ import { valuesFilter, type ColumnFilter, type FilterValue, type FilterCondition, type DatePeriod } from './sheet/auto-filter'
12
13
 
13
14
  type Props = {
14
15
  /** The header text, for Clear Filter From "X". */
@@ -19,26 +20,43 @@
19
20
  filter: ColumnFilter | null
20
21
  /** Whether the column holds numbers, for Number Filters over Text Filters. */
21
22
  numeric: boolean
23
+ /** Whether the column holds dates, for Date Filters. */
24
+ dates?: boolean
25
+ /** The fills the column's cells carry (null for none), for Filter by
26
+ * Color; the entry is left off with fewer than two. */
27
+ fills?: ReadonlyArray<string | null>
22
28
  onSort: (direction: 'asc' | 'desc') => void
23
29
  onApply: (filter: ColumnFilter | null) => void
24
30
  onCancel: () => void
25
31
  }
26
32
 
27
- let { header, values, filter, numeric, onSort, onApply, onCancel }: Props = $props()
33
+ let { header, values, filter, numeric, dates = false, fills = [], onSort, onApply, onCancel }: Props = $props()
34
+ const t = useSheetText()
28
35
 
29
- const TEXT_OPS: ReadonlyArray<[ExcelFilterOperator, string]> = [
30
- ['equals', 'Equals'], ['notEquals', 'Does Not Equal'], ['startsWith', 'Begins With'], ['endsWith', 'Ends With'],
31
- ['contains', 'Contains'], ['notContains', 'Does Not Contain'], ['isBlank', 'Is Blank'], ['isNotBlank', 'Is Not Blank'],
36
+ const PERIODS: ReadonlyArray<DatePeriod> = [
37
+ 'equals', 'before', 'after', 'between', 'tomorrow', 'today', 'yesterday',
38
+ 'nextWeek', 'thisWeek', 'lastWeek', 'nextMonth', 'thisMonth', 'lastMonth',
39
+ 'nextQuarter', 'thisQuarter', 'lastQuarter', 'nextYear', 'thisYear', 'lastYear', 'yearToDate',
32
40
  ]
33
- const NUMBER_OPS: ReadonlyArray<[ExcelFilterOperator, string]> = [
34
- ['equals', 'Equals'], ['notEquals', 'Does Not Equal'], ['greaterThan', 'Greater Than'], ['lessThan', 'Less Than'],
35
- ['between', 'Between'], ['isBlank', 'Is Blank'], ['isNotBlank', 'Is Not Blank'],
41
+ const typedPeriod = (p: DatePeriod) => p === 'equals' || p === 'before' || p === 'after' || p === 'between'
42
+
43
+ const TEXT_OPS: ReadonlyArray<ExcelFilterOperator> = [
44
+ 'equals', 'notEquals', 'startsWith', 'endsWith', 'contains', 'notContains', 'isBlank', 'isNotBlank',
45
+ ]
46
+ const NUMBER_OPS: ReadonlyArray<ExcelFilterOperator> = [
47
+ 'equals', 'notEquals', 'greaterThan', 'lessThan', 'between', 'isBlank', 'isNotBlank',
36
48
  ]
37
49
  const ops = $derived(numeric ? NUMBER_OPS : TEXT_OPS)
38
50
 
39
51
  let search = $state('')
40
52
  let ticked = $state<Set<string>>(new Set())
41
- let mode = $state<'values' | 'condition'>('values')
53
+ let mode = $state<'values' | 'condition' | 'date' | 'top'>('values')
54
+ let period = $state<DatePeriod>('today')
55
+ let d1 = $state('')
56
+ let d2 = $state('')
57
+ let topSide = $state<'top' | 'bottom'>('top')
58
+ let topCount = $state(10)
59
+ let topUnit = $state<'items' | 'percent'>('items')
42
60
  let op1 = $state<ExcelFilterOperator>('equals')
43
61
  let v1 = $state('')
44
62
  let v1to = $state('')
@@ -52,9 +70,15 @@
52
70
  // Open on the column's state: the ticks it has, or the condition it has.
53
71
  const f = filter
54
72
  const all = values.map((v) => v.text)
55
- if (f?.kind === 'values') ticked = new Set(all.filter((t) => !f.excluded.includes(t)))
73
+ if (f?.kind === 'values') ticked = new Set(f.included ? all.filter((t) => f.included!.includes(t)) : all.filter((t) => !f.excluded.includes(t)))
56
74
  else ticked = new Set(all)
57
- mode = f?.kind === 'condition' ? 'condition' : 'values'
75
+ mode = f?.kind === 'condition' ? 'condition' : f?.kind === 'date' ? 'date' : f?.kind === 'top' ? 'top' : 'values'
76
+ period = f?.kind === 'date' ? f.period : 'today'
77
+ d1 = f?.kind === 'date' ? f.value ?? '' : ''
78
+ d2 = f?.kind === 'date' ? f.valueTo ?? '' : ''
79
+ topSide = f?.kind === 'top' && !f.top ? 'bottom' : 'top'
80
+ topCount = f?.kind === 'top' ? f.count : 10
81
+ topUnit = f?.kind === 'top' && f.percent ? 'percent' : 'items'
58
82
  if (f?.kind === 'condition') {
59
83
  op1 = f.first.op; v1 = f.first.value ?? ''; v1to = f.first.valueTo ?? ''
60
84
  join = f.join ?? 'and'
@@ -93,7 +117,22 @@
93
117
  && (op2 === '' || !needsValue(op2) || v2.trim() !== '') && (op2 !== 'between' || v2to.trim() !== ''),
94
118
  )
95
119
 
120
+ const dateValid = $derived(
121
+ !typedPeriod(period) || (d1.trim() !== '' && (period !== 'between' || d2.trim() !== '')),
122
+ )
123
+ const topValid = $derived(Number.isFinite(topCount) && topCount > 0)
124
+
96
125
  function ok() {
126
+ if (mode === 'date') {
127
+ if (!dateValid) return
128
+ onApply({ kind: 'date', period, ...(typedPeriod(period) ? { value: d1.trim() } : {}), ...(period === 'between' ? { valueTo: d2.trim() } : {}) })
129
+ return
130
+ }
131
+ if (mode === 'top') {
132
+ if (!topValid) return
133
+ onApply({ kind: 'top', top: topSide === 'top', count: Math.trunc(topCount), ...(topUnit === 'percent' ? { percent: true } : {}) })
134
+ return
135
+ }
97
136
  if (mode === 'condition') {
98
137
  if (!conditionValid) return
99
138
  const first: FilterCondition = { op: op1, ...(needsValue(op1) ? { value: v1.trim() } : {}), ...(op1 === 'between' ? { valueTo: v1to.trim() } : {}) }
@@ -115,52 +154,100 @@
115
154
  </script>
116
155
 
117
156
  <!-- svelte-ignore a11y_no_static_element_interactions -->
118
- <div class="sv-sheet-filter-menu" role="dialog" aria-label={`Filter ${header}`} onkeydown={onKeyDown}>
119
- <button type="button" class="row" onclick={() => onSort('asc')}>{numeric ? 'Sort Smallest to Largest' : 'Sort A to Z'}</button>
120
- <button type="button" class="row" onclick={() => onSort('desc')}>{numeric ? 'Sort Largest to Smallest' : 'Sort Z to A'}</button>
157
+ <div class="sv-sheet-filter-menu" role="dialog" tabindex="-1" aria-label={t('filter.label', { header })} onkeydown={onKeyDown}>
158
+ <button type="button" class="row" onclick={() => onSort('asc')}>{t(numeric ? 'filter.sortSmallest' : 'filter.sortAsc')}</button>
159
+ <button type="button" class="row" onclick={() => onSort('desc')}>{t(numeric ? 'filter.sortLargest' : 'filter.sortDesc')}</button>
121
160
  <hr />
122
- <button type="button" class="row" disabled={!filter} onclick={() => onApply(null)}>Clear Filter From "{header}"</button>
161
+ <button type="button" class="row" disabled={!filter} onclick={() => onApply(null)}>{t('filter.clear', { header })}</button>
162
+ {#if fills.length > 1}
163
+ <!-- Excel's Filter by Color: the fills the column carries, as swatches. -->
164
+ <details class="conditions colours" open={filter?.kind === 'color'}>
165
+ <summary>{t('filter.byColor')}</summary>
166
+ <div class="swatches" role="group" aria-label={t('filter.byColorGroup')}>
167
+ {#each fills as fill (fill ?? '')}
168
+ <button
169
+ type="button"
170
+ class="swatch"
171
+ class:none={fill === null}
172
+ class:on={filter?.kind === 'color' && (filter.fill ?? '') === (fill ?? '')}
173
+ style:background={fill ?? undefined}
174
+ title={fill ?? t('filter.noFill')}
175
+ aria-label={fill ? t('filter.byFill', { fill }) : t('filter.byNoFill')}
176
+ onclick={() => onApply({ kind: 'color', fill })}
177
+ >{#if fill === null}{t('filter.noFill')}{/if}</button>
178
+ {/each}
179
+ </div>
180
+ </details>
181
+ {/if}
182
+ {#if dates}
183
+ <details class="conditions" open={mode === 'date'} ontoggle={(e) => { if ((e.currentTarget as HTMLDetailsElement).open) mode = 'date' }}>
184
+ <summary>{t('filter.dateFilters')}</summary>
185
+ <div class="condition">
186
+ <select bind:value={period} aria-label={t('filter.datePeriod')} onchange={() => (mode = 'date')}>
187
+ {#each PERIODS as id (id)}<option value={id}>{t(`filter.period.${id}`)}</option>{/each}
188
+ </select>
189
+ {#if typedPeriod(period)}<input type="date" bind:value={d1} aria-label={t('filter.date')} oninput={() => (mode = 'date')} />{/if}
190
+ {#if period === 'between'}<span>{t('filter.and')}</span><input type="date" bind:value={d2} aria-label={t('filter.secondDate')} />{/if}
191
+ </div>
192
+ </details>
193
+ {/if}
123
194
  <details class="conditions" open={mode === 'condition'} ontoggle={(e) => { if ((e.currentTarget as HTMLDetailsElement).open) mode = 'condition' }}>
124
- <summary>{numeric ? 'Number Filters' : 'Text Filters'}</summary>
195
+ <summary>{t(numeric ? 'filter.numberFilters' : 'filter.textFilters')}</summary>
125
196
  <div class="condition">
126
- <select bind:value={op1} aria-label="First condition" onchange={() => (mode = 'condition')}>
127
- {#each ops as [op, label] (op)}<option value={op}>{label}</option>{/each}
197
+ <select bind:value={op1} aria-label={t('filter.firstCondition')} onchange={() => (mode = 'condition')}>
198
+ {#each ops as op (op)}<option value={op}>{t(`filter.op.${op}`)}</option>{/each}
128
199
  </select>
129
- {#if needsValue(op1)}<input type="text" bind:value={v1} aria-label="First value" oninput={() => (mode = 'condition')} />{/if}
130
- {#if op1 === 'between'}<span>and</span><input type="text" bind:value={v1to} aria-label="First upper value" />{/if}
200
+ {#if needsValue(op1)}<input type="text" bind:value={v1} aria-label={t('filter.firstValue')} oninput={() => (mode = 'condition')} />{/if}
201
+ {#if op1 === 'between'}<span>{t('filter.and')}</span><input type="text" bind:value={v1to} aria-label={t('filter.firstUpperValue')} />{/if}
131
202
  </div>
132
203
  <div class="join">
133
- <label class="check"><input type="radio" name="sv-sheet-filter-join" value="and" bind:group={join} /> And</label>
134
- <label class="check"><input type="radio" name="sv-sheet-filter-join" value="or" bind:group={join} /> Or</label>
204
+ <label class="check"><input type="radio" name="sv-sheet-filter-join" value="and" bind:group={join} /> {t('filter.joinAnd')}</label>
205
+ <label class="check"><input type="radio" name="sv-sheet-filter-join" value="or" bind:group={join} /> {t('filter.joinOr')}</label>
135
206
  </div>
136
207
  <div class="condition">
137
- <select bind:value={op2} aria-label="Second condition">
138
- <option value="">(none)</option>
139
- {#each ops as [op, label] (op)}<option value={op}>{label}</option>{/each}
208
+ <select bind:value={op2} aria-label={t('filter.secondCondition')}>
209
+ <option value="">{t('filter.none')}</option>
210
+ {#each ops as op (op)}<option value={op}>{t(`filter.op.${op}`)}</option>{/each}
140
211
  </select>
141
- {#if needsValue(op2)}<input type="text" bind:value={v2} aria-label="Second value" />{/if}
142
- {#if op2 === 'between'}<span>and</span><input type="text" bind:value={v2to} aria-label="Second upper value" />{/if}
212
+ {#if needsValue(op2)}<input type="text" bind:value={v2} aria-label={t('filter.secondValue')} />{/if}
213
+ {#if op2 === 'between'}<span>{t('filter.and')}</span><input type="text" bind:value={v2to} aria-label={t('filter.secondUpperValue')} />{/if}
143
214
  </div>
144
215
  </details>
216
+ {#if numeric}
217
+ <details class="conditions" open={mode === 'top'} ontoggle={(e) => { if ((e.currentTarget as HTMLDetailsElement).open) mode = 'top' }}>
218
+ <summary>{t('filter.top10')}</summary>
219
+ <div class="condition top">
220
+ <select bind:value={topSide} aria-label={t('filter.topOrBottom')} onchange={() => (mode = 'top')}>
221
+ <option value="top">{t('filter.top')}</option>
222
+ <option value="bottom">{t('filter.bottom')}</option>
223
+ </select>
224
+ <input type="number" min="1" bind:value={topCount} aria-label={t('filter.howMany')} oninput={() => (mode = 'top')} />
225
+ <select bind:value={topUnit} aria-label={t('filter.itemsOrPercent')}>
226
+ <option value="items">{t('filter.items')}</option>
227
+ <option value="percent">{t('filter.percent')}</option>
228
+ </select>
229
+ </div>
230
+ </details>
231
+ {/if}
145
232
  <hr />
146
- <input bind:this={searchBox} type="search" class="search" placeholder="Search" aria-label="Search values" bind:value={search} />
147
- <div class="values" role="group" aria-label="Values">
233
+ <input bind:this={searchBox} type="search" class="search" placeholder={t('filter.search')} aria-label={t('filter.searchValues')} bind:value={search} />
234
+ <div class="values" role="group" aria-label={t('filter.values')}>
148
235
  <label class="check all">
149
236
  <input type="checkbox" checked={allShownTicked} indeterminate={!allShownTicked && !noneShownTicked} onchange={toggleAll} />
150
- (Select All{search.trim() ? ' Search Results' : ''})
237
+ {t(search.trim() ? 'filter.selectAllResults' : 'filter.selectAll')}
151
238
  </label>
152
239
  {#each shown as v (v.text)}
153
240
  <label class="check">
154
241
  <input type="checkbox" checked={ticked.has(v.text)} onchange={() => toggle(v.text)} />
155
- <span class="text">{v.text === '' ? '(Blanks)' : v.text}</span>
242
+ <span class="text">{v.text === '' ? t('filter.blanks') : v.text}</span>
156
243
  <span class="count">{v.count}</span>
157
244
  </label>
158
245
  {/each}
159
- {#if shown.length === 0}<div class="none">No matches.</div>{/if}
246
+ {#if shown.length === 0}<div class="none">{t('filter.noMatches')}</div>{/if}
160
247
  </div>
161
248
  <div class="sv-sheet-dialog-buttons">
162
- <button type="button" class="btn primary" onclick={ok} disabled={mode === 'condition' ? !conditionValid : noneShownTicked && !search.trim()}>OK</button>
163
- <button type="button" class="btn" onclick={onCancel}>Cancel</button>
249
+ <button type="button" class="btn primary" onclick={ok} disabled={mode === 'condition' ? !conditionValid : mode === 'date' ? !dateValid : mode === 'top' ? !topValid : noneShownTicked && !search.trim()}>{t('ok')}</button>
250
+ <button type="button" class="btn" onclick={onCancel}>{t('cancel')}</button>
164
251
  </div>
165
252
  </div>
166
253
 
@@ -192,6 +279,22 @@
192
279
  .condition, .join { display: flex; align-items: center; gap: 6px; padding: 3px 8px; }
193
280
  .condition select { flex: 0 0 120px; }
194
281
  .condition input { flex: 1 1 60px; min-width: 40px; }
282
+ .condition.top select { flex: 0 0 80px; }
283
+ .condition.top input { flex: 0 0 56px; }
284
+ .swatches { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 8px; }
285
+ .swatch {
286
+ width: 22px;
287
+ height: 22px;
288
+ padding: 0;
289
+ font: inherit;
290
+ font-size: 10px;
291
+ color: inherit;
292
+ border: 1px solid var(--sg-border, #d1d1d1);
293
+ border-radius: 3px;
294
+ cursor: pointer;
295
+ }
296
+ .swatch.none { width: auto; padding: 0 6px; background: var(--sg-bg, #fff); }
297
+ .swatch.on { outline: 2px solid var(--sg-accent, #217346); outline-offset: 1px; }
195
298
  select, input[type="text"], .search {
196
299
  height: 24px;
197
300
  padding: 0 6px;
@@ -8,6 +8,11 @@ type Props = {
8
8
  filter: ColumnFilter | null;
9
9
  /** Whether the column holds numbers, for Number Filters over Text Filters. */
10
10
  numeric: boolean;
11
+ /** Whether the column holds dates, for Date Filters. */
12
+ dates?: boolean;
13
+ /** The fills the column's cells carry (null for none), for Filter by
14
+ * Color; the entry is left off with fewer than two. */
15
+ fills?: ReadonlyArray<string | null>;
11
16
  onSort: (direction: 'asc' | 'desc') => void;
12
17
  onApply: (filter: ColumnFilter | null) => void;
13
18
  onCancel: () => void;