@svgrid/grid 2.6.19 → 2.6.21

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 (65) hide show
  1. package/dist/SvGrid.controller.svelte.d.ts +1 -0
  2. package/dist/SvGrid.controller.svelte.js +64 -2
  3. package/dist/SvGrid.svelte +2 -1
  4. package/dist/SvGrid.types.d.ts +150 -0
  5. package/dist/ai.d.ts +28 -0
  6. package/dist/ai.js +6 -0
  7. package/dist/cdn/{GridMenus-B0F9iBrG.js → GridMenus-BfTAKn84.js} +1 -1
  8. package/dist/cdn/{GridMenus-IHK_l7m6.js → GridMenus-C3bJd7w8.js} +1 -1
  9. package/dist/cdn/{src-Cd0tearp.js → src-BYq-qyrp.js} +1012 -999
  10. package/dist/cdn/{src-B1TdiyS8.js → src-DBel9wRZ.js} +1324 -1311
  11. package/dist/cdn/svgrid.js +1 -1
  12. package/dist/cdn/svgrid.svelte-external.js +1 -1
  13. package/dist/cdn/validate-_CDJzgIo.js +75 -0
  14. package/dist/cell-formatting.d.ts +2 -0
  15. package/dist/cell-formatting.js +2 -0
  16. package/dist/chart-export.d.ts +1 -0
  17. package/dist/chart.d.ts +31 -5
  18. package/dist/chart.js +9 -3
  19. package/dist/core.d.ts +197 -0
  20. package/dist/core.js +72 -0
  21. package/dist/createTree.svelte.d.ts +3 -0
  22. package/dist/createTree.svelte.js +1 -0
  23. package/dist/datetime/date-core.d.ts +2 -0
  24. package/dist/datetime/date-restrict.d.ts +1 -0
  25. package/dist/datetime/timezone.d.ts +1 -0
  26. package/dist/dock-manager-model.d.ts +3 -0
  27. package/dist/dock-manager-model.js +1 -0
  28. package/dist/dock-model.d.ts +6 -0
  29. package/dist/dock-model.js +3 -0
  30. package/dist/editor-contract.d.ts +1 -0
  31. package/dist/list-option.d.ts +1 -0
  32. package/dist/positioning.d.ts +2 -0
  33. package/dist/scheduler-ical.d.ts +1 -0
  34. package/dist/scheduler-model.d.ts +1 -0
  35. package/dist/summaries.js +22 -0
  36. package/dist/svgrid-wrapper.types.d.ts +5 -0
  37. package/dist/toast-store.svelte.d.ts +4 -0
  38. package/dist/validate.d.ts +50 -0
  39. package/dist/validate.js +187 -0
  40. package/package.json +4 -1
  41. package/src/SvGrid.controller.svelte.ts +68 -2
  42. package/src/SvGrid.svelte +2 -1
  43. package/src/SvGrid.types.ts +150 -0
  44. package/src/ai.ts +28 -0
  45. package/src/cell-formatting.ts +2 -0
  46. package/src/chart-export.ts +1 -0
  47. package/src/chart.ts +31 -5
  48. package/src/core.ts +207 -0
  49. package/src/createTree.svelte.ts +3 -0
  50. package/src/datetime/date-core.ts +2 -0
  51. package/src/datetime/date-restrict.ts +1 -0
  52. package/src/datetime/timezone.ts +1 -0
  53. package/src/dock-manager-model.ts +3 -0
  54. package/src/dock-model.ts +6 -0
  55. package/src/editor-contract.ts +1 -0
  56. package/src/list-option.ts +1 -0
  57. package/src/positioning.ts +2 -0
  58. package/src/scheduler-ical.ts +1 -0
  59. package/src/scheduler-model.ts +1 -0
  60. package/src/summaries.ts +21 -0
  61. package/src/svgrid-wrapper.types.ts +5 -0
  62. package/src/svgrid.summaries.test.ts +217 -0
  63. package/src/toast-store.svelte.ts +4 -0
  64. package/src/validate.test.ts +207 -0
  65. package/src/validate.ts +269 -0
package/src/core.ts CHANGED
@@ -1,30 +1,79 @@
1
1
  import type { SparklineConfig } from './sparkline'
2
2
  import { resolveColumnId } from './column-id'
3
3
 
4
+ /**
5
+ * The constraint every row type satisfies: an object keyed by string. Your own
6
+ * row type (`type Person = { name: string }`) is what flows through the generics
7
+ * below; this is only the lower bound they are declared against.
8
+ */
4
9
  export type RowData = Record<string, unknown>
10
+
11
+ /**
12
+ * A new value, or a function that derives it from the previous one - the shape
13
+ * every `set*` on the grid accepts, so callers can update state without first
14
+ * reading it.
15
+ *
16
+ * api.setSorting([{ id: 'name', desc: false }])
17
+ * api.setSorting((prev) => [...prev, { id: 'age', desc: true }])
18
+ */
5
19
  export type Updater<T> = T | ((prev: T) => T)
20
+
21
+ /** Active sort clauses, outermost first. `desc: false` is ascending. */
6
22
  export type SortingState = Array<{ id: string; desc: boolean }>
23
+
24
+ /**
25
+ * One column's filter: the column `id`, the `value` being matched, and
26
+ * optionally which comparison to use. `fn` defaults to the column's own type -
27
+ * see {@link filterFns} for the available names.
28
+ */
7
29
  export type ColumnFilter = { id: string; value: unknown; fn?: keyof typeof filterFns }
30
+
31
+ /** Every active column filter. A column with no entry here is unfiltered. */
8
32
  export type ColumnFiltersState = Array<ColumnFilter>
33
+
34
+ /** Current page position. `pageIndex` is 0-based, so page 1 is index 0. */
9
35
  export type PaginationState = { pageIndex: number; pageSize: number }
36
+
37
+ /** Column ids the rows are grouped by, outermost first. */
10
38
  export type GroupingState = Array<string>
39
+
40
+ /** Which rows are expanded, keyed by row id. Absent means collapsed. */
11
41
  export type ExpandedState = Record<string, boolean>
42
+
43
+ /** Which rows are selected, keyed by row id. Absent means unselected. */
12
44
  export type RowSelectionState = Record<string, boolean>
45
+
46
+ /**
47
+ * Where keyboard focus sits. The indices address the *displayed* grid (after
48
+ * sorting, filtering and paging), not the source data.
49
+ */
13
50
  export type ActiveCellState = {
14
51
  rowIndex: number
15
52
  colIndex: number
16
53
  cellId: string | null
17
54
  }
55
+
56
+ /**
57
+ * The set of features a grid has registered, as built by {@link tableFeatures}.
58
+ * Deliberately open: a feature is identified by its key, so the type carries
59
+ * which ones are on without enumerating them.
60
+ */
18
61
  export type TableFeatures = Record<string, unknown>
19
62
 
63
+ /** A cell's value. Unconstrained - a column can hold anything. */
20
64
  export type CellData = unknown
21
65
 
66
+ /** What a column's `header` render function receives. */
22
67
  export type HeaderContext<TData extends RowData> = {
23
68
  header: Header<TData>
24
69
  column: Column<TData>
25
70
  table: SvGrid<TData>
26
71
  }
27
72
 
73
+ /**
74
+ * What a column's `cell` render function receives. `getValue()` applies the
75
+ * column's accessor (`field` or `fieldFn`); `row.original` is the raw object.
76
+ */
28
77
  export type CellContext<TData extends RowData> = {
29
78
  cell: Cell<TData>
30
79
  row: Row<TData>
@@ -84,6 +133,11 @@ export type EditorContext<TData extends RowData> = CellContext<TData> & {
84
133
  cancel: () => void
85
134
  }
86
135
 
136
+ /**
137
+ * Declarative cell formatting, applied through `Intl` - number, currency,
138
+ * percent, date and datetime. Prefer this over a `formatter` function: it is
139
+ * locale-aware, and export and the clipboard reuse the same configuration.
140
+ */
87
141
  export type CellFormatConfig =
88
142
  | {
89
143
  type: 'number'
@@ -119,6 +173,10 @@ export type CellFormatConfig =
119
173
  options?: Intl.DateTimeFormatOptions
120
174
  }
121
175
 
176
+ /**
177
+ * A column's custom display function, for anything {@link CellFormatConfig}
178
+ * cannot express. Returns a string - to render markup, use `cell` instead.
179
+ */
122
180
  export type CellFormatter<TData extends RowData> = (context: {
123
181
  value: unknown
124
182
  row: Row<TData>
@@ -126,6 +184,7 @@ export type CellFormatter<TData extends RowData> = (context: {
126
184
  table: SvGrid<TData>
127
185
  }) => string
128
186
 
187
+ /** A header or cell slot: a literal string, or a function returning renderable content. */
129
188
  export type ColumnDefTemplate<TContext> = string | ((context: TContext) => unknown)
130
189
 
131
190
  /**
@@ -378,6 +437,20 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
378
437
  * is formatted with this column's `format` and shown in the group header.
379
438
  */
380
439
  aggregate?: GroupAggregator<TData>
440
+ /**
441
+ * What this column contributes to the grid's footer summary row (the one
442
+ * turned on with `summary` / `enableRowSummaries`). Takes the same
443
+ * aggregators as {@link aggregate}, and the result is formatted with this
444
+ * column's `format`.
445
+ *
446
+ * Without it the footer falls back to its default: the sum of a numeric
447
+ * column, `Count: N` otherwise. Set `false` to leave the cell blank, which is
448
+ * usually what an actions or checkbox column wants.
449
+ *
450
+ * { field: 'amount', summary: 'avg' }
451
+ * { id: 'actions', summary: false }
452
+ */
453
+ summary?: GroupAggregator<TData> | false
381
454
  /**
382
455
  * Render the cell as an in-cell sparkline chart. The cell value should be
383
456
  * an array of numbers (or a comma/space separated string). Mutually
@@ -445,6 +518,12 @@ export type GridColumnDef<TData extends RowData = RowData> = ColumnDef<TableFeat
445
518
  /** An array of {@link GridColumnDef} - what you pass to `<SvGrid columns={...}>`. */
446
519
  export type GridColumns<TData extends RowData = RowData> = Array<GridColumnDef<TData>>
447
520
 
521
+ /**
522
+ * A resolved column: your {@link ColumnDef} plus everything the grid computed
523
+ * from it - its id, its depth under any group header, and the sort handlers a
524
+ * header needs. This is what you receive in render contexts; the `ColumnDef`
525
+ * is what you wrote.
526
+ */
448
527
  export type Column<TData extends RowData> = {
449
528
  id: string
450
529
  columnDef: ColumnDef<any, TData>
@@ -456,6 +535,11 @@ export type Column<TData extends RowData> = {
456
535
  getToggleSortingHandler: () => () => void
457
536
  }
458
537
 
538
+ /**
539
+ * One header cell. `colSpan` is how many leaf columns it covers, and
540
+ * `isPlaceholder` marks the empty cells that pad a group-header row so the
541
+ * levels line up.
542
+ */
459
543
  export type Header<TData extends RowData> = {
460
544
  id: string
461
545
  isPlaceholder: boolean
@@ -464,11 +548,13 @@ export type Header<TData extends RowData> = {
464
548
  getContext: () => HeaderContext<TData>
465
549
  }
466
550
 
551
+ /** One row of header cells. A grid with grouped columns has several, outermost first. */
467
552
  export type HeaderGroup<TData extends RowData> = {
468
553
  id: string
469
554
  headers: Array<Header<TData>>
470
555
  }
471
556
 
557
+ /** One cell: the intersection of a {@link Row} and a {@link Column}. */
472
558
  export type Cell<TData extends RowData> = {
473
559
  id: string
474
560
  row: Row<TData>
@@ -477,6 +563,13 @@ export type Cell<TData extends RowData> = {
477
563
  getContext: () => CellContext<TData>
478
564
  }
479
565
 
566
+ /**
567
+ * A row in the display model. `original` is your untouched data object;
568
+ * everything else is grid-computed. `index` is the position in the displayed
569
+ * set, so it shifts as sorting and filtering change - key on `id`, not index.
570
+ *
571
+ * Group rows and tree parents carry `subRows`; a plain data row does not.
572
+ */
480
573
  export type Row<TData extends RowData> = {
481
574
  id: string
482
575
  index: number
@@ -494,10 +587,19 @@ export type Row<TData extends RowData> = {
494
587
  getCellValueByColumnId: (columnId: string) => unknown
495
588
  }
496
589
 
590
+ /** The output of the row pipeline: the rows to display, in order. */
497
591
  export type RowModel<TData extends RowData> = {
498
592
  rows: Array<Row<TData>>
499
593
  }
500
594
 
595
+ /**
596
+ * The minimal reactive store behind the headless core - read `state`, write
597
+ * through `setState`, and `subscribe` for changes. Deliberately framework
598
+ * free, which is what lets the core run under plain Node.
599
+ *
600
+ * In Svelte you rarely touch this: `subscribeGrid` wraps it with fine-grained
601
+ * selectors so a component only re-runs for the slice it read.
602
+ */
501
603
  export type Store<T> = {
502
604
  readonly state: T
503
605
  setState: (updater: (prev: T) => T) => void
@@ -522,17 +624,51 @@ function createStore<T>(initial: T): Store<T> {
522
624
  }
523
625
  }
524
626
 
627
+ /**
628
+ * Click-to-sort. Injected by the `sortable` shortcut.
629
+ *
630
+ * This and the five features below are opaque markers: pass the ones you want
631
+ * to {@link tableFeatures} and the grid wires up the matching row model. With
632
+ * `<SvGrid>` you rarely name them - the boolean shortcuts (`sortable`,
633
+ * `filterable`, `pageable`, `groupable`) inject them for you. Reach for them
634
+ * directly when driving the headless core, or when you want a feature on
635
+ * without its UI.
636
+ *
637
+ * The names match TanStack Table v9, so a features object written for it works
638
+ * here unchanged.
639
+ */
525
640
  export const rowSortingFeature = { key: 'rowSortingFeature' }
641
+ /** Per-column filtering. Injected by the `filterable` shortcut. */
526
642
  export const columnFilteringFeature = { key: 'columnFilteringFeature' }
643
+ /** Paging of the row model. Injected by the `pageable` shortcut. */
527
644
  export const rowPaginationFeature = { key: 'rowPaginationFeature' }
645
+ /** Row grouping with aggregation. Injected by the `groupable` shortcut. */
528
646
  export const columnGroupingFeature = { key: 'columnGroupingFeature' }
647
+ /** Row selection state (the checkbox column reads it). */
529
648
  export const rowSelectionFeature = { key: 'rowSelectionFeature' }
649
+ /** Expand / collapse, for tree rows and master-detail. */
530
650
  export const rowExpandingFeature = { key: 'rowExpandingFeature' }
531
651
 
652
+ /**
653
+ * Declare which features a grid uses. Identity at runtime - its whole job is to
654
+ * capture the exact set in the type, so `ColumnDef<typeof features, Row>` knows
655
+ * what is registered and anything you did not register is tree-shaken out.
656
+ *
657
+ * ```ts
658
+ * const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
659
+ * ```
660
+ *
661
+ * Same call signature as TanStack Table v9, so a features object written for it
662
+ * transfers unchanged.
663
+ */
532
664
  export function tableFeatures<T extends TableFeatures>(features: T): T {
533
665
  return features
534
666
  }
535
667
 
668
+ /**
669
+ * Built-in comparators, chosen per column by its data type. `auto` compares as
670
+ * text; set a column's type or supply your own comparator to override.
671
+ */
536
672
  export const sortFns = {
537
673
  auto: (a: unknown, b: unknown) => String(a).localeCompare(String(b)),
538
674
  number: (a: unknown, b: unknown) => Number(a ?? 0) - Number(b ?? 0),
@@ -543,20 +679,37 @@ export const sortFns = {
543
679
  },
544
680
  }
545
681
 
682
+ /**
683
+ * Built-in match functions, named by {@link ColumnFilter}'s `fn`.
684
+ * `includesString` is case-insensitive substring; `equals` is strict identity.
685
+ */
546
686
  export const filterFns = {
547
687
  includesString: (value: unknown, query: string) =>
548
688
  String(value).toLowerCase().includes(query.toLowerCase()),
549
689
  equals: (value: unknown, query: unknown) => value === query,
550
690
  }
551
691
 
692
+ /**
693
+ * One stage of the row pipeline: takes the rows produced so far and returns the
694
+ * next set. Stages compose in the order given to `_rowModels`, so filtering
695
+ * before sorting sorts only what survived the filter.
696
+ */
552
697
  export type RowModelFactory<TData extends RowData> = (args: {
553
698
  table: SvGrid<TData>
554
699
  rows: Array<Row<TData>>
555
700
  }) => Array<Row<TData>>
556
701
 
702
+ /**
703
+ * The identity stage that starts every pipeline. Always required, even when no
704
+ * other stage is: it is what turns your data into rows.
705
+ */
557
706
  export function createCoreRowModel<TData extends RowData>(): RowModelFactory<TData> {
558
707
  return ({ rows }) => rows
559
708
  }
709
+ /**
710
+ * Drops rows that fail the active {@link ColumnFiltersState}. Pairs with
711
+ * `columnFilteringFeature`; without it there are no filters to apply.
712
+ */
560
713
  export function createFilteredRowModel<TData extends RowData>(): RowModelFactory<TData> {
561
714
  return ({ table, rows }) => {
562
715
  const filters: ColumnFiltersState = table.getState().columnFilters ?? []
@@ -573,6 +726,10 @@ export function createFilteredRowModel<TData extends RowData>(): RowModelFactory
573
726
  })
574
727
  }
575
728
  }
729
+ /**
730
+ * Narrows the rows to the current page. Put it LAST: anything after it would
731
+ * only ever see one page of data.
732
+ */
576
733
  export function createPaginatedRowModel<TData extends RowData>(): RowModelFactory<TData> {
577
734
  return ({ table, rows }) => {
578
735
  const pagination = table.getState().pagination ?? { pageIndex: 0, pageSize: rows.length || 10 }
@@ -580,6 +737,10 @@ export function createPaginatedRowModel<TData extends RowData>(): RowModelFactor
580
737
  return rows.slice(start, start + pagination.pageSize)
581
738
  }
582
739
  }
740
+ /**
741
+ * Buckets rows by the active {@link GroupingState} and inserts a group row
742
+ * ahead of each bucket, carrying that bucket's aggregates.
743
+ */
583
744
  export function createGroupedRowModel<TData extends RowData>(): RowModelFactory<TData> {
584
745
  return ({ table, rows }) => {
585
746
  const grouping: GroupingState = table.getState().grouping ?? []
@@ -683,6 +844,14 @@ export function createGroupedRowModel<TData extends RowData>(): RowModelFactory<
683
844
  return buildGroups(rows, 0, 0, 'group')
684
845
  }
685
846
  }
847
+ /**
848
+ * How to read a hierarchy out of FLAT rows: each row names its parent, and the
849
+ * grid reconstructs the tree. Rows whose parent id matches nothing become roots
850
+ * rather than disappearing.
851
+ *
852
+ * For nested source data (`children: [...]`), flatten it first with
853
+ * {@link flattenTreeData}.
854
+ */
686
855
  export type TreeRowModelOptions = {
687
856
  /** Field holding each row's parent id. Rows with no parent are roots. */
688
857
  parentField: string
@@ -761,6 +930,11 @@ export function createTreeRowModel<TData extends RowData>(
761
930
  }
762
931
  }
763
932
 
933
+ /**
934
+ * How to flatten NESTED source data into the parent-id shape tree rows need.
935
+ * `parentField` is written onto each row, so point `treeData.parentField` at
936
+ * the same name afterwards.
937
+ */
764
938
  export type FlattenTreeOptions = {
765
939
  /** Field holding an array of child objects. */
766
940
  childrenField: string
@@ -796,6 +970,10 @@ export function flattenTreeData<T extends RowData>(
796
970
  return out
797
971
  }
798
972
 
973
+ /**
974
+ * Hides the descendants of collapsed rows. Needed for grouping, tree data and
975
+ * master-detail alike - all three are the same expand/collapse mechanism.
976
+ */
799
977
  export function createExpandedRowModel<TData extends RowData>(): RowModelFactory<TData> {
800
978
  return ({ table, rows }) => {
801
979
  const expanded: ExpandedState = table.getState().expanded ?? {}
@@ -810,6 +988,11 @@ export function createExpandedRowModel<TData extends RowData>(): RowModelFactory
810
988
  return flattened
811
989
  }
812
990
  }
991
+ /**
992
+ * Orders rows by the active {@link SortingState}. Pass your own comparators to
993
+ * override the built-in {@link sortFns} - useful for locale-aware or
994
+ * domain-specific ordering.
995
+ */
813
996
  export function createSortedRowModel<TData extends RowData>(
814
997
  localSortFns: typeof sortFns = sortFns,
815
998
  ): RowModelFactory<TData> {
@@ -840,6 +1023,14 @@ export function createSortedRowModel<TData extends RowData>(
840
1023
  }
841
1024
  }
842
1025
 
1026
+ /**
1027
+ * Everything {@link createSvGridCore} accepts: the data and columns, the
1028
+ * features and row models that make up the pipeline, and an `on*Change`
1029
+ * callback per piece of state for controlled use.
1030
+ *
1031
+ * `<SvGrid>` builds this for you from its props - you only construct it
1032
+ * directly when driving the headless core.
1033
+ */
843
1034
  export type SvGridOptions<TFeatures extends TableFeatures, TData extends RowData> = {
844
1035
  _features: TFeatures
845
1036
  _rowModels?: {
@@ -869,6 +1060,13 @@ export type SvGridOptions<TFeatures extends TableFeatures, TData extends RowData
869
1060
  onActiveCellChange?: (updater: Updater<ActiveCellState>) => void
870
1061
  }
871
1062
 
1063
+ /**
1064
+ * The headless grid instance: the state stores plus the read methods a renderer
1065
+ * needs (`getHeaderGroups()`, `getRowModel()`, the `set*` writers).
1066
+ *
1067
+ * Framework free by design - `<SvGrid>` is one renderer over this, and you can
1068
+ * write another. See the "Why headless?" guide.
1069
+ */
872
1070
  export type SvGrid<TData extends RowData> = {
873
1071
  store: Store<Record<string, any>>
874
1072
  optionsStore: Store<Record<string, any>>
@@ -892,6 +1090,14 @@ type InternalGrid<TData extends RowData> = SvGrid<TData> & {
892
1090
  getAllColumns: () => Array<Column<TData>>
893
1091
  }
894
1092
 
1093
+ /**
1094
+ * Build a headless grid: state, the row pipeline, and the read methods, with no
1095
+ * DOM and no Svelte. This is the engine `<SvGrid>` renders.
1096
+ *
1097
+ * Most callers want `createSvGrid` (the runes-aware wrapper) or the component
1098
+ * itself; reach for this when you are writing your own renderer or running the
1099
+ * pipeline outside a browser.
1100
+ */
895
1101
  export function createSvGridCore<TFeatures extends TableFeatures, TData extends RowData>(
896
1102
  options: SvGridOptions<TFeatures, TData>,
897
1103
  ): SvGrid<TData> {
@@ -1256,6 +1462,7 @@ export function createSvGridCore<TFeatures extends TableFeatures, TData extends
1256
1462
  return grid
1257
1463
  }
1258
1464
 
1465
+ /** Narrowing helper for the many options that accept a value or a function. */
1259
1466
  export function isFunction(value: unknown): value is (...args: Array<any>) => any {
1260
1467
  return typeof value === 'function'
1261
1468
  }
@@ -31,6 +31,7 @@ export type TreeNode = {
31
31
  lazy?: boolean
32
32
  }
33
33
 
34
+ /** A node's checkbox state. `'indeterminate'` means some but not all descendants are checked. */
34
35
  export type CheckState = 'checked' | 'indeterminate' | 'unchecked'
35
36
 
36
37
  /** A single visible (flattened) tree row. */
@@ -145,6 +146,7 @@ export type TreeConfig = {
145
146
  filter?: () => string | undefined
146
147
  }
147
148
 
149
+ /** Build the headless tree model: expansion, selection and checkbox cascading, with no markup. */
148
150
  export function createTree(config: TreeConfig) {
149
151
  const nodes = () => config.nodes()
150
152
  const checkable = () => config.checkable?.() ?? false
@@ -316,4 +318,5 @@ export function createTree(config: TreeConfig) {
316
318
  }
317
319
  }
318
320
 
321
+ /** The headless tree instance returned by {@link createTree}. */
319
322
  export type Tree = ReturnType<typeof createTree>
@@ -10,6 +10,7 @@
10
10
  * `firstDayOfWeek`.
11
11
  */
12
12
 
13
+ /** Anything the date helpers accept: a `Date`, epoch milliseconds, or a parseable string. */
13
14
  export type DateLike = Date | number | string
14
15
 
15
16
  /** Coerce a Date | epoch-ms | parseable string to a Date, or null if invalid. */
@@ -137,6 +138,7 @@ export function centuryRange(year: number): { start: number; end: number } {
137
138
  return { start, end: start + 99 }
138
139
  }
139
140
 
141
+ /** One cell of a month grid, including the leading and trailing days from adjacent months. */
140
142
  export type MonthMatrixCell = {
141
143
  date: Date
142
144
  /** In the displayed month (vs. leading/trailing days of adjacent months). */
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { compareDay, isSameDay, toDate, type DateLike } from './date-core'
7
7
 
8
+ /** Which dates a picker allows: bounds, an explicit disabled set, and disabled weekdays. */
8
9
  export type RestrictOptions = {
9
10
  min?: DateLike | null
10
11
  max?: DateLike | null
@@ -12,6 +12,7 @@
12
12
  * headless contexts that forbid `Date.now()`).
13
13
  */
14
14
 
15
+ /** A wall-clock time broken into fields, as it reads in a specific time zone. */
15
16
  export type ZoneParts = {
16
17
  year: number
17
18
  month: number // 1-12
@@ -31,6 +31,7 @@ import {
31
31
  type IdGen,
32
32
  } from './dock-model'
33
33
 
34
+ /** Which edge a pane docks against when dropped. */
34
35
  export type DockSide = Exclude<DockZone, 'center'>
35
36
 
36
37
  /** A floating window: one tabs leaf shown in a movable/resizable frame. */
@@ -58,6 +59,7 @@ export type AutoHideEntry = {
58
59
  size: number
59
60
  }
60
61
 
62
+ /** The whole dock manager: its layout tree plus floating and pinned panes. */
61
63
  export type DockManagerState = {
62
64
  main: DockNode | null
63
65
  floating: FloatWindow[]
@@ -74,6 +76,7 @@ export type PaneLocation =
74
76
  | { kind: 'floating'; windowId: string }
75
77
  | { kind: 'autoHide'; entryId: string }
76
78
 
79
+ /** Find a pane in the layout tree by id, returning it with its parent for mutation. */
77
80
  export function locatePane(state: DockManagerState, paneId: string): PaneLocation | null {
78
81
  if (state.main && findTabsWithPane(state.main, paneId)) return { kind: 'main' }
79
82
  for (const w of state.floating) {
package/src/dock-model.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  * without a browser. Modelled on Smart's `smart-layout` group/item structure.
14
14
  */
15
15
 
16
+ /** One dockable pane: its id, title, and the state a tab needs to render. */
16
17
  export type DockPane = {
17
18
  id: string
18
19
  title: string
@@ -40,15 +41,18 @@ export type DockGroup = {
40
41
  sizes: number[]
41
42
  }
42
43
 
44
+ /** A node in the layout tree - either a split group or a tabbed leaf. */
43
45
  export type DockNode = DockGroup | DockTabs
44
46
 
45
47
  /** Where a dragged pane lands relative to a target leaf. */
46
48
  export type DockZone = 'left' | 'right' | 'top' | 'bottom' | 'center'
47
49
 
50
+ /** Supplies ids for newly created nodes, so layouts stay deterministic in tests. */
48
51
  export type IdGen = () => string
49
52
 
50
53
  // ---- construction helpers -------------------------------------------------
51
54
 
55
+ /** Build a {@link DockPane}. */
52
56
  export function pane(id: string, title: string, closable = true): DockPane {
53
57
  return { id, title, closable }
54
58
  }
@@ -58,10 +62,12 @@ export function leafMinSize(node: DockTabs): number {
58
62
  return node.panes.reduce((m, p) => Math.max(m, p.minSize ?? 0), 0)
59
63
  }
60
64
 
65
+ /** Build a tabbed leaf holding the given panes. */
61
66
  export function tabs(genId: IdGen, panes: DockPane[], active = 0): DockTabs {
62
67
  return { type: 'tabs', id: genId(), panes, active: clampIndex(active, panes.length) }
63
68
  }
64
69
 
70
+ /** Build a split group: panes or nested groups laid out in a row or column. */
65
71
  export function group(
66
72
  genId: IdGen,
67
73
  direction: 'row' | 'column',
@@ -10,6 +10,7 @@
10
10
  * Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
11
11
  */
12
12
 
13
+ /** How much room a cell editor asks for, so the grid can size its overlay. */
13
14
  export type EditorSize = 'sm' | 'md' | 'lg'
14
15
 
15
16
  /**
@@ -71,6 +71,7 @@ export type VirtualListRow =
71
71
  | { type: 'group'; label: string; size: number }
72
72
  | { type: 'option'; opt: IndexedOption; size: number }
73
73
 
74
+ /** A flattened option list plus its measurements, for virtualizing long dropdowns. */
74
75
  export type FlatVirtualModel = {
75
76
  /** Group headings + options in render order, each with its px height. */
76
77
  entries: VirtualListRow[]
@@ -35,6 +35,7 @@ export type Rect = { x: number; y: number; width: number; height: number }
35
35
  /** The available viewport (defaults to `window` in the browser). */
36
36
  export type Viewport = { width: number; height: number }
37
37
 
38
+ /** Where to put a floating element: its preferred side, offsets, and collision behaviour. */
38
39
  export type ComputePositionOptions = {
39
40
  /** Preferred placement. Default `'bottom-start'`. */
40
41
  placement?: Placement
@@ -58,6 +59,7 @@ export type ComputePositionOptions = {
58
59
  minMainAxis?: number
59
60
  }
60
61
 
62
+ /** The resolved position, including the side actually used after collision handling. */
61
63
  export type ComputePositionResult = {
62
64
  /** Floating left, in viewport (fixed-position) coordinates. */
63
65
  x: number
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { RecurrenceRule } from './recurrence'
9
9
 
10
+ /** One VEVENT: the fields SvGrid reads from and writes to an iCalendar feed. */
10
11
  export type ICalEvent = {
11
12
  uid?: string
12
13
  title: string
@@ -444,6 +444,7 @@ export type DayLayout<TData = unknown> = {
444
444
  overflows: OverflowMarker<TData>[]
445
445
  }
446
446
 
447
+ /** Tuning for event layout: how overlapping events share horizontal space. */
447
448
  export type LayoutOptions = {
448
449
  dayStartHour?: number
449
450
  dayEndHour?: number
package/src/summaries.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  // reading/writing controller state via the `ctx` handle; the reactive core
3
3
  // ($state/$derived/$effect) stays in the controller.
4
4
  import {
5
+ applyGroupAggregate,
5
6
  type Column,
6
7
  type Row,
7
8
  type RowData,
@@ -45,6 +46,26 @@ export function createSummaries<
45
46
  const fieldFn = def.fieldFn;
46
47
  const field = def.field;
47
48
  const columnId = column.id;
49
+
50
+ // A column that declares its own `summary` opts out of the default
51
+ // sum/count below. Only that column pays for the aggregator dispatch, so
52
+ // a grid that declares none keeps the original hot loop exactly as it was.
53
+ const declared = def.summary;
54
+ if (declared !== undefined) {
55
+ if (declared === false) {
56
+ summary[columnId] = "";
57
+ continue;
58
+ }
59
+ const value = applyGroupAggregate(declared, columnId, rows);
60
+ summary[columnId] =
61
+ typeof value === "number" && Number.isFinite(value)
62
+ ? formatSummaryNumeric(column, value)
63
+ : value == null
64
+ ? ""
65
+ : String(value);
66
+ continue;
67
+ }
68
+
48
69
  let numericSum = 0;
49
70
  let numericCount = 0;
50
71
  for (let i = 0; i < rowCount; i += 1) {
@@ -9,6 +9,7 @@ import type { GridPredicateExpr } from './filtering/predicate-expr'
9
9
  // Aliased to the core union rather than restated: the API surfaces below hand
10
10
  // back whatever the grid actually filtered with, so a hand-maintained subset
11
11
  // here silently mistypes operators like 'endsWith' that reach callers at runtime.
12
+ /** The comparisons a column filter can use, as shown in the filter menu. */
12
13
  export type SvGridFilterOperator = FilterOperator
13
14
 
14
15
  /**
@@ -79,6 +80,7 @@ export type SvGridTransaction<TData> = {
79
80
  remove?: ReadonlyArray<TData | string>
80
81
  }
81
82
 
83
+ /** What a batched `applyTransaction` did: how many rows were added, updated and removed. */
82
84
  export type SvGridTransactionResult = {
83
85
  added: number
84
86
  updated: number
@@ -503,6 +505,7 @@ export type SvGridApi<
503
505
  refresh(): void
504
506
  }
505
507
 
508
+ /** The props `<SvGrid>` accepts. See the SvGrid reference for the full list with defaults. */
506
509
  export type SvGridWrapperProps<
507
510
  TFeatures extends TableFeatures,
508
511
  TData extends RowData,
@@ -553,6 +556,8 @@ export type SvGridWrapperProps<
553
556
  enableCellSelection?: boolean
554
557
  enableInlineEditing?: boolean
555
558
  enableRowSummaries?: boolean
559
+ /** Shortcut alias for `enableRowSummaries`; wins when both are set. */
560
+ summary?: boolean
556
561
  /** Receives the imperative grid API when the component is ready. */
557
562
  onApiReady?: (api: SvGridApi<TFeatures, TData>) => void
558
563
  }