@svgrid/grid 2.6.21 → 2.7.0
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/CHANGELOG.md +150 -88
- package/README.md +221 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/GridMenus.svelte +201 -18
- package/dist/SvAutoComplete.svelte +169 -165
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +279 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.controller.svelte.d.ts +61 -9
- package/dist/SvGrid.controller.svelte.js +363 -123
- package/dist/SvGrid.css +222 -1
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +255 -56
- package/dist/SvGrid.types.d.ts +146 -1
- package/dist/SvGridCellEditor.svelte +744 -662
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +270 -266
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -289
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +172 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +239 -235
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-C4oLsJyj.js +600 -0
- package/dist/cdn/GridMenus-yEivjNwl.js +601 -0
- package/dist/cdn/SvDateRangeInput-Bh0A0JkF.js +199 -0
- package/dist/cdn/SvDateRangeInput-DflbiP7N.js +198 -0
- package/dist/cdn/SvDateTimePicker-BWwpfB_o.js +812 -0
- package/dist/cdn/SvDateTimePicker-Bivn8dAP.js +816 -0
- package/dist/cdn/SvGridCellEditor-AXL8cHGO.js +550 -0
- package/dist/cdn/SvGridCellEditor-Ba7rY3eu.js +549 -0
- package/dist/cdn/{SvGridChart-BwVos976.js → SvGridChart-Bs2GIR2Q.js} +1 -1
- package/dist/cdn/{SvGridChart-BEJmNNx9.js → SvGridChart-CwhFz7GV.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-BOknOkrP.js → SvGridChartPanel-1WXuStzM.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-D2PjII4O.js → SvGridChartPanel-e5_nqNP9.js} +1 -1
- package/dist/cdn/{SvGridChartView-00LPUHL1.js → SvGridChartView-SmPW10dI.js} +1 -1
- package/dist/cdn/{SvGridChartView-BhUEJ5ki.js → SvGridChartView-eSPuJE6g.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D13MtJ2j.js → SvGridDropdown-CtdbHcIS.js} +1 -1
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/date-format-CcP1tafP.js +1395 -0
- package/dist/cdn/date-format-DZT7T1wf.js +1393 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-BYq-qyrp.js → src-COC0vnpg.js} +7846 -7317
- package/dist/cdn/{src-DBel9wRZ.js → src-pYGV2rMR.js} +10033 -9504
- package/dist/cdn/svgrid.js +15 -11
- package/dist/cdn/svgrid.svelte-external.js +15 -11
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/chart-export.js +8 -8
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +19 -4
- package/dist/core.js +460 -119
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +41 -8
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +133 -8
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/GridMenus.svelte +201 -18
- package/src/SvAutoComplete.svelte +169 -165
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +279 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +404 -133
- package/src/SvGrid.css +222 -1
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +255 -56
- package/src/SvGrid.types.ts +2092 -1940
- package/src/SvGridCellEditor.svelte +744 -662
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +270 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -326
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +293 -289
- package/src/SvNumberInput.svelte +172 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichCell.svelte +125 -0
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +239 -235
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1419 -1419
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -846
- package/src/builtin-editors.grid.test.ts +83 -83
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-formatting.ts +171 -171
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +148 -118
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.performance.test.ts +30 -30
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +1865 -1468
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +322 -322
- package/src/date-string-column.test.ts +87 -0
- package/src/datetime/date-core.ts +206 -206
- package/src/datetime/date-restrict.ts +61 -61
- package/src/datetime/timezone.ts +135 -135
- package/src/dock-manager-model.ts +596 -596
- package/src/dock-model.ts +374 -374
- package/src/editing.test.ts +974 -949
- package/src/editing.ts +609 -606
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editor-contract.ts +171 -171
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/editors/cell-editors.ts +27 -1
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +160 -160
- package/src/filtering/excel-filters.ts +325 -295
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/grid-messages.ts +34 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/index.ts +25 -1
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +179 -173
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +597 -567
- package/src/merge-objects.ts +48 -48
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/resize-props.test.ts +361 -0
- package/src/row-drag-touch.ts +211 -211
- package/src/row-drag.test.ts +353 -353
- package/src/row-drag.ts +415 -415
- package/src/row-resize.test.ts +524 -493
- package/src/row-resize.ts +228 -210
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/scheduler-ical.ts +181 -181
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +873 -873
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +900 -743
- package/src/selection.ts +135 -8
- package/src/server-data-source.test.ts +383 -383
- package/src/server-data-source.ts +469 -469
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/summaries.ts +113 -113
- package/src/svgrid-wrapper.types.ts +563 -563
- package/src/svgrid.async-editor-options.test.ts +273 -273
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +910 -910
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +127 -127
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +377 -331
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +288 -288
- package/src/toast-store.svelte.ts +250 -250
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +322 -322
- package/dist/cdn/GridMenus-BfTAKn84.js +0 -488
- package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* End-to-end: after registerBuiltinEditors(), a column with editorType 'duration'
|
|
3
|
-
* mounts SvDurationInput in the editing cell (proving the built-in registration +
|
|
4
|
-
* the SvGrid registry wiring together).
|
|
5
|
-
*/
|
|
6
|
-
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
-
import { mount, unmount } from 'svelte'
|
|
8
|
-
import SvGrid from './SvGrid.svelte'
|
|
9
|
-
import {
|
|
10
|
-
registerBuiltinEditors,
|
|
11
|
-
unregisterCellEditor,
|
|
12
|
-
registeredCellEditorTypes,
|
|
13
|
-
createCoreRowModel,
|
|
14
|
-
createFilteredRowModel,
|
|
15
|
-
createPaginatedRowModel,
|
|
16
|
-
createSortedRowModel,
|
|
17
|
-
sortFns,
|
|
18
|
-
tableFeatures,
|
|
19
|
-
columnFilteringFeature,
|
|
20
|
-
rowSortingFeature,
|
|
21
|
-
} from './index'
|
|
22
|
-
import type { ColumnDef, SvGridApi } from './index'
|
|
23
|
-
|
|
24
|
-
type Row = { id: number; task: string; estimate: number }
|
|
25
|
-
|
|
26
|
-
const features = tableFeatures({ columnFilteringFeature, rowSortingFeature })
|
|
27
|
-
const rows: Row[] = [
|
|
28
|
-
{ id: 1, task: 'Design', estimate: 90 },
|
|
29
|
-
{ id: 2, task: 'Build', estimate: 240 },
|
|
30
|
-
]
|
|
31
|
-
const cols: ColumnDef<typeof features, Row>[] = [
|
|
32
|
-
{ field: 'task', header: 'Task', width: 200, editorType: 'text' },
|
|
33
|
-
{ field: 'estimate', header: 'Estimate', width: 160, editorType: 'duration' },
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function mountGrid() {
|
|
38
|
-
return new Promise<{ api: SvGridApi<typeof features, Row>; target: HTMLElement; destroy: () => void }>(
|
|
39
|
-
(res) => {
|
|
40
|
-
const target = document.createElement('div')
|
|
41
|
-
document.body.appendChild(target)
|
|
42
|
-
const app = mount(SvGrid, {
|
|
43
|
-
target,
|
|
44
|
-
props: {
|
|
45
|
-
data: rows,
|
|
46
|
-
columns: cols,
|
|
47
|
-
features,
|
|
48
|
-
_rowModels: {
|
|
49
|
-
coreRowModel: createCoreRowModel(),
|
|
50
|
-
filteredRowModel: createFilteredRowModel(),
|
|
51
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
52
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
53
|
-
},
|
|
54
|
-
rowHeight: 36,
|
|
55
|
-
containerHeight: 300,
|
|
56
|
-
virtualization: false,
|
|
57
|
-
enableInlineEditing: true,
|
|
58
|
-
onApiReady(api: SvGridApi<typeof features, Row>) {
|
|
59
|
-
res({ api, target, destroy: () => { unmount(app); target.remove() } })
|
|
60
|
-
},
|
|
61
|
-
} as any,
|
|
62
|
-
})
|
|
63
|
-
},
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
afterEach(() => {
|
|
68
|
-
for (const t of registeredCellEditorTypes()) unregisterCellEditor(t)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
describe('registerBuiltinEditors in SvGrid', () => {
|
|
72
|
-
it("mounts SvDurationInput for an editorType 'duration' cell", async () => {
|
|
73
|
-
registerBuiltinEditors()
|
|
74
|
-
const { api, target, destroy } = await mountGrid()
|
|
75
|
-
try {
|
|
76
|
-
api.startEditing(0, 'estimate')
|
|
77
|
-
// SvGridCellEditor is a lazy chunk; poll instead of fixing a tick count.
|
|
78
|
-
await vi.waitFor(() => expect(target.querySelector('.sv-dur__input')).not.toBeNull())
|
|
79
|
-
} finally {
|
|
80
|
-
destroy()
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end: after registerBuiltinEditors(), a column with editorType 'duration'
|
|
3
|
+
* mounts SvDurationInput in the editing cell (proving the built-in registration +
|
|
4
|
+
* the SvGrid registry wiring together).
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
import { mount, unmount } from 'svelte'
|
|
8
|
+
import SvGrid from './SvGrid.svelte'
|
|
9
|
+
import {
|
|
10
|
+
registerBuiltinEditors,
|
|
11
|
+
unregisterCellEditor,
|
|
12
|
+
registeredCellEditorTypes,
|
|
13
|
+
createCoreRowModel,
|
|
14
|
+
createFilteredRowModel,
|
|
15
|
+
createPaginatedRowModel,
|
|
16
|
+
createSortedRowModel,
|
|
17
|
+
sortFns,
|
|
18
|
+
tableFeatures,
|
|
19
|
+
columnFilteringFeature,
|
|
20
|
+
rowSortingFeature,
|
|
21
|
+
} from './index'
|
|
22
|
+
import type { ColumnDef, SvGridApi } from './index'
|
|
23
|
+
|
|
24
|
+
type Row = { id: number; task: string; estimate: number }
|
|
25
|
+
|
|
26
|
+
const features = tableFeatures({ columnFilteringFeature, rowSortingFeature })
|
|
27
|
+
const rows: Row[] = [
|
|
28
|
+
{ id: 1, task: 'Design', estimate: 90 },
|
|
29
|
+
{ id: 2, task: 'Build', estimate: 240 },
|
|
30
|
+
]
|
|
31
|
+
const cols: ColumnDef<typeof features, Row>[] = [
|
|
32
|
+
{ field: 'task', header: 'Task', width: 200, editorType: 'text' },
|
|
33
|
+
{ field: 'estimate', header: 'Estimate', width: 160, editorType: 'duration' },
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function mountGrid() {
|
|
38
|
+
return new Promise<{ api: SvGridApi<typeof features, Row>; target: HTMLElement; destroy: () => void }>(
|
|
39
|
+
(res) => {
|
|
40
|
+
const target = document.createElement('div')
|
|
41
|
+
document.body.appendChild(target)
|
|
42
|
+
const app = mount(SvGrid, {
|
|
43
|
+
target,
|
|
44
|
+
props: {
|
|
45
|
+
data: rows,
|
|
46
|
+
columns: cols,
|
|
47
|
+
features,
|
|
48
|
+
_rowModels: {
|
|
49
|
+
coreRowModel: createCoreRowModel(),
|
|
50
|
+
filteredRowModel: createFilteredRowModel(),
|
|
51
|
+
sortedRowModel: createSortedRowModel(sortFns),
|
|
52
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
53
|
+
},
|
|
54
|
+
rowHeight: 36,
|
|
55
|
+
containerHeight: 300,
|
|
56
|
+
virtualization: false,
|
|
57
|
+
enableInlineEditing: true,
|
|
58
|
+
onApiReady(api: SvGridApi<typeof features, Row>) {
|
|
59
|
+
res({ api, target, destroy: () => { unmount(app); target.remove() } })
|
|
60
|
+
},
|
|
61
|
+
} as any,
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
for (const t of registeredCellEditorTypes()) unregisterCellEditor(t)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('registerBuiltinEditors in SvGrid', () => {
|
|
72
|
+
it("mounts SvDurationInput for an editorType 'duration' cell", async () => {
|
|
73
|
+
registerBuiltinEditors()
|
|
74
|
+
const { api, target, destroy } = await mountGrid()
|
|
75
|
+
try {
|
|
76
|
+
api.startEditing(0, 'estimate')
|
|
77
|
+
// SvGridCellEditor is a lazy chunk; poll instead of fixing a tick count.
|
|
78
|
+
await vi.waitFor(() => expect(target.querySelector('.sv-dur__input')).not.toBeNull())
|
|
79
|
+
} finally {
|
|
80
|
+
destroy()
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -30,9 +30,26 @@ afterEach(() => {
|
|
|
30
30
|
describe('registerBuiltinEditors', () => {
|
|
31
31
|
it('registers the config-free editor types', () => {
|
|
32
32
|
const types = registerBuiltinEditors()
|
|
33
|
-
expect(types).toEqual(['otp', 'duration'])
|
|
33
|
+
expect(types).toEqual(['otp', 'duration', 'richtext'])
|
|
34
34
|
expect(hasCellEditor('otp')).toBe(true)
|
|
35
35
|
expect(hasCellEditor('duration')).toBe(true)
|
|
36
|
+
expect(hasCellEditor('richtext')).toBe(true)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('sanitizes the rich-text value on the way in and on the way out', () => {
|
|
40
|
+
registerBuiltinEditors()
|
|
41
|
+
const onChange = vi.fn()
|
|
42
|
+
// Markup already sitting in the row data is cleaned before the editor sees
|
|
43
|
+
// it, so opening a poisoned cell cannot execute anything either.
|
|
44
|
+
const props = resolveEditorProps(
|
|
45
|
+
getCellEditor('richtext')!,
|
|
46
|
+
ctx({ value: '<p onclick="alert(1)">hi</p><script>alert(2)</script>', onChange }),
|
|
47
|
+
)
|
|
48
|
+
expect(props.value).toBe('<p>hi</p>')
|
|
49
|
+
;(props.onChange as (v: string) => void)('<b>ok</b><img src=x onerror="alert(3)">')
|
|
50
|
+
const stored = onChange.mock.calls[0]![0] as string
|
|
51
|
+
expect(stored).toContain('<b>ok</b>')
|
|
52
|
+
expect(stored).not.toContain('onerror')
|
|
36
53
|
})
|
|
37
54
|
|
|
38
55
|
it("maps the OTP editor's onComplete to commit", () => {
|
package/src/builtin-editors.ts
CHANGED
|
@@ -25,10 +25,12 @@
|
|
|
25
25
|
import { registerCellEditor } from './editor-registry'
|
|
26
26
|
import SvOtpInput from './SvOtpInput.svelte'
|
|
27
27
|
import SvDurationInput from './SvDurationInput.svelte'
|
|
28
|
+
import SvRichText from './SvRichText.svelte'
|
|
29
|
+
import { sanitizeHtml } from './sanitize-html'
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
|
-
* Register the config-free UI-kit editors (`otp`, `duration`) as
|
|
31
|
-
* editors. Idempotent. Returns the list of type names it registered.
|
|
32
|
+
* Register the config-free UI-kit editors (`otp`, `duration`, `richtext`) as
|
|
33
|
+
* grid cell editors. Idempotent. Returns the list of type names it registered.
|
|
32
34
|
*/
|
|
33
35
|
export function registerBuiltinEditors(): string[] {
|
|
34
36
|
registerCellEditor('otp', {
|
|
@@ -52,5 +54,19 @@ export function registerBuiltinEditors(): string[] {
|
|
|
52
54
|
onCancel: () => ctx.onCancel(),
|
|
53
55
|
}),
|
|
54
56
|
})
|
|
55
|
-
|
|
57
|
+
// Rich text carries a toolbar, so give the column a taller row (or edit it
|
|
58
|
+
// from a detail panel) - it does not fit a default single-line row.
|
|
59
|
+
//
|
|
60
|
+
// Both the value handed to the editor and the value coming back are
|
|
61
|
+
// sanitized. The stored string is the one SvRichCell later paints, so
|
|
62
|
+
// cleaning it on the way in means a pasted payload never reaches row data.
|
|
63
|
+
registerCellEditor('richtext', {
|
|
64
|
+
component: SvRichText,
|
|
65
|
+
props: (ctx) => ({
|
|
66
|
+
value: sanitizeHtml((ctx.value ?? '') as string),
|
|
67
|
+
autofocus: true,
|
|
68
|
+
onChange: (html: string) => ctx.onChange(sanitizeHtml(html)),
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
return ['otp', 'duration', 'richtext']
|
|
56
72
|
}
|
package/src/cell-formatting.ts
CHANGED
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helpers for SvGrid column `format` presets (numbers, currency, percent, date patterns).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** Map shortcut date patterns to Intl options (merged with caller `options`). */
|
|
6
|
-
export function resolveDatePattern(
|
|
7
|
-
pattern: string | undefined,
|
|
8
|
-
kind: 'date' | 'datetime',
|
|
9
|
-
): Intl.DateTimeFormatOptions | undefined {
|
|
10
|
-
if (!pattern) return undefined
|
|
11
|
-
|
|
12
|
-
const p = pattern.trim()
|
|
13
|
-
|
|
14
|
-
switch (p) {
|
|
15
|
-
/** Short numeric local date (no weekday). */
|
|
16
|
-
case 'd':
|
|
17
|
-
return kind === 'datetime'
|
|
18
|
-
? {
|
|
19
|
-
year: 'numeric',
|
|
20
|
-
month: 'numeric',
|
|
21
|
-
day: 'numeric',
|
|
22
|
-
hour: 'numeric',
|
|
23
|
-
minute: 'numeric',
|
|
24
|
-
}
|
|
25
|
-
: { year: 'numeric', month: 'numeric', day: 'numeric' }
|
|
26
|
-
/** Long spelled-out date (+ time if datetime). */
|
|
27
|
-
case 'D':
|
|
28
|
-
return kind === 'datetime'
|
|
29
|
-
? {
|
|
30
|
-
weekday: 'long',
|
|
31
|
-
year: 'numeric',
|
|
32
|
-
month: 'long',
|
|
33
|
-
day: 'numeric',
|
|
34
|
-
hour: 'numeric',
|
|
35
|
-
minute: 'numeric',
|
|
36
|
-
}
|
|
37
|
-
: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
|
|
38
|
-
/** ISO-friendly yyyy-mm-dd (with time when datetime). */
|
|
39
|
-
case 'y-m-d':
|
|
40
|
-
return kind === 'datetime'
|
|
41
|
-
? {
|
|
42
|
-
year: 'numeric',
|
|
43
|
-
month: '2-digit',
|
|
44
|
-
day: '2-digit',
|
|
45
|
-
hour: '2-digit',
|
|
46
|
-
minute: '2-digit',
|
|
47
|
-
hour12: false,
|
|
48
|
-
}
|
|
49
|
-
: { year: 'numeric', month: '2-digit', day: '2-digit' }
|
|
50
|
-
case 'medium':
|
|
51
|
-
return { dateStyle: 'medium', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
52
|
-
case 'short':
|
|
53
|
-
return { dateStyle: 'short', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
54
|
-
case 'long':
|
|
55
|
-
return { dateStyle: 'long', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
56
|
-
default:
|
|
57
|
-
return undefined
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
type NumericFormatInput = {
|
|
62
|
-
type: 'number' | 'currency' | 'percent'
|
|
63
|
-
locales?: string | readonly string[]
|
|
64
|
-
currency?: string
|
|
65
|
-
/** When type is percent: treat value as 0–100 instead of Intl’s 0–1 fraction */
|
|
66
|
-
valueIsPercentPoints?: boolean
|
|
67
|
-
options?: Intl.NumberFormatOptions
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* `Intl.NumberFormat` is one of the most expensive constructors in the
|
|
72
|
-
* platform - building one per cell on every scroll makes large grids
|
|
73
|
-
* noticeably stutter. Cache instances by config signature (locale + style
|
|
74
|
-
* + currency + options).
|
|
75
|
-
*/
|
|
76
|
-
const numberFormatterCache = new Map<string, Intl.NumberFormat>()
|
|
77
|
-
function getNumberFormatter(
|
|
78
|
-
locales: string | readonly string[] | undefined,
|
|
79
|
-
options: Intl.NumberFormatOptions,
|
|
80
|
-
): Intl.NumberFormat {
|
|
81
|
-
// Stable key - for normal column configs the options object is reused
|
|
82
|
-
// (declared in module scope), so this stringifies a small static shape.
|
|
83
|
-
const key =
|
|
84
|
-
(Array.isArray(locales) ? locales.join(',') : locales ?? '') +
|
|
85
|
-
'|' +
|
|
86
|
-
(options.style ?? '') +
|
|
87
|
-
'|' +
|
|
88
|
-
(options.currency ?? '') +
|
|
89
|
-
'|' +
|
|
90
|
-
(options.minimumFractionDigits ?? '') +
|
|
91
|
-
'|' +
|
|
92
|
-
(options.maximumFractionDigits ?? '') +
|
|
93
|
-
'|' +
|
|
94
|
-
(options.useGrouping ?? '') +
|
|
95
|
-
'|' +
|
|
96
|
-
(options.currencyDisplay ?? '') +
|
|
97
|
-
'|' +
|
|
98
|
-
(options.notation ?? '')
|
|
99
|
-
let fmt = numberFormatterCache.get(key)
|
|
100
|
-
if (!fmt) {
|
|
101
|
-
fmt = new Intl.NumberFormat(locales as string | string[] | undefined, options)
|
|
102
|
-
numberFormatterCache.set(key, fmt)
|
|
103
|
-
}
|
|
104
|
-
return fmt
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Cache of `Intl.DateTimeFormat` by `(locale, options)` signature. */
|
|
108
|
-
const dateFormatterCache = new Map<string, Intl.DateTimeFormat>()
|
|
109
|
-
/** A cached `Intl.DateTimeFormat` for the locale and options given. Cached because
|
|
110
|
-
* constructing one per cell is the dominant cost when formatting a date column. */
|
|
111
|
-
export function getDateFormatter(
|
|
112
|
-
locales: string | readonly string[] | undefined,
|
|
113
|
-
options: Intl.DateTimeFormatOptions,
|
|
114
|
-
): Intl.DateTimeFormat {
|
|
115
|
-
const key =
|
|
116
|
-
(Array.isArray(locales) ? locales.join(',') : locales ?? '') +
|
|
117
|
-
'|' +
|
|
118
|
-
(options.dateStyle ?? '') +
|
|
119
|
-
'|' +
|
|
120
|
-
(options.timeStyle ?? '') +
|
|
121
|
-
'|' +
|
|
122
|
-
(options.year ?? '') +
|
|
123
|
-
'|' +
|
|
124
|
-
(options.month ?? '') +
|
|
125
|
-
'|' +
|
|
126
|
-
(options.day ?? '') +
|
|
127
|
-
'|' +
|
|
128
|
-
(options.hour ?? '') +
|
|
129
|
-
'|' +
|
|
130
|
-
(options.minute ?? '') +
|
|
131
|
-
'|' +
|
|
132
|
-
(options.second ?? '') +
|
|
133
|
-
'|' +
|
|
134
|
-
(options.weekday ?? '') +
|
|
135
|
-
'|' +
|
|
136
|
-
(options.hour12 ?? '')
|
|
137
|
-
let fmt = dateFormatterCache.get(key)
|
|
138
|
-
if (!fmt) {
|
|
139
|
-
fmt = new Intl.DateTimeFormat(locales as string | string[] | undefined, options)
|
|
140
|
-
dateFormatterCache.set(key, fmt)
|
|
141
|
-
}
|
|
142
|
-
return fmt
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Format a numeric value for number / currency / percent column formats. */
|
|
146
|
-
export function formatNumericWithConfig(value: unknown, config: NumericFormatInput): string {
|
|
147
|
-
const n = typeof value === 'number' ? value : Number(value)
|
|
148
|
-
if (!Number.isFinite(n)) return String(value ?? '')
|
|
149
|
-
|
|
150
|
-
const locales = config.locales
|
|
151
|
-
|
|
152
|
-
if (config.type === 'currency') {
|
|
153
|
-
return getNumberFormatter(locales, {
|
|
154
|
-
style: 'currency',
|
|
155
|
-
currency: config.currency ?? 'USD',
|
|
156
|
-
...config.options,
|
|
157
|
-
}).format(n)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (config.type === 'percent') {
|
|
161
|
-
const frac = config.valueIsPercentPoints === true ? n / 100 : n
|
|
162
|
-
return getNumberFormatter(locales, {
|
|
163
|
-
style: 'percent',
|
|
164
|
-
minimumFractionDigits: 0,
|
|
165
|
-
maximumFractionDigits: 2,
|
|
166
|
-
...config.options,
|
|
167
|
-
}).format(frac)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return getNumberFormatter(locales, config.options ?? {}).format(n)
|
|
171
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for SvGrid column `format` presets (numbers, currency, percent, date patterns).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Map shortcut date patterns to Intl options (merged with caller `options`). */
|
|
6
|
+
export function resolveDatePattern(
|
|
7
|
+
pattern: string | undefined,
|
|
8
|
+
kind: 'date' | 'datetime',
|
|
9
|
+
): Intl.DateTimeFormatOptions | undefined {
|
|
10
|
+
if (!pattern) return undefined
|
|
11
|
+
|
|
12
|
+
const p = pattern.trim()
|
|
13
|
+
|
|
14
|
+
switch (p) {
|
|
15
|
+
/** Short numeric local date (no weekday). */
|
|
16
|
+
case 'd':
|
|
17
|
+
return kind === 'datetime'
|
|
18
|
+
? {
|
|
19
|
+
year: 'numeric',
|
|
20
|
+
month: 'numeric',
|
|
21
|
+
day: 'numeric',
|
|
22
|
+
hour: 'numeric',
|
|
23
|
+
minute: 'numeric',
|
|
24
|
+
}
|
|
25
|
+
: { year: 'numeric', month: 'numeric', day: 'numeric' }
|
|
26
|
+
/** Long spelled-out date (+ time if datetime). */
|
|
27
|
+
case 'D':
|
|
28
|
+
return kind === 'datetime'
|
|
29
|
+
? {
|
|
30
|
+
weekday: 'long',
|
|
31
|
+
year: 'numeric',
|
|
32
|
+
month: 'long',
|
|
33
|
+
day: 'numeric',
|
|
34
|
+
hour: 'numeric',
|
|
35
|
+
minute: 'numeric',
|
|
36
|
+
}
|
|
37
|
+
: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
|
|
38
|
+
/** ISO-friendly yyyy-mm-dd (with time when datetime). */
|
|
39
|
+
case 'y-m-d':
|
|
40
|
+
return kind === 'datetime'
|
|
41
|
+
? {
|
|
42
|
+
year: 'numeric',
|
|
43
|
+
month: '2-digit',
|
|
44
|
+
day: '2-digit',
|
|
45
|
+
hour: '2-digit',
|
|
46
|
+
minute: '2-digit',
|
|
47
|
+
hour12: false,
|
|
48
|
+
}
|
|
49
|
+
: { year: 'numeric', month: '2-digit', day: '2-digit' }
|
|
50
|
+
case 'medium':
|
|
51
|
+
return { dateStyle: 'medium', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
52
|
+
case 'short':
|
|
53
|
+
return { dateStyle: 'short', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
54
|
+
case 'long':
|
|
55
|
+
return { dateStyle: 'long', timeStyle: kind === 'datetime' ? 'short' : undefined }
|
|
56
|
+
default:
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type NumericFormatInput = {
|
|
62
|
+
type: 'number' | 'currency' | 'percent'
|
|
63
|
+
locales?: string | readonly string[]
|
|
64
|
+
currency?: string
|
|
65
|
+
/** When type is percent: treat value as 0–100 instead of Intl’s 0–1 fraction */
|
|
66
|
+
valueIsPercentPoints?: boolean
|
|
67
|
+
options?: Intl.NumberFormatOptions
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* `Intl.NumberFormat` is one of the most expensive constructors in the
|
|
72
|
+
* platform - building one per cell on every scroll makes large grids
|
|
73
|
+
* noticeably stutter. Cache instances by config signature (locale + style
|
|
74
|
+
* + currency + options).
|
|
75
|
+
*/
|
|
76
|
+
const numberFormatterCache = new Map<string, Intl.NumberFormat>()
|
|
77
|
+
function getNumberFormatter(
|
|
78
|
+
locales: string | readonly string[] | undefined,
|
|
79
|
+
options: Intl.NumberFormatOptions,
|
|
80
|
+
): Intl.NumberFormat {
|
|
81
|
+
// Stable key - for normal column configs the options object is reused
|
|
82
|
+
// (declared in module scope), so this stringifies a small static shape.
|
|
83
|
+
const key =
|
|
84
|
+
(Array.isArray(locales) ? locales.join(',') : locales ?? '') +
|
|
85
|
+
'|' +
|
|
86
|
+
(options.style ?? '') +
|
|
87
|
+
'|' +
|
|
88
|
+
(options.currency ?? '') +
|
|
89
|
+
'|' +
|
|
90
|
+
(options.minimumFractionDigits ?? '') +
|
|
91
|
+
'|' +
|
|
92
|
+
(options.maximumFractionDigits ?? '') +
|
|
93
|
+
'|' +
|
|
94
|
+
(options.useGrouping ?? '') +
|
|
95
|
+
'|' +
|
|
96
|
+
(options.currencyDisplay ?? '') +
|
|
97
|
+
'|' +
|
|
98
|
+
(options.notation ?? '')
|
|
99
|
+
let fmt = numberFormatterCache.get(key)
|
|
100
|
+
if (!fmt) {
|
|
101
|
+
fmt = new Intl.NumberFormat(locales as string | string[] | undefined, options)
|
|
102
|
+
numberFormatterCache.set(key, fmt)
|
|
103
|
+
}
|
|
104
|
+
return fmt
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Cache of `Intl.DateTimeFormat` by `(locale, options)` signature. */
|
|
108
|
+
const dateFormatterCache = new Map<string, Intl.DateTimeFormat>()
|
|
109
|
+
/** A cached `Intl.DateTimeFormat` for the locale and options given. Cached because
|
|
110
|
+
* constructing one per cell is the dominant cost when formatting a date column. */
|
|
111
|
+
export function getDateFormatter(
|
|
112
|
+
locales: string | readonly string[] | undefined,
|
|
113
|
+
options: Intl.DateTimeFormatOptions,
|
|
114
|
+
): Intl.DateTimeFormat {
|
|
115
|
+
const key =
|
|
116
|
+
(Array.isArray(locales) ? locales.join(',') : locales ?? '') +
|
|
117
|
+
'|' +
|
|
118
|
+
(options.dateStyle ?? '') +
|
|
119
|
+
'|' +
|
|
120
|
+
(options.timeStyle ?? '') +
|
|
121
|
+
'|' +
|
|
122
|
+
(options.year ?? '') +
|
|
123
|
+
'|' +
|
|
124
|
+
(options.month ?? '') +
|
|
125
|
+
'|' +
|
|
126
|
+
(options.day ?? '') +
|
|
127
|
+
'|' +
|
|
128
|
+
(options.hour ?? '') +
|
|
129
|
+
'|' +
|
|
130
|
+
(options.minute ?? '') +
|
|
131
|
+
'|' +
|
|
132
|
+
(options.second ?? '') +
|
|
133
|
+
'|' +
|
|
134
|
+
(options.weekday ?? '') +
|
|
135
|
+
'|' +
|
|
136
|
+
(options.hour12 ?? '')
|
|
137
|
+
let fmt = dateFormatterCache.get(key)
|
|
138
|
+
if (!fmt) {
|
|
139
|
+
fmt = new Intl.DateTimeFormat(locales as string | string[] | undefined, options)
|
|
140
|
+
dateFormatterCache.set(key, fmt)
|
|
141
|
+
}
|
|
142
|
+
return fmt
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Format a numeric value for number / currency / percent column formats. */
|
|
146
|
+
export function formatNumericWithConfig(value: unknown, config: NumericFormatInput): string {
|
|
147
|
+
const n = typeof value === 'number' ? value : Number(value)
|
|
148
|
+
if (!Number.isFinite(n)) return String(value ?? '')
|
|
149
|
+
|
|
150
|
+
const locales = config.locales
|
|
151
|
+
|
|
152
|
+
if (config.type === 'currency') {
|
|
153
|
+
return getNumberFormatter(locales, {
|
|
154
|
+
style: 'currency',
|
|
155
|
+
currency: config.currency ?? 'USD',
|
|
156
|
+
...config.options,
|
|
157
|
+
}).format(n)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (config.type === 'percent') {
|
|
161
|
+
const frac = config.valueIsPercentPoints === true ? n / 100 : n
|
|
162
|
+
return getNumberFormatter(locales, {
|
|
163
|
+
style: 'percent',
|
|
164
|
+
minimumFractionDigits: 0,
|
|
165
|
+
maximumFractionDigits: 2,
|
|
166
|
+
...config.options,
|
|
167
|
+
}).format(frac)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return getNumberFormatter(locales, config.options ?? {}).format(n)
|
|
171
|
+
}
|