@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.
- package/LICENSE +94 -24
- package/README.md +36 -4
- package/dist/SvAdvancedFilter.svelte +1 -1
- package/dist/SvAlertsManager.svelte +2 -1
- package/dist/SvBoard.svelte +2 -1
- package/dist/SvExportMenu.svelte +4 -1
- package/dist/SvExportMenu.svelte.d.ts +0 -13
- package/dist/SvExpressionEditor.svelte +3 -3
- package/dist/SvFormulaBar.svelte +94 -20
- package/dist/SvFormulaBar.svelte.d.ts +7 -0
- package/dist/SvGridAlerts.svelte +49 -9
- package/dist/SvGridBoard.svelte +13 -8
- package/dist/SvGridBulkEditDrawer.svelte +26 -2
- package/dist/SvGridEditPanel.svelte +3 -0
- package/dist/SvGridScheduler.svelte +69 -40
- package/dist/SvGridSelectionBar.svelte +10 -3
- package/dist/SvPivotDesigner.svelte +198 -40
- package/dist/SvPivotDesigner.svelte.d.ts +27 -20
- package/dist/SvSheet.svelte +4043 -243
- package/dist/SvSheet.svelte.d.ts +76 -3
- package/dist/SvSheetChartSetup.svelte +151 -0
- package/dist/SvSheetChartSetup.svelte.d.ts +18 -0
- package/dist/SvSheetComment.svelte +216 -31
- package/dist/SvSheetComment.svelte.d.ts +11 -11
- package/dist/SvSheetConditionalFormat.svelte +28 -31
- package/dist/SvSheetDataValidation.svelte +66 -42
- package/dist/SvSheetEditRanges.svelte +217 -0
- package/dist/SvSheetEditRanges.svelte.d.ts +15 -0
- package/dist/SvSheetErrors.svelte +125 -0
- package/dist/SvSheetErrors.svelte.d.ts +20 -0
- package/dist/SvSheetEvaluate.svelte +133 -0
- package/dist/SvSheetEvaluate.svelte.d.ts +15 -0
- package/dist/SvSheetFilterMenu.svelte +137 -34
- package/dist/SvSheetFilterMenu.svelte.d.ts +5 -0
- package/dist/SvSheetFindReplace.svelte +19 -19
- package/dist/SvSheetFormatCells.svelte +125 -81
- package/dist/SvSheetGoalSeek.svelte +17 -16
- package/dist/SvSheetInsertFunction.svelte +15 -13
- package/dist/SvSheetIteration.svelte +81 -0
- package/dist/SvSheetIteration.svelte.d.ts +10 -0
- package/dist/SvSheetLink.svelte +99 -0
- package/dist/SvSheetLink.svelte.d.ts +17 -0
- package/dist/SvSheetListPicker.svelte +4 -2
- package/dist/SvSheetListPicker.svelte.d.ts +0 -6
- package/dist/SvSheetManageRules.svelte +20 -18
- package/dist/SvSheetNameManager.svelte +17 -15
- package/dist/SvSheetPageSetup.svelte +159 -0
- package/dist/SvSheetPageSetup.svelte.d.ts +13 -0
- package/dist/SvSheetPasteSpecial.svelte +22 -20
- package/dist/SvSheetPivot.svelte +220 -0
- package/dist/SvSheetPivot.svelte.d.ts +21 -0
- package/dist/SvSheetProtectSheet.svelte +81 -0
- package/dist/SvSheetProtectSheet.svelte.d.ts +11 -0
- package/dist/SvSheetRemoveDuplicates.svelte +13 -10
- package/dist/SvSheetRibbon.svelte +104 -51
- package/dist/SvSheetSizeDialog.svelte +4 -2
- package/dist/SvSheetSort.svelte +218 -0
- package/dist/SvSheetSort.svelte.d.ts +28 -0
- package/dist/SvSheetSparklines.svelte +135 -0
- package/dist/SvSheetSparklines.svelte.d.ts +14 -0
- package/dist/SvSheetTable.svelte +160 -0
- package/dist/SvSheetTable.svelte.d.ts +24 -0
- package/dist/SvSheetTabs.svelte +161 -31
- package/dist/SvSheetTabs.svelte.d.ts +22 -8
- package/dist/SvSheetTextToColumns.svelte +20 -20
- package/dist/SvSheetValidationAlert.svelte +8 -6
- package/dist/advanced-filter-enable.js +1 -1
- package/dist/alerts/alert-engine.d.ts +6 -0
- package/dist/alerts/alert-engine.js +21 -4
- package/dist/alerts/alert-formats.js +2 -0
- package/dist/alerts/alert-types.d.ts +1 -1
- package/dist/alerts.d.ts +1 -1
- package/dist/alerts.js +2 -2
- package/dist/board.js +2 -2
- package/dist/bulk-edit.d.ts +3 -1
- package/dist/bulk-edit.js +1 -1
- package/dist/cdn/svgrid-enterprise.svelte-external.js +39827 -16429
- package/dist/designer/assets/GridMenus-8gTNssgV.js +8 -0
- package/dist/designer/assets/{SvChartMenu-D9hEeHar.js → SvChartMenu-BDoeqGz6.js} +1 -1
- package/dist/designer/assets/{SvDateTimePicker-DARvKUL2.js → SvDateTimePicker-DCPg7WGN.js} +1 -1
- package/dist/designer/assets/SvGridCellEditor-DMoWJjEI.js +4 -0
- package/dist/designer/assets/{SvGridChart-B8j1SSy3.js → SvGridChart-Cy1ATrWh.js} +2 -2
- package/dist/designer/assets/SvGridChartBuilder-eQ3mn0L9.js +1 -0
- package/dist/designer/assets/{SvGridChartPanel-pT6ITc2P.js → SvGridChartPanel-CATdVc9X.js} +3 -3
- package/dist/designer/assets/{SvGridChartView-bg281UA1.js → SvGridChartView-CmsZtCED.js} +1 -1
- package/dist/designer/assets/SvGridDropdown-Cjz0H-fI.js +1 -0
- package/dist/designer/assets/{SvTabs-UFCMTHXS.js → SvTabs-2mHC6oaz.js} +1 -1
- package/dist/designer/assets/cell-formatting-BbubJTVz.js +1 -0
- package/dist/designer/assets/{chart-cIPUsbM5.js → chart-Cn7MnNqA.js} +1 -1
- package/dist/designer/assets/{chart-panel-messages-DtTxpBUU.js → chart-panel-messages-Dz7PTIqT.js} +1 -1
- package/dist/designer/assets/{chart-summary-Czu_5epb.js → chart-summary-BD1qYB2R.js} +1 -1
- package/dist/designer/assets/{chart-trend-D5I5VEe8.js → chart-trend-B8xr5LuD.js} +1 -1
- package/dist/designer/assets/{dismissable-BO547MH9.js → dismissable-ChWRPkoa.js} +1 -1
- package/dist/designer/assets/{export-format-D-eP4b2l.js → export-format-DGckmgKr.js} +1 -1
- package/dist/designer/assets/{focus-trap-CAreAhlU.js → focus-trap-V2-6Fy9I.js} +1 -1
- package/dist/designer/assets/{index-DwU7CAMm.js → index-D5bVbuzI.js} +317 -315
- package/dist/designer/assets/index-_JVFxt02.css +1 -0
- package/dist/designer/assets/row-resize-BInFWCpr.js +1 -0
- package/dist/designer/assets/server-block-cache-CXJKkJpy.js +1 -0
- package/dist/designer/assets/src-DUQ4xE3x.js +58 -0
- package/dist/designer/assets/validate-C3B8QKsM.js +1 -0
- package/dist/designer/index.html +17 -16
- package/dist/export-pdf.d.ts +75 -0
- package/dist/export-pdf.js +216 -15
- package/dist/export.js +10 -2
- package/dist/gantt/SvGridGantt.svelte +2890 -0
- package/dist/gantt/SvGridGantt.svelte.d.ts +30 -0
- package/dist/gantt/gantt-config.d.ts +73 -0
- package/dist/gantt/gantt-config.js +1 -0
- package/dist/gantt/gantt-critical-path.d.ts +67 -0
- package/dist/gantt/gantt-critical-path.js +207 -0
- package/dist/gantt/gantt-model.d.ts +257 -0
- package/dist/gantt/gantt-model.js +651 -0
- package/dist/gantt/gantt-resources.d.ts +59 -0
- package/dist/gantt/gantt-resources.js +71 -0
- package/dist/gantt/gantt.d.ts +7 -0
- package/dist/gantt/gantt.js +36 -0
- package/dist/gantt/index.d.ts +15 -0
- package/dist/gantt/index.js +22 -0
- package/dist/gantt/timeline-arrows.d.ts +102 -0
- package/dist/gantt/timeline-arrows.js +181 -0
- package/dist/import.d.ts +2 -2
- package/dist/import.js +1 -1
- package/dist/index.d.ts +34 -7
- package/dist/index.js +44 -7
- package/dist/install.js +8 -1
- package/dist/license-core.d.ts +17 -1
- package/dist/license-core.js +16 -5
- package/dist/license.d.ts +18 -2
- package/dist/license.js +71 -6
- package/dist/node/studio.js +140 -8
- package/dist/pivot-enable.js +2 -2
- package/dist/scheduler-dependencies.d.ts +35 -0
- package/dist/scheduler-dependencies.js +36 -1
- package/dist/scheduler.js +2 -2
- package/dist/selection-bar.js +2 -2
- package/dist/server/SvGroupCell.svelte +213 -0
- package/dist/server/SvGroupCell.svelte.d.ts +62 -0
- package/dist/server/SvRowGroupPanel.svelte +322 -0
- package/dist/server/SvRowGroupPanel.svelte.d.ts +37 -0
- package/dist/server/enable.d.ts +17 -0
- package/dist/server/enable.js +49 -0
- package/dist/server/index.d.ts +22 -0
- package/dist/server/index.js +24 -0
- package/dist/server/messages.d.ts +45 -0
- package/dist/server/messages.js +35 -0
- package/dist/server/server-group-model.d.ts +104 -0
- package/dist/server/server-group-model.js +272 -0
- package/dist/server/server-pivot.d.ts +46 -0
- package/dist/server/server-pivot.js +57 -0
- package/dist/server/server-row-model.d.ts +547 -0
- package/dist/server/server-row-model.js +1551 -0
- package/dist/server/server-selection.d.ts +99 -0
- package/dist/server/server-selection.js +215 -0
- package/dist/server/svgrid-adapter.d.ts +155 -0
- package/dist/server/svgrid-adapter.js +202 -0
- package/dist/sheet/ast.d.ts +45 -1
- package/dist/sheet/ast.js +14 -0
- package/dist/sheet/auto-filter.d.ts +58 -4
- package/dist/sheet/auto-filter.js +159 -11
- package/dist/sheet/autocomplete.d.ts +9 -0
- package/dist/sheet/autocomplete.js +90 -4
- package/dist/sheet/cell-images.d.ts +39 -0
- package/dist/sheet/cell-images.js +90 -0
- package/dist/sheet/cell-types.d.ts +58 -0
- package/dist/sheet/cell-types.js +96 -0
- package/dist/sheet/coerce.d.ts +40 -0
- package/dist/sheet/coerce.js +170 -3
- package/dist/sheet/comments.d.ts +43 -9
- package/dist/sheet/comments.js +85 -27
- package/dist/sheet/conditional-formats.d.ts +29 -6
- package/dist/sheet/conditional-formats.js +37 -12
- package/dist/sheet/csv.d.ts +32 -0
- package/dist/sheet/csv.js +158 -0
- package/dist/sheet/culture.d.ts +87 -0
- package/dist/sheet/culture.js +247 -0
- package/dist/sheet/delta.d.ts +102 -0
- package/dist/sheet/delta.js +203 -0
- package/dist/sheet/deps.d.ts +45 -1
- package/dist/sheet/deps.js +77 -2
- package/dist/sheet/document.d.ts +106 -4
- package/dist/sheet/document.js +204 -3
- package/dist/sheet/engine.d.ts +67 -0
- package/dist/sheet/engine.js +19 -0
- package/dist/sheet/entry.d.ts +8 -4
- package/dist/sheet/entry.js +171 -21
- package/dist/sheet/error-check.d.ts +49 -0
- package/dist/sheet/error-check.js +83 -0
- package/dist/sheet/evaluate-steps.d.ts +48 -0
- package/dist/sheet/evaluate-steps.js +196 -0
- package/dist/sheet/evaluate.d.ts +67 -0
- package/dist/sheet/evaluate.js +725 -13
- package/dist/sheet/filter-files.d.ts +37 -0
- package/dist/sheet/filter-files.js +465 -0
- package/dist/sheet/format-store.d.ts +15 -0
- package/dist/sheet/format-store.js +11 -1
- package/dist/sheet/from-rows.d.ts +14 -0
- package/dist/sheet/from-rows.js +50 -0
- package/dist/sheet/function-catalog.d.ts +1 -1
- package/dist/sheet/function-catalog.js +296 -5
- package/dist/sheet/functions.js +363 -91
- package/dist/sheet/index.d.ts +35 -8
- package/dist/sheet/index.js +35 -8
- package/dist/sheet/links.d.ts +57 -0
- package/dist/sheet/links.js +174 -0
- package/dist/sheet/messages.d.ts +778 -0
- package/dist/sheet/messages.js +806 -0
- package/dist/sheet/number-format.d.ts +52 -5
- package/dist/sheet/number-format.js +465 -68
- package/dist/sheet/objects.d.ts +106 -0
- package/dist/sheet/objects.js +149 -0
- package/dist/sheet/ods-document.d.ts +44 -0
- package/dist/sheet/ods-document.js +1275 -0
- package/dist/sheet/open-file.d.ts +26 -0
- package/dist/sheet/open-file.js +56 -0
- package/dist/sheet/outline.d.ts +82 -0
- package/dist/sheet/outline.js +228 -0
- package/dist/sheet/packs/array.d.ts +16 -0
- package/dist/sheet/packs/array.js +172 -0
- package/dist/sheet/packs/database.d.ts +42 -0
- package/dist/sheet/packs/database.js +142 -0
- package/dist/sheet/packs/engineering.d.ts +2 -0
- package/dist/sheet/packs/engineering.js +296 -0
- package/dist/sheet/packs/financial.d.ts +5 -0
- package/dist/sheet/packs/financial.js +220 -0
- package/dist/sheet/packs/math-stats.d.ts +2 -0
- package/dist/sheet/packs/math-stats.js +372 -0
- package/dist/sheet/packs/special.d.ts +44 -0
- package/dist/sheet/packs/special.js +187 -0
- package/dist/sheet/packs/stats-dist.d.ts +2 -0
- package/dist/sheet/packs/stats-dist.js +785 -0
- package/dist/sheet/packs/text-date.d.ts +2 -0
- package/dist/sheet/packs/text-date.js +332 -0
- package/dist/sheet/packs/trig.d.ts +2 -0
- package/dist/sheet/packs/trig.js +73 -0
- package/dist/sheet/page-setup.d.ts +73 -0
- package/dist/sheet/page-setup.js +73 -0
- package/dist/sheet/parse.js +42 -10
- package/dist/sheet/paste-special.d.ts +22 -0
- package/dist/sheet/paste-special.js +14 -5
- package/dist/sheet/pivot-range.d.ts +160 -0
- package/dist/sheet/pivot-range.js +333 -0
- package/dist/sheet/presence.d.ts +73 -0
- package/dist/sheet/presence.js +79 -0
- package/dist/sheet/print.d.ts +104 -0
- package/dist/sheet/print.js +169 -0
- package/dist/sheet/protection.d.ts +55 -1
- package/dist/sheet/protection.js +55 -2
- package/dist/sheet/rects.d.ts +1 -1
- package/dist/sheet/refs.d.ts +48 -0
- package/dist/sheet/refs.js +155 -8
- package/dist/sheet/ribbon-icons.d.ts +1 -1
- package/dist/sheet/ribbon-icons.js +186 -0
- package/dist/sheet/ribbon.d.ts +1 -1
- package/dist/sheet/ribbon.js +324 -23
- package/dist/sheet/sheet-kinds.d.ts +74 -0
- package/dist/sheet/sheet-kinds.js +91 -0
- package/dist/sheet/shortcuts.d.ts +13 -3
- package/dist/sheet/shortcuts.js +63 -12
- package/dist/sheet/sort.d.ts +51 -0
- package/dist/sheet/sort.js +107 -0
- package/dist/sheet/sparklines.d.ts +111 -0
- package/dist/sheet/sparklines.js +204 -0
- package/dist/sheet/table-styles.d.ts +74 -0
- package/dist/sheet/table-styles.js +152 -0
- package/dist/sheet/tables.d.ts +19 -0
- package/dist/sheet/tables.js +31 -0
- package/dist/sheet/tokenize.d.ts +18 -1
- package/dist/sheet/tokenize.js +60 -9
- package/dist/sheet/validation.d.ts +16 -0
- package/dist/sheet/validation.js +37 -0
- package/dist/sheet/workbook.d.ts +148 -3
- package/dist/sheet/workbook.js +742 -31
- package/dist/sheet/xls-cfb.d.ts +32 -0
- package/dist/sheet/xls-cfb.js +290 -0
- package/dist/sheet/xls-document.d.ts +10 -0
- package/dist/sheet/xls-document.js +1243 -0
- package/dist/sheet/xls-formula.d.ts +40 -0
- package/dist/sheet/xls-formula.js +569 -0
- package/dist/sheet/xlsx-document.d.ts +108 -0
- package/dist/sheet/xlsx-document.js +1849 -0
- package/dist/sheet/xlsx-drawing.d.ts +99 -0
- package/dist/sheet/xlsx-drawing.js +420 -0
- package/dist/sheet-enable.js +6 -7
- package/dist/sheet-text.d.ts +7 -0
- package/dist/sheet-text.js +29 -0
- package/dist/sources/auth-supabase.js +2 -2
- package/dist/sources/realtime-supabase.js +2 -2
- package/dist/sources/rest.d.ts +6 -2
- package/dist/sources/rest.js +42 -7
- package/dist/sources/supabase.d.ts +10 -1
- package/dist/sources/supabase.js +73 -40
- package/dist/studio/emit-project.js +14 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio/project.d.ts +11 -2
- package/dist/studio/project.js +5 -2
- package/dist/studio/ui-components.generated.js +107 -2
- package/dist/sveltekit/in-memory.d.ts +2 -2
- package/dist/sveltekit/in-memory.js +125 -4
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/dist/sveltekit/query-plan.d.ts +50 -4
- package/dist/sveltekit/query-plan.js +25 -3
- package/dist/sveltekit/sql-source.d.ts +6 -0
- package/dist/sveltekit/sql-source.js +34 -6
- package/dist/sveltekit/sql.d.ts +31 -0
- package/dist/sveltekit/sql.js +135 -25
- package/dist/sveltekit/transport.d.ts +10 -2
- package/dist/sveltekit/transport.js +30 -6
- package/dist/upgrade-prompt.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/watermark.js +35 -2
- package/dist/wc/GridMenus-By9vPuq0.js +1088 -0
- package/dist/wc/SvChartMenu-CQNrQGT2.js +308 -0
- package/dist/wc/SvDateRangeInput-DR6m7bSM.js +199 -0
- package/dist/wc/SvDateTimePicker-D-vRnaMo.js +771 -0
- package/dist/wc/SvField-B_kurx_P.js +149 -0
- package/dist/wc/SvGrid.helpers-BdVDtjUm.js +135 -0
- package/dist/wc/SvGridCellEditor-Bnf1Kr5C.js +560 -0
- package/dist/wc/SvGridChart-BqUJh2pG.js +3572 -0
- package/dist/wc/SvGridChartBuilder-CGycC5cn.js +988 -0
- package/dist/wc/SvGridChartPanel-CRnizsHD.js +1746 -0
- package/dist/wc/SvGridChartView-D1pa_xMy.js +47 -0
- package/dist/wc/SvGridDropdown-45WsObLL.js +259 -0
- package/dist/wc/SvModal-DrfOYYyX.js +189 -0
- package/dist/wc/cell-formatting-CBfgahv1.js +86 -0
- package/dist/wc/chart-6OXlybVT.js +4341 -0
- package/dist/wc/chart-export-CeSADGfi.js +135 -0
- package/dist/wc/chart-export-pdf-BGko7MER.js +104 -0
- package/dist/wc/chart-summary-2m3ia1-n.js +137 -0
- package/dist/wc/chart-trend-CQOQtTMl.js +50 -0
- package/dist/wc/chart-validate-CD6ptiwg.js +180 -0
- package/dist/wc/client-DQIzjDBG.js +3064 -0
- package/dist/wc/column-resize-DUPgynvc.js +101 -0
- package/dist/wc/date-format-DS-z_h9v.js +1130 -0
- package/dist/wc/dismissable-CQ6XFXsk.js +45 -0
- package/dist/wc/editor-contract-peBDxir_.js +22 -0
- package/dist/wc/editor-registry-DxGKqcvx.js +24 -0
- package/dist/wc/focus-trap-Dbb2tJxb.js +65 -0
- package/dist/wc/list-option-DrRyivWp.js +82 -0
- package/dist/wc/popover-WsGlvtQv.js +69 -0
- package/dist/wc/positioning-BiKLfu1E.js +112 -0
- package/dist/wc/react.d.ts +41 -0
- package/dist/wc/react.js +90 -0
- package/dist/wc/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/wc/row-drag-touch-CycRR65n.js +93 -0
- package/dist/wc/row-resize-limG_N1z.js +111 -0
- package/dist/wc/sv-sheet-element.d.ts +97 -0
- package/dist/wc/sv-sheet-element.js +44213 -0
- package/dist/wc/svelte-virtualizer.svelte-CXtQy2TQ.js +530 -0
- package/dist/wc/validate-BqDthkNr.js +88 -0
- package/dist/wc/vue.d.ts +31 -0
- package/dist/wc/vue.js +75 -0
- package/package.json +30 -5
- package/dist/designer/assets/GridMenus-BlsvL_aU.js +0 -8
- package/dist/designer/assets/SvGridCellEditor-C7uQXYds.js +0 -4
- package/dist/designer/assets/SvGridChartBuilder-kvyIU_Rl.js +0 -1
- package/dist/designer/assets/SvGridDropdown-Bb3HH2Aj.js +0 -1
- package/dist/designer/assets/cell-formatting-wr5wm7US.js +0 -1
- package/dist/designer/assets/index-CMFgAoVV.css +0 -1
- package/dist/designer/assets/row-resize-DLktcqUK.js +0 -1
- package/dist/designer/assets/src-EdI3Xqds.js +0 -58
- package/dist/designer/assets/validate-h-aslRV9.js +0 -1
package/dist/SvSheetTabs.svelte
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
* and calls back, so the keyboard shortcuts and the tabs cannot disagree
|
|
7
7
|
* about which sheet is active.
|
|
8
8
|
*/
|
|
9
|
+
import { tick } from 'svelte'
|
|
10
|
+
import { SvModal } from '@svgrid/grid'
|
|
9
11
|
import type { Workbook } from './sheet/workbook'
|
|
10
12
|
import { isValidSheetName } from './sheet/workbook'
|
|
13
|
+
import { useSheetText } from './sheet-text'
|
|
11
14
|
|
|
12
15
|
type Props = {
|
|
13
16
|
workbook: Workbook
|
|
@@ -21,6 +24,25 @@
|
|
|
21
24
|
*/
|
|
22
25
|
onRename?: (from: string, to: string) => void
|
|
23
26
|
onRemove?: (name: string) => void
|
|
27
|
+
/**
|
|
28
|
+
* Called when a pointer action on the strip is over: a tab clicked, the
|
|
29
|
+
* add button, an entry of the tab menu, a delete confirmed. Each leaves
|
|
30
|
+
* the focus on a button or nowhere; a consumer whose cells should keep
|
|
31
|
+
* the keyboard, as a spreadsheet's do, puts it back from here. Not
|
|
32
|
+
* called for the arrow keys inside the strip, which keep the focus on
|
|
33
|
+
* the tab they moved to, nor after a rename ends, which goes to its tab.
|
|
34
|
+
*/
|
|
35
|
+
onReturnFocus?: () => void
|
|
36
|
+
/**
|
|
37
|
+
* The sheets that are hidden: their tabs are not drawn and the menu
|
|
38
|
+
* offers Unhide for each. The consumer keeps the list (the shell keeps
|
|
39
|
+
* it in the document) and answers `onHide` / `onUnhide`.
|
|
40
|
+
*/
|
|
41
|
+
hidden?: ReadonlyArray<string>
|
|
42
|
+
onHide?: (name: string) => void
|
|
43
|
+
onUnhide?: (name: string) => void
|
|
44
|
+
/** Excel's Move or Copy > Create a copy. Without it the entry is not offered. */
|
|
45
|
+
onDuplicate?: (name: string) => void
|
|
24
46
|
/** Off hides the add button and the context actions. */
|
|
25
47
|
editable?: boolean
|
|
26
48
|
/**
|
|
@@ -35,7 +57,9 @@
|
|
|
35
57
|
version?: number
|
|
36
58
|
}
|
|
37
59
|
|
|
38
|
-
let { workbook, onChange, onRename, onRemove, editable = true, version = 0 }: Props = $props()
|
|
60
|
+
let { workbook, onChange, onRename, onRemove, onReturnFocus, hidden = [], onHide, onUnhide, onDuplicate, editable = true, version = 0 }: Props = $props()
|
|
61
|
+
|
|
62
|
+
const t = useSheetText()
|
|
39
63
|
|
|
40
64
|
let renaming = $state<string | null>(null)
|
|
41
65
|
let draft = $state('')
|
|
@@ -46,10 +70,17 @@
|
|
|
46
70
|
// keeps one of its own and reads both.
|
|
47
71
|
let localVersion = $state(0)
|
|
48
72
|
|
|
73
|
+
const isHidden = (name: string) => hidden.some((h) => h.toLowerCase() === name.toLowerCase())
|
|
74
|
+
/** The tabs drawn: the workbook's sheets less the hidden ones. */
|
|
49
75
|
const sheets = $derived.by(() => {
|
|
50
76
|
void version
|
|
51
77
|
void localVersion
|
|
52
|
-
return workbook.sheets
|
|
78
|
+
return workbook.sheets.filter((name) => !isHidden(name))
|
|
79
|
+
})
|
|
80
|
+
const hiddenSheets = $derived.by(() => {
|
|
81
|
+
void version
|
|
82
|
+
void localVersion
|
|
83
|
+
return workbook.sheets.filter((name) => isHidden(name))
|
|
53
84
|
})
|
|
54
85
|
const activeSheet = $derived.by(() => {
|
|
55
86
|
void version
|
|
@@ -67,11 +98,16 @@
|
|
|
67
98
|
changed()
|
|
68
99
|
}
|
|
69
100
|
|
|
101
|
+
let renameInput = $state<HTMLInputElement | null>(null)
|
|
70
102
|
function startRename(name: string) {
|
|
71
103
|
if (!editable) return
|
|
72
104
|
renaming = name
|
|
73
105
|
draft = name
|
|
74
106
|
error = null
|
|
107
|
+
// Focused by hand rather than with `autofocus`, which only takes when
|
|
108
|
+
// nothing has the focus: the consumer may have just put it back on its
|
|
109
|
+
// cells after the click that preceded this double-click.
|
|
110
|
+
void tick().then(() => { renameInput?.focus(); renameInput?.select() })
|
|
75
111
|
}
|
|
76
112
|
|
|
77
113
|
function commitRename() {
|
|
@@ -81,11 +117,11 @@
|
|
|
81
117
|
renaming = null
|
|
82
118
|
if (to === '' || to === from) return
|
|
83
119
|
if (!isValidSheetName(to)) {
|
|
84
|
-
error =
|
|
120
|
+
error = t('invalidSheetName', { name: to })
|
|
85
121
|
return
|
|
86
122
|
}
|
|
87
123
|
if (!workbook.renameSheet(from, to)) {
|
|
88
|
-
error =
|
|
124
|
+
error = t('duplicateSheetName', { name: to })
|
|
89
125
|
return
|
|
90
126
|
}
|
|
91
127
|
error = null
|
|
@@ -93,12 +129,40 @@
|
|
|
93
129
|
changed()
|
|
94
130
|
}
|
|
95
131
|
|
|
132
|
+
function focusActiveTab() {
|
|
133
|
+
void tick().then(() => strip?.querySelector<HTMLElement>('[role="tab"][aria-selected="true"]')?.focus())
|
|
134
|
+
}
|
|
135
|
+
|
|
96
136
|
function add() {
|
|
97
137
|
workbook.addSheet()
|
|
98
138
|
changed()
|
|
99
139
|
}
|
|
100
140
|
|
|
141
|
+
/** Delete, once confirmed when the sheet holds anything, as Excel asks. */
|
|
142
|
+
let confirmDelete = $state<string | null>(null)
|
|
143
|
+
|
|
144
|
+
function hasContent(name: string): boolean {
|
|
145
|
+
for (let r = 0; r < workbook.rowCount(name); r += 1) {
|
|
146
|
+
for (let c = 0; c < workbook.colCount(name); c += 1) {
|
|
147
|
+
if (workbook.getRaw(name, r, c).trim() !== '') return true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function askRemove(name: string) {
|
|
154
|
+
if (workbook.sheets.length < 2) return
|
|
155
|
+
if (hasContent(name)) confirmDelete = name
|
|
156
|
+
else remove(name)
|
|
157
|
+
}
|
|
158
|
+
/** The ribbon's Delete Sheet: the tab menu's Delete for the active sheet,
|
|
159
|
+
* with the same question first when the sheet holds anything. */
|
|
160
|
+
export function deleteSheet(name: string = workbook.active): void {
|
|
161
|
+
askRemove(name)
|
|
162
|
+
}
|
|
163
|
+
|
|
101
164
|
function remove(name: string) {
|
|
165
|
+
confirmDelete = null
|
|
102
166
|
// The workbook refuses to remove the last sheet; reflect that rather than
|
|
103
167
|
// showing a button that does nothing.
|
|
104
168
|
if (!workbook.removeSheet(name)) return
|
|
@@ -106,6 +170,23 @@
|
|
|
106
170
|
changed()
|
|
107
171
|
}
|
|
108
172
|
|
|
173
|
+
function hide(name: string) {
|
|
174
|
+
// Excel refuses to hide the last visible sheet.
|
|
175
|
+
if (sheets.length < 2) return
|
|
176
|
+
onHide?.(name)
|
|
177
|
+
changed()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function unhide(name: string) {
|
|
181
|
+
onUnhide?.(name)
|
|
182
|
+
changed()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function duplicate(name: string) {
|
|
186
|
+
onDuplicate?.(name)
|
|
187
|
+
changed()
|
|
188
|
+
}
|
|
189
|
+
|
|
109
190
|
// ---- Excel's right-click menu on a tab ------------------------------------
|
|
110
191
|
let menu = $state<{ name: string; x: number; y: number } | null>(null)
|
|
111
192
|
|
|
@@ -115,6 +196,13 @@
|
|
|
115
196
|
select(name)
|
|
116
197
|
menu = { name, x: event.clientX, y: event.clientY }
|
|
117
198
|
}
|
|
199
|
+
/** An entry was taken: the menu goes, and the keyboard goes back to the
|
|
200
|
+
* consumer's cells, unless the entry opened something of its own (the
|
|
201
|
+
* rename input, the delete question), which hands it on itself. */
|
|
202
|
+
function closeMenu() {
|
|
203
|
+
menu = null
|
|
204
|
+
if (renaming === null && confirmDelete === null) onReturnFocus?.()
|
|
205
|
+
}
|
|
118
206
|
/**
|
|
119
207
|
* The tabs sit at the foot of the sheet, so a menu dropped from the click
|
|
120
208
|
* runs off the bottom of the window; it opens upward from the click, as
|
|
@@ -186,21 +274,30 @@
|
|
|
186
274
|
changed()
|
|
187
275
|
}
|
|
188
276
|
|
|
189
|
-
/**
|
|
190
|
-
* keyboard user; the shortcut layer's
|
|
191
|
-
* anywhere in the grid.
|
|
277
|
+
/** The arrows move between tabs and Home and End reach the ends, which is
|
|
278
|
+
* what a tablist owes a keyboard user; the shortcut layer's
|
|
279
|
+
* Ctrl+PageUp/Down does the same from anywhere in the grid. Focus follows
|
|
280
|
+
* the selection, and the arrows are mirrored on a right-to-left strip. */
|
|
192
281
|
function onTabKey(event: KeyboardEvent, name: string) {
|
|
193
|
-
|
|
194
|
-
if (event.key === 'ArrowRight' && at < sheets.length - 1) {
|
|
195
|
-
event.preventDefault()
|
|
196
|
-
select(sheets[at + 1]!)
|
|
197
|
-
} else if (event.key === 'ArrowLeft' && at > 0) {
|
|
198
|
-
event.preventDefault()
|
|
199
|
-
select(sheets[at - 1]!)
|
|
200
|
-
} else if (event.key === 'F2') {
|
|
282
|
+
if (event.key === 'F2') {
|
|
201
283
|
event.preventDefault()
|
|
202
284
|
startRename(name)
|
|
285
|
+
return
|
|
203
286
|
}
|
|
287
|
+
const at = sheets.indexOf(name)
|
|
288
|
+
const list = (event.currentTarget as HTMLElement).closest<HTMLElement>('[role="tablist"]')
|
|
289
|
+
const rtl = list ? getComputedStyle(list).direction === 'rtl' : false
|
|
290
|
+
const forward = rtl ? 'ArrowLeft' : 'ArrowRight'
|
|
291
|
+
const back = rtl ? 'ArrowRight' : 'ArrowLeft'
|
|
292
|
+
let next = -1
|
|
293
|
+
if (event.key === forward && at < sheets.length - 1) next = at + 1
|
|
294
|
+
else if (event.key === back && at > 0) next = at - 1
|
|
295
|
+
else if (event.key === 'Home') next = 0
|
|
296
|
+
else if (event.key === 'End') next = sheets.length - 1
|
|
297
|
+
if (next < 0 || next === at) return
|
|
298
|
+
event.preventDefault()
|
|
299
|
+
select(sheets[next]!)
|
|
300
|
+
void tick().then(() => list?.querySelector<HTMLElement>('[role="tab"][aria-selected="true"]')?.focus())
|
|
204
301
|
}
|
|
205
302
|
</script>
|
|
206
303
|
|
|
@@ -212,15 +309,15 @@
|
|
|
212
309
|
<div class="sv-sheet-tabs">
|
|
213
310
|
<!-- Excel's tab-scrolling arrows: always drawn, greyed until they can move something. -->
|
|
214
311
|
<div class="nav">
|
|
215
|
-
<button type="button" class="nav-btn" tabindex="-1" aria-label=
|
|
312
|
+
<button type="button" class="nav-btn" tabindex="-1" aria-label={t('scrollTabsLeft')} disabled={!canLeft} onclick={() => scrollTabs(-120)}>
|
|
216
313
|
<svg viewBox="0 0 8 8" width="8" height="8"><path d="M5.5 1L2.5 4l3 3" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
|
217
314
|
</button>
|
|
218
|
-
<button type="button" class="nav-btn" tabindex="-1" aria-label=
|
|
315
|
+
<button type="button" class="nav-btn" tabindex="-1" aria-label={t('scrollTabsRight')} disabled={!canRight} onclick={() => scrollTabs(120)}>
|
|
219
316
|
<svg viewBox="0 0 8 8" width="8" height="8"><path d="M2.5 1l3 3-3 3" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
|
220
317
|
</button>
|
|
221
318
|
</div>
|
|
222
319
|
|
|
223
|
-
<div role="tablist" aria-label=
|
|
320
|
+
<div role="tablist" aria-label={t('sheets')} class="tabs" bind:this={strip}>
|
|
224
321
|
{#each sheets as name (name)}
|
|
225
322
|
{@const isActive = name === activeSheet}
|
|
226
323
|
<!-- role="presentation" so the tablist still OWNS the role="tab"
|
|
@@ -239,17 +336,18 @@
|
|
|
239
336
|
oncontextmenu={(e) => openMenu(e, name)}
|
|
240
337
|
>
|
|
241
338
|
{#if renaming === name}
|
|
242
|
-
<!-- svelte-ignore a11y_autofocus -->
|
|
243
339
|
<input
|
|
244
340
|
class="rename"
|
|
245
|
-
aria-label=
|
|
246
|
-
|
|
341
|
+
aria-label={t('sheetName')}
|
|
342
|
+
bind:this={renameInput}
|
|
247
343
|
value={draft}
|
|
248
344
|
oninput={(e) => (draft = e.currentTarget.value)}
|
|
249
345
|
onblur={commitRename}
|
|
250
346
|
onkeydown={(e) => {
|
|
251
|
-
|
|
252
|
-
|
|
347
|
+
// Enter and Escape hand the focus to the active tab, where a
|
|
348
|
+
// keyboard user came from; a click elsewhere keeps its own.
|
|
349
|
+
if (e.key === 'Enter') { e.preventDefault(); commitRename(); focusActiveTab() }
|
|
350
|
+
if (e.key === 'Escape') { e.preventDefault(); renaming = null; focusActiveTab() }
|
|
253
351
|
}}
|
|
254
352
|
/>
|
|
255
353
|
{:else}
|
|
@@ -258,7 +356,7 @@
|
|
|
258
356
|
role="tab"
|
|
259
357
|
aria-selected={isActive}
|
|
260
358
|
tabindex={isActive ? 0 : -1}
|
|
261
|
-
onclick={() => select(name)}
|
|
359
|
+
onclick={() => { select(name); onReturnFocus?.() }}
|
|
262
360
|
ondblclick={() => startRename(name)}
|
|
263
361
|
onkeydown={(e) => onTabKey(e, name)}
|
|
264
362
|
>{name}</button>
|
|
@@ -268,7 +366,7 @@
|
|
|
268
366
|
</div>
|
|
269
367
|
|
|
270
368
|
{#if editable}
|
|
271
|
-
<button type="button" class="add" aria-label=
|
|
369
|
+
<button type="button" class="add" aria-label={t('newSheet')} title={`${t('newSheet')} (Shift+F11)`} onclick={() => { add(); onReturnFocus?.() }}>
|
|
272
370
|
<svg viewBox="0 0 10 10" width="10" height="10" aria-hidden="true"><path d="M5 1.5v7M1.5 5h7" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" /></svg>
|
|
273
371
|
</button>
|
|
274
372
|
{/if}
|
|
@@ -284,15 +382,41 @@
|
|
|
284
382
|
on the tab face, so a stray click cannot remove a sheet.
|
|
285
383
|
-->
|
|
286
384
|
<div class="sheet-menu" role="menu" aria-label={`${menu.name} sheet`} style:left={`${menu.x}px`} style:top={`${menu.y}px`} use:keepMenuInView={menu}>
|
|
287
|
-
<button type="button" role="menuitem" onclick={() => { insertBefore(menu!.name);
|
|
288
|
-
<button type="button" role="menuitem" disabled={sheets.length < 2} onclick={() => {
|
|
289
|
-
<button type="button" role="menuitem" onclick={() => { startRename(menu!.name); menu = null }}>
|
|
385
|
+
<button type="button" role="menuitem" onclick={() => { insertBefore(menu!.name); closeMenu() }}>{t('tabInsert')}</button>
|
|
386
|
+
<button type="button" role="menuitem" disabled={workbook.sheets.length < 2} onclick={() => { askRemove(menu!.name); closeMenu() }}>{t('tabDelete')}</button>
|
|
387
|
+
<button type="button" role="menuitem" onclick={() => { startRename(menu!.name); menu = null }}>{t('tabRename')}</button>
|
|
388
|
+
{#if onDuplicate}
|
|
389
|
+
<button type="button" role="menuitem" onclick={() => { duplicate(menu!.name); closeMenu() }}>{t('tabDuplicate')}</button>
|
|
390
|
+
{/if}
|
|
290
391
|
<div class="sep" role="separator"></div>
|
|
291
|
-
<button type="button" role="menuitem" disabled={sheets.indexOf(menu.name) === 0} onclick={() => { moveBy(menu!.name, -1);
|
|
292
|
-
<button type="button" role="menuitem" disabled={sheets.indexOf(menu.name) === sheets.length - 1} onclick={() => { moveBy(menu!.name, 1);
|
|
392
|
+
<button type="button" role="menuitem" disabled={sheets.indexOf(menu.name) === 0} onclick={() => { moveBy(menu!.name, -1); closeMenu() }}>{t('tabMoveLeft')}</button>
|
|
393
|
+
<button type="button" role="menuitem" disabled={sheets.indexOf(menu.name) === sheets.length - 1} onclick={() => { moveBy(menu!.name, 1); closeMenu() }}>{t('tabMoveRight')}</button>
|
|
394
|
+
{#if onHide}
|
|
395
|
+
<div class="sep" role="separator"></div>
|
|
396
|
+
<button type="button" role="menuitem" disabled={sheets.length < 2} onclick={() => { hide(menu!.name); closeMenu() }}>{t('tabHide')}</button>
|
|
397
|
+
{#if hiddenSheets.length}
|
|
398
|
+
<!-- Excel's Unhide opens a list; here each hidden sheet is an entry. -->
|
|
399
|
+
<div class="heading" role="presentation">{t('tabUnhide')}</div>
|
|
400
|
+
{#each hiddenSheets as name (name)}
|
|
401
|
+
<button type="button" role="menuitem" class="indent" onclick={() => { unhide(name); closeMenu() }}>{name}</button>
|
|
402
|
+
{/each}
|
|
403
|
+
{/if}
|
|
404
|
+
{/if}
|
|
293
405
|
</div>
|
|
294
406
|
{/if}
|
|
295
407
|
|
|
408
|
+
<SvModal open={confirmDelete !== null} title={t('deleteSheetTitle')} size="sm" onClose={() => (confirmDelete = null)}>
|
|
409
|
+
<div class="sv-sheet-dialog">
|
|
410
|
+
<p class="note">{t('deleteSheetMessage', { name: confirmDelete ?? '' })}</p>
|
|
411
|
+
</div>
|
|
412
|
+
{#snippet footer()}
|
|
413
|
+
<div class="sv-sheet-dialog-buttons">
|
|
414
|
+
<button type="button" class="btn primary" onclick={() => { if (confirmDelete) remove(confirmDelete); onReturnFocus?.() }}>{t('delete')}</button>
|
|
415
|
+
<button type="button" class="btn" onclick={() => { confirmDelete = null; onReturnFocus?.() }}>{t('cancel')}</button>
|
|
416
|
+
</div>
|
|
417
|
+
{/snippet}
|
|
418
|
+
</SvModal>
|
|
419
|
+
|
|
296
420
|
<style>
|
|
297
421
|
.sv-sheet-tabs {
|
|
298
422
|
display: flex;
|
|
@@ -351,7 +475,10 @@
|
|
|
351
475
|
box-shadow: inset 0 -2px 0 0 var(--sg-accent, #107c41);
|
|
352
476
|
}
|
|
353
477
|
.tab.active button {
|
|
354
|
-
|
|
478
|
+
/* Mixed toward the text colour: the raw accent under 4.5:1 on the
|
|
479
|
+
lifted white tab is what axe finds and a low-vision reader feels.
|
|
480
|
+
The line along the bottom edge above keeps the accent pure. */
|
|
481
|
+
color: color-mix(in srgb, var(--sg-accent, #107c41) 72%, var(--sg-fg, #242424));
|
|
355
482
|
font-weight: 600;
|
|
356
483
|
}
|
|
357
484
|
.tab:not(.active):hover { background: var(--sg-row-hover-bg, #e9e9e9); }
|
|
@@ -438,4 +565,7 @@
|
|
|
438
565
|
.sheet-menu button:hover:not(:disabled) { background: var(--sg-row-hover-bg, #f0f0f0); }
|
|
439
566
|
.sheet-menu button:disabled { opacity: 0.4; cursor: default; }
|
|
440
567
|
.sheet-menu .sep { height: 1px; margin: 4px 6px; background: var(--sg-border, #e0e0e0); }
|
|
568
|
+
.sheet-menu .heading { padding: 4px 10px 2px; color: var(--sg-muted, #616161); font-size: 11px; }
|
|
569
|
+
.sheet-menu button.indent { padding-left: 22px; }
|
|
570
|
+
.note { margin: 0; }
|
|
441
571
|
</style>
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The sheet tab strip along the bottom of a workbook.
|
|
3
|
-
*
|
|
4
|
-
* Owns no workbook state of its own: it renders what the workbook reports
|
|
5
|
-
* and calls back, so the keyboard shortcuts and the tabs cannot disagree
|
|
6
|
-
* about which sheet is active.
|
|
7
|
-
*/
|
|
8
1
|
import type { Workbook } from './sheet/workbook.js';
|
|
9
2
|
type Props = {
|
|
10
3
|
workbook: Workbook;
|
|
@@ -18,6 +11,25 @@ type Props = {
|
|
|
18
11
|
*/
|
|
19
12
|
onRename?: (from: string, to: string) => void;
|
|
20
13
|
onRemove?: (name: string) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called when a pointer action on the strip is over: a tab clicked, the
|
|
16
|
+
* add button, an entry of the tab menu, a delete confirmed. Each leaves
|
|
17
|
+
* the focus on a button or nowhere; a consumer whose cells should keep
|
|
18
|
+
* the keyboard, as a spreadsheet's do, puts it back from here. Not
|
|
19
|
+
* called for the arrow keys inside the strip, which keep the focus on
|
|
20
|
+
* the tab they moved to, nor after a rename ends, which goes to its tab.
|
|
21
|
+
*/
|
|
22
|
+
onReturnFocus?: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* The sheets that are hidden: their tabs are not drawn and the menu
|
|
25
|
+
* offers Unhide for each. The consumer keeps the list (the shell keeps
|
|
26
|
+
* it in the document) and answers `onHide` / `onUnhide`.
|
|
27
|
+
*/
|
|
28
|
+
hidden?: ReadonlyArray<string>;
|
|
29
|
+
onHide?: (name: string) => void;
|
|
30
|
+
onUnhide?: (name: string) => void;
|
|
31
|
+
/** Excel's Move or Copy > Create a copy. Without it the entry is not offered. */
|
|
32
|
+
onDuplicate?: (name: string) => void;
|
|
21
33
|
/** Off hides the add button and the context actions. */
|
|
22
34
|
editable?: boolean;
|
|
23
35
|
/**
|
|
@@ -31,6 +43,8 @@ type Props = {
|
|
|
31
43
|
*/
|
|
32
44
|
version?: number;
|
|
33
45
|
};
|
|
34
|
-
declare const SvSheetTabs: import("svelte").Component<Props, {
|
|
46
|
+
declare const SvSheetTabs: import("svelte").Component<Props, {
|
|
47
|
+
deleteSheet: (name?: string) => void;
|
|
48
|
+
}, "">;
|
|
35
49
|
type SvSheetTabs = ReturnType<typeof SvSheetTabs>;
|
|
36
50
|
export default SvSheetTabs;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Finish writes the fields into the columns to the right of the source
|
|
6
6
|
* inside one batch, so the whole split is one Ctrl+Z.
|
|
7
7
|
*/
|
|
8
|
+
import { useSheetText } from './sheet-text'
|
|
8
9
|
import { untrack } from 'svelte'
|
|
9
10
|
import { SvModal } from '@svgrid/grid'
|
|
10
11
|
import type { GridCommandContext } from '@svgrid/grid/shortcuts'
|
|
@@ -22,13 +23,14 @@
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
let { open = $bindable(false), workbook, cmd, onDone, onClose }: Props = $props()
|
|
26
|
+
const t = useSheetText()
|
|
25
27
|
|
|
26
28
|
const DELIMITERS = [
|
|
27
|
-
{ id: 'tab', label: '
|
|
28
|
-
{ id: 'semicolon', label: '
|
|
29
|
-
{ id: 'comma', label: '
|
|
30
|
-
{ id: 'space', label: '
|
|
31
|
-
{ id: 'other', label: '
|
|
29
|
+
{ id: 'tab', label: 'textToColumns.tab', sep: '\t' },
|
|
30
|
+
{ id: 'semicolon', label: 'textToColumns.semicolon', sep: ';' },
|
|
31
|
+
{ id: 'comma', label: 'textToColumns.comma', sep: ',' },
|
|
32
|
+
{ id: 'space', label: 'textToColumns.space', sep: ' ' },
|
|
33
|
+
{ id: 'other', label: 'textToColumns.other', sep: '' },
|
|
32
34
|
] as const
|
|
33
35
|
type DelimiterId = (typeof DELIMITERS)[number]['id']
|
|
34
36
|
|
|
@@ -90,7 +92,7 @@
|
|
|
90
92
|
open = false
|
|
91
93
|
c.setSelection(column.top, column.index)
|
|
92
94
|
c.extendSelection(column.top + rows.length - 1, column.index + width - 1)
|
|
93
|
-
onDone(
|
|
95
|
+
onDone(t('textToColumns.done', { rows: rows.length, columns: width }))
|
|
94
96
|
onClose?.()
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -100,26 +102,26 @@
|
|
|
100
102
|
}
|
|
101
103
|
</script>
|
|
102
104
|
|
|
103
|
-
<SvModal bind:open onClose={onClose} title=
|
|
104
|
-
<div class="sv-sheet-dialog" role="group" aria-label=
|
|
105
|
-
<p class="lead">
|
|
105
|
+
<SvModal bind:open onClose={onClose} title={t('textToColumns.title')} size="md">
|
|
106
|
+
<div class="sv-sheet-dialog" role="group" aria-label={t('textToColumns.group')}>
|
|
107
|
+
<p class="lead">{t('textToColumns.lead')}</p>
|
|
106
108
|
<fieldset class="group">
|
|
107
|
-
<legend>
|
|
109
|
+
<legend>{t('textToColumns.delimiters')}</legend>
|
|
108
110
|
{#each DELIMITERS as d (d.id)}
|
|
109
111
|
<label class="check">
|
|
110
112
|
<input type="radio" name="sheet-delimiter" value={d.id} bind:group={delimiter} />
|
|
111
|
-
{d.label}
|
|
113
|
+
{t(d.label)}
|
|
112
114
|
{#if d.id === 'other'}
|
|
113
|
-
<input class="other" type="text" maxlength="1" bind:value={other} aria-label=
|
|
115
|
+
<input class="other" type="text" maxlength="1" bind:value={other} aria-label={t('textToColumns.otherDelimiter')} />
|
|
114
116
|
{/if}
|
|
115
117
|
</label>
|
|
116
118
|
{/each}
|
|
117
119
|
</fieldset>
|
|
118
120
|
<div class="checks">
|
|
119
|
-
<label class="check"><input type="checkbox" bind:checked={collapse} />
|
|
120
|
-
<label class="check"><input type="checkbox" bind:checked={trim} />
|
|
121
|
+
<label class="check"><input type="checkbox" bind:checked={collapse} /> {t('textToColumns.collapse')}</label>
|
|
122
|
+
<label class="check"><input type="checkbox" bind:checked={trim} /> {t('textToColumns.trim')}</label>
|
|
121
123
|
</div>
|
|
122
|
-
<div class="preview" aria-label=
|
|
124
|
+
<div class="preview" aria-label={t('textToColumns.preview')}>
|
|
123
125
|
<table>
|
|
124
126
|
<tbody>
|
|
125
127
|
{#each preview.rows as fields, i (i)}
|
|
@@ -129,15 +131,13 @@
|
|
|
129
131
|
</table>
|
|
130
132
|
</div>
|
|
131
133
|
<p class="status">
|
|
132
|
-
{column.texts.length
|
|
133
|
-
become {preview.width} column{preview.width === 1 ? '' : 's'} from column {colToLetters(column.index)}.
|
|
134
|
-
Anything already in those columns is replaced.
|
|
134
|
+
{t('textToColumns.status', { rows: column.texts.length, from: colToLetters(column.index), count: preview.width, unit: t(preview.width === 1 ? 'textToColumns.column' : 'textToColumns.columns') })}
|
|
135
135
|
</p>
|
|
136
136
|
</div>
|
|
137
137
|
{#snippet footer()}
|
|
138
138
|
<div class="sv-sheet-dialog-buttons">
|
|
139
|
-
<button type="button" class="btn primary" onclick={finish} disabled={column.texts.length === 0}>
|
|
140
|
-
<button type="button" class="btn" onclick={close}>
|
|
139
|
+
<button type="button" class="btn primary" onclick={finish} disabled={column.texts.length === 0}>{t('textToColumns.finish')}</button>
|
|
140
|
+
<button type="button" class="btn" onclick={close}>{t('cancel')}</button>
|
|
141
141
|
</div>
|
|
142
142
|
{/snippet}
|
|
143
143
|
</SvModal>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* the message, "Continue?", Yes (the entry lands anyway), No (retry) and
|
|
6
6
|
* Cancel. Enter takes the first button, Escape cancels.
|
|
7
7
|
*/
|
|
8
|
+
import { useSheetText } from './sheet-text'
|
|
8
9
|
import { SvModal } from '@svgrid/grid'
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
let { open = $bindable(false), alert, onRetry, onAccept, onCancel }: Props = $props()
|
|
21
|
+
const t = useSheetText()
|
|
20
22
|
let first = $state<HTMLButtonElement | null>(null)
|
|
21
23
|
|
|
22
24
|
$effect(() => {
|
|
@@ -30,23 +32,23 @@
|
|
|
30
32
|
}
|
|
31
33
|
</script>
|
|
32
34
|
|
|
33
|
-
<SvModal bind:open onClose={onCancel} title={alert?.title ?? '
|
|
35
|
+
<SvModal bind:open onClose={onCancel} title={alert?.title ?? t('dataValidationTitle')} size="sm">
|
|
34
36
|
<div class="sv-sheet-dialog alert" role="alertdialog" aria-describedby="sv-sheet-validation-message">
|
|
35
37
|
<span class="mark" class:warning={alert?.style === 'warning'} aria-hidden="true">{alert?.style === 'warning' ? '!' : '×'}</span>
|
|
36
38
|
<div>
|
|
37
39
|
<p id="sv-sheet-validation-message" class="message">{alert?.message}</p>
|
|
38
|
-
{#if alert?.style === 'warning'}<p class="message">
|
|
40
|
+
{#if alert?.style === 'warning'}<p class="message">{t('validationContinue')}</p>{/if}
|
|
39
41
|
</div>
|
|
40
42
|
</div>
|
|
41
43
|
{#snippet footer()}
|
|
42
44
|
<div class="sv-sheet-dialog-buttons">
|
|
43
45
|
{#if alert?.style === 'warning'}
|
|
44
|
-
<button bind:this={first} type="button" class="btn primary" onclick={() => done(onAccept)}>
|
|
45
|
-
<button type="button" class="btn" onclick={() => done(onRetry)}>
|
|
46
|
+
<button bind:this={first} type="button" class="btn primary" onclick={() => done(onAccept)}>{t('yes')}</button>
|
|
47
|
+
<button type="button" class="btn" onclick={() => done(onRetry)}>{t('no')}</button>
|
|
46
48
|
{:else}
|
|
47
|
-
<button bind:this={first} type="button" class="btn primary" onclick={() => done(onRetry)}>
|
|
49
|
+
<button bind:this={first} type="button" class="btn primary" onclick={() => done(onRetry)}>{t('retry')}</button>
|
|
48
50
|
{/if}
|
|
49
|
-
<button type="button" class="btn" onclick={() => done(onCancel)}>
|
|
51
|
+
<button type="button" class="btn" onclick={() => done(onCancel)}>{t('cancel')}</button>
|
|
50
52
|
</div>
|
|
51
53
|
{/snippet}
|
|
52
54
|
</SvModal>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Advanced Filter (Pro): register the expression compiler with the grid.
|
|
3
3
|
*
|
|
4
|
-
* Same shape as `pivot-enable.ts` - idempotent flag, register,
|
|
4
|
+
* Same shape as `pivot-enable.ts` - idempotent flag, register, unlicensed nudge.
|
|
5
5
|
* The grid owns the filter state and the pipeline slot; this supplies the
|
|
6
6
|
* compiler that turns an expression into a row predicate.
|
|
7
7
|
*/
|
|
@@ -19,6 +19,12 @@ export type AlertEngine<TData> = {
|
|
|
19
19
|
evaluate(rows: ReadonlyArray<TData>): AlertEvent[];
|
|
20
20
|
/** Prev -> next pass: fires `relativeChange` plus `dataChange` edges. */
|
|
21
21
|
evaluateTransition(prev: ReadonlyArray<TData>, next: ReadonlyArray<TData>): AlertEvent[];
|
|
22
|
+
/**
|
|
23
|
+
* Scheduled pass for one rule: every row matching its predicate now, with
|
|
24
|
+
* no edge memory, since the point of a schedule is the standing list at
|
|
25
|
+
* that moment. Aggregate rules answer with one event or none.
|
|
26
|
+
*/
|
|
27
|
+
evaluateScheduled(ruleId: string, rows: ReadonlyArray<TData>): AlertEvent[];
|
|
22
28
|
/** Would editing `(row, columnId) -> nextValue` trip a validation rule? */
|
|
23
29
|
validateEdit(row: TData, columnId: string, nextValue: unknown): ValidateEditResult;
|
|
24
30
|
/**
|
|
@@ -26,21 +26,23 @@ function defaultMessage(rule) {
|
|
|
26
26
|
}
|
|
27
27
|
export function createAlertEngine(options) {
|
|
28
28
|
let rules = options.rules;
|
|
29
|
-
const getValue = options.getValue;
|
|
30
29
|
const now = options.now ?? (() => Date.now());
|
|
31
30
|
// Edge memory: keys currently satisfying a `dataChange`/aggregate predicate.
|
|
32
31
|
const active = new Set();
|
|
32
|
+
// Read off `options` at use, not once here: the overlay hands in getters
|
|
33
|
+
// for its `getValue` and `locale` props so a change after mount reaches
|
|
34
|
+
// the next evaluation.
|
|
33
35
|
const ctxFor = (row, rows, prev) => ({
|
|
34
36
|
row,
|
|
35
37
|
prev,
|
|
36
38
|
rows,
|
|
37
|
-
getValue,
|
|
39
|
+
getValue: options.getValue,
|
|
38
40
|
locale: options.locale,
|
|
39
41
|
});
|
|
40
42
|
const val = (row, columnId) => columnId == null
|
|
41
43
|
? undefined
|
|
42
|
-
: getValue
|
|
43
|
-
? getValue(row, columnId)
|
|
44
|
+
: options.getValue
|
|
45
|
+
? options.getValue(row, columnId)
|
|
44
46
|
: row?.[columnId];
|
|
45
47
|
// A representative column for the message `{value}`/`{column}` tokens: the
|
|
46
48
|
// rule's explicit target, else the first column its predicate references.
|
|
@@ -172,6 +174,21 @@ export function createAlertEngine(options) {
|
|
|
172
174
|
}
|
|
173
175
|
return events;
|
|
174
176
|
},
|
|
177
|
+
evaluateScheduled(ruleId, rows) {
|
|
178
|
+
const rule = activeRules().find((r) => r.id === ruleId);
|
|
179
|
+
if (!rule)
|
|
180
|
+
return [];
|
|
181
|
+
if (rule.scope === 'aggregate')
|
|
182
|
+
return evaluateAggregate(rule, rows);
|
|
183
|
+
const events = [];
|
|
184
|
+
for (const row of rows) {
|
|
185
|
+
if (!evaluatePredicate(rule.predicate, ctxFor(row, rows)))
|
|
186
|
+
continue;
|
|
187
|
+
const source = primaryColumn(rule);
|
|
188
|
+
events.push(buildEvent(rule, { row, rowId: options.getRowId(row), columnId: rule.scope === 'cell' ? source : undefined, value: val(row, source) }));
|
|
189
|
+
}
|
|
190
|
+
return events;
|
|
191
|
+
},
|
|
175
192
|
validateEdit(row, columnId, nextValue) {
|
|
176
193
|
const candidate = { ...row, [columnId]: nextValue };
|
|
177
194
|
const events = [];
|
|
@@ -24,6 +24,7 @@ export function rulesToConditionalFormats(rules, ctx = {}) {
|
|
|
24
24
|
when: ({ row }) => evaluatePredicate(rule.predicate, { row, getValue: ctx.getValue, locale: ctx.locale }),
|
|
25
25
|
...(style.background ? { background: style.background } : {}),
|
|
26
26
|
...(style.color ? { color: style.color } : {}),
|
|
27
|
+
...(style.icon ? { icon: style.icon } : {}),
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -51,6 +52,7 @@ export function toConditionalFormats(events, getRowId) {
|
|
|
51
52
|
when: ({ row }) => getRowId(row) === rowId,
|
|
52
53
|
...(style.background ? { background: style.background } : {}),
|
|
53
54
|
...(style.color ? { color: style.color } : {}),
|
|
55
|
+
...(style.icon ? { icon: style.icon } : {}),
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -40,7 +40,7 @@ export type AlertActionKind = 'toast' | 'badge' | 'cellFlash' | 'highlight' | 'p
|
|
|
40
40
|
export type AlertActionStyle = {
|
|
41
41
|
background?: string;
|
|
42
42
|
color?: string;
|
|
43
|
-
/** A short glyph/emoji
|
|
43
|
+
/** A short glyph/emoji drawn before the cell value while the rule matches. */
|
|
44
44
|
icon?: string;
|
|
45
45
|
};
|
|
46
46
|
export type AlertAction = {
|
package/dist/alerts.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** Turn on the alerts feature (
|
|
1
|
+
/** Turn on the alerts feature (runs without a key, with the watermark and the console notice). Idempotent. */
|
|
2
2
|
export declare function enableAlerts(): void;
|
package/dist/alerts.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Unlike the scheduler/board *views*, alerts are an overlay the consumer mounts
|
|
5
5
|
* next to the grid (`<SvGridAlerts>`), so there is no renderer to register with
|
|
6
|
-
* the grid. `enableAlerts()` exists only to keep the
|
|
6
|
+
* the grid. `enableAlerts()` exists only to keep the evaluation behaviour consistent
|
|
7
7
|
* with the rest of Enterprise: it works without a license key but nudges once in
|
|
8
8
|
* the console when unlicensed.
|
|
9
9
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { isLicenseKeySet } from './license.js';
|
|
19
19
|
import { emitUnlicensedNudge } from './watermark.js';
|
|
20
20
|
let enabled = false;
|
|
21
|
-
/** Turn on the alerts feature (
|
|
21
|
+
/** Turn on the alerts feature (runs without a key, with the watermark and the console notice). Idempotent. */
|
|
22
22
|
export function enableAlerts() {
|
|
23
23
|
if (enabled)
|
|
24
24
|
return;
|