@svelte-lean/table 0.10.0 → 0.12.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @svelte-lean/table
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dba6ebc: `DataTable` takes `measureKey`: change it when what the cells show changes without the rows changing (a masked value revealed in place, a value a cell fetched itself), and the grid measures every row on the page again when idle, so the column widens instead of the value running over the next column. A column only grows; the first value measures nothing extra.
8
+
9
+ ## 0.11.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 84e0055: A paginated `DataTable` loading without rows draws a page of placeholder rows by default (its page size, at most 25) instead of five, so the pager and whatever follows the table stand where they will be once the rows arrive. An unpaginated table still draws five; `skeletonRows` still sets the number.
14
+
15
+ ### Patch Changes
16
+
17
+ - 8ba61b8: A paginated `DataTable` on a page that no longer exists moves to the last page there is. The model already drew the last page, but the state kept the old index, so a manual table whose last page emptied (its last row approved or deleted) showed "page 23 of 23" while it kept asking the server for page 24 and drew it empty. Once the table is neither `loading` nor `fetching`, `state.pageIndex` now follows and `onStateChange` fires.
18
+
3
19
  ## 0.10.0
4
20
 
5
21
  No changes in this release.
@@ -104,7 +104,8 @@
104
104
  onExpandedRowsChange,
105
105
  loading = false,
106
106
  fetching = false,
107
- skeletonRows = 5,
107
+ skeletonRows,
108
+ measureKey,
108
109
  error,
109
110
  onRetry,
110
111
  empty,
@@ -180,6 +181,19 @@
180
181
  const m = $derived({ ...en, ...messages });
181
182
  const cols = $derived(table.visibleColumns);
182
183
  const model = $derived(table.model);
184
+ /**
185
+ * A page that no longer exists is left for the last one there is. When the rows of the last
186
+ * page go — a filter elsewhere, the last row on it approved, the total of a manual table
187
+ * shrinking — the model draws the last page that remains, and the state follows it, so a
188
+ * manual table's `onStateChange` asks for that page instead of drawing the empty one it asked
189
+ * for. Not while the table loads or fetches: a page count read before the answer arrives is
190
+ * not the data's.
191
+ */
192
+ $effect(() => {
193
+ if (loading || fetching || !pagination) return;
194
+ const last = model.pageIndex;
195
+ if (last < s.pageIndex) untrack(() => table.setPage(last));
196
+ });
183
197
  /** The expand toggle has a column of its own when asked for and when anything can expand. */
184
198
  const expander = $derived(expandColumn && (!!rowDetails || !!getSubRows));
185
199
  /** Every column of a full-width row: the leading control columns and the data columns. */
@@ -510,6 +524,8 @@
510
524
  const GRID_MAX = 480;
511
525
  const GRID_MIN = 48;
512
526
  const MEASURE_ROWS = 30;
527
+ /** The most placeholder rows a loading page draws when `skeletonRows` is not given. */
528
+ const MAX_PLACEHOLDERS = 25;
513
529
  const grid = $derived(layout === 'grid');
514
530
  let measured = $state<Record<string, number>>({});
515
531
  function gridWidth(col: Column<T>): number {
@@ -566,6 +582,22 @@
566
582
  whenIdle(() => measure(true, first));
567
583
  });
568
584
  });
585
+ /**
586
+ * A cell's content changed under the same rows (`measureKey`): every row on the page is
587
+ * measured again when idle, so a value revealed in place widens its column instead of
588
+ * running over the next one. Not on the first value: the rows are measured as they come.
589
+ */
590
+ let lastMeasureKey: unknown = undefined;
591
+ let measureKeySeen = false;
592
+ $effect(() => {
593
+ const key = measureKey;
594
+ if (!grid) return;
595
+ untrack(() => {
596
+ if (measureKeySeen && key !== lastMeasureKey) whenIdle(() => measure(true, true, true));
597
+ measureKeySeen = true;
598
+ lastMeasureKey = key;
599
+ });
600
+ });
569
601
  /**
570
602
  * What a body row stands, from the first section once its rows are drawn: the sections the
571
603
  * browser skips assume it, so the page is about as tall before they are drawn as after.
@@ -591,15 +623,15 @@
591
623
  * hidden table inside the root, laid out there with every cell at its max-content width
592
624
  * (`data-measuring`), read, and removed. The live rows are neither restyled nor laid out.
593
625
  */
594
- function measure(whole: boolean, first = true) {
626
+ function measure(whole: boolean, first = true, every = false) {
595
627
  const live = viewport?.firstElementChild as HTMLElement | null;
596
628
  if (!live || !root) return;
597
629
  const leaves = new Set(cols.map((col) => col.id));
598
630
  const rows = [...ownRows()];
599
631
  // The first rows and the header, and for a whole page twenty rows spread over the rest; a pass
600
632
  // over a page whose first rows were measured already (`first` false) takes only the spread.
601
- const sample = first ? rows.slice(0, MEASURE_ROWS) : [];
602
- if (whole && rows.length > MEASURE_ROWS) {
633
+ const sample = every ? rows : first ? rows.slice(0, MEASURE_ROWS) : [];
634
+ if (!every && whole && rows.length > MEASURE_ROWS) {
603
635
  const step = Math.ceil((rows.length - MEASURE_ROWS) / 20);
604
636
  for (let i = MEASURE_ROWS; i < rows.length; i += step) sample.push(rows[i]!);
605
637
  }
@@ -873,9 +905,17 @@
873
905
  manual && rowCount !== undefined ? rowCount + headerRowCount : undefined
874
906
  );
875
907
  const pageSizes = $derived([...new Set([...pageSizeOptions, s.pageSize])]);
908
+ /**
909
+ * A paged table loading draws a page of placeholders (at most 25, a screenful and more), so
910
+ * the pager and whatever follows the table stand where they will be once the rows arrive; five
911
+ * stood in for a page of twenty, and everything below moved when it came.
912
+ */
913
+ const skeletonCount = $derived(
914
+ skeletonRows ?? (pagination && s.pageSize > 0 ? Math.min(s.pageSize, MAX_PLACEHOLDERS) : 5)
915
+ );
876
916
  const skeleton = $derived(
877
917
  loading && !model.rows.length
878
- ? Array.from({ length: Math.max(0, skeletonRows) }, (_, i) => i)
918
+ ? Array.from({ length: Math.max(0, skeletonCount) }, (_, i) => i)
879
919
  : []
880
920
  );
881
921
  const emptyContext = $derived({
package/dist/types.d.ts CHANGED
@@ -268,8 +268,17 @@ export interface TableProps<T> {
268
268
  onExpandedRowsChange?: (rows: RowId[]) => void;
269
269
  loading?: boolean;
270
270
  fetching?: boolean;
271
- /** Skeleton rows drawn while `loading` and no row is present. */
271
+ /**
272
+ * Skeleton rows drawn while `loading` and no row is present. Default: the page size, at most
273
+ * 25, when paginated (so nothing below the table moves when the rows arrive); otherwise 5.
274
+ */
272
275
  skeletonRows?: number;
276
+ /**
277
+ * Changes when what the cells show changes without the rows changing — a masked value
278
+ * revealed in place, a value the cell fetched itself. On each change after the first the grid
279
+ * measures every row on the page again, when idle; a column only grows.
280
+ */
281
+ measureKey?: unknown;
273
282
  error?: string;
274
283
  onRetry?: () => void;
275
284
  empty?: Snippet<[EmptyContext]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-lean/table",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "A Svelte-native table and data-grid engine that ships only the features you use.",
5
5
  "license": "MIT",
6
6
  "author": "Ömer Say",