simple-table-core 3.7.6 → 3.8.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.
@@ -11,3 +11,10 @@ export declare const TABLE_HEADER_CELL_WIDTH_DEFAULT = 150;
11
11
  export declare const PINNED_BORDER_WIDTH = 1;
12
12
  export declare const CHART_COLUMN_TYPES: string[];
13
13
  export declare const OPTIMAL_CHART_COLUMN_WIDTH = 150;
14
+ export declare const AUTO_SIZE_HEAD_SAMPLE_SIZE = 25;
15
+ export declare const AUTO_SIZE_STRIDED_SAMPLE_SIZE = 75;
16
+ export declare const AUTO_SIZE_MAX_WIDTH = 500;
17
+ export declare const AUTO_SIZE_OUTLIER_PERCENTILE = 95;
18
+ export declare const AUTO_SIZE_OUTLIER_THRESHOLD = 0.5;
19
+ export declare const AUTO_SIZE_MIN_SAMPLE_FOR_CLIP = 20;
20
+ export declare const AUTO_SIZE_WIDTH_BUFFER = 2;
@@ -14,6 +14,12 @@ export declare class SimpleTableVanilla {
14
14
  private localRows;
15
15
  private headers;
16
16
  private essentialAccessors;
17
+ /** Accessors of leaf columns that should size to content (width:"auto" or autoSizeColumns). */
18
+ private autoSizeAccessors;
19
+ /** Accessors awaiting a content-fit measurement on the next render. */
20
+ private pendingAutoSize;
21
+ /** Guard against re-entrancy while the auto-size pass re-renders. */
22
+ private isAutoSizing;
17
23
  private currentPage;
18
24
  private scrollTop;
19
25
  private scrollDirection;
@@ -215,6 +221,27 @@ export declare class SimpleTableVanilla {
215
221
  private updateAriaLiveRegion;
216
222
  private getRenderContext;
217
223
  private getRenderState;
224
+ /** A leaf column that should size to content (declared with width:"auto"). */
225
+ private isAutoSizeLeaf;
226
+ private computeAutoSizeAccessors;
227
+ private getAutoSizeStyleRoot;
228
+ /** Immutably write measured pixel widths into the leaf headers. */
229
+ private applyMeasuredWidths;
230
+ /**
231
+ * Measure and apply content-fit widths for any pending "auto" columns, then
232
+ * re-render once at the final widths. Called at the end of render(); the
233
+ * measurement reads the just-rendered (provisional-width) DOM and the
234
+ * corrective render happens in the same synchronous task, so there is no
235
+ * visible width snap.
236
+ */
237
+ private maybeAutoSizeColumns;
238
+ /**
239
+ * Re-run the content-fit measurement for all auto-size columns. Useful for
240
+ * host frameworks (e.g. React) whose custom renderers mount asynchronously:
241
+ * calling this from a layout effect (pre-paint) re-measures once the real
242
+ * renderer DOM is present, so the column fits accurately without flicker.
243
+ */
244
+ refitAutoSizeColumns(): void;
218
245
  private render;
219
246
  update(config: Partial<SimpleTableConfig>): void;
220
247
  /** @deprecated Use {@link update} — same behavior. */
@@ -10,6 +10,12 @@ import { QuickFilterGetter } from "./QuickFilterTypes";
10
10
  export type Accessor = keyof Row | string;
11
11
  export type ColumnType = "string" | "number" | "boolean" | "date" | "enum" | "lineAreaChart" | "barChart" | "other";
12
12
  export type ShowWhen = "parentCollapsed" | "parentExpanded" | "always";
13
+ /**
14
+ * Controls what an auto-sized column (`width: "auto"`) fits to:
15
+ * - "content" (default): fit the wider of the header and the sampled cell content
16
+ * - "header": fit the header label only (ignore cell content)
17
+ */
18
+ export type AutoSizeMode = "content" | "header";
13
19
  export interface ChartOptions {
14
20
  min?: number;
15
21
  max?: number;
@@ -57,6 +63,13 @@ type HeaderObject = {
57
63
  accessor: Accessor;
58
64
  aggregation?: AggregationConfig;
59
65
  align?: "left" | "center" | "right";
66
+ /**
67
+ * When `width` is the string `"auto"`, the column is sized to fit its content
68
+ * on load (and re-fit when row data changes). `autoSizeMode` controls whether
69
+ * it fits the header + cells (`"content"`, default) or the header label only
70
+ * (`"header"`). `minWidth` / `maxWidth` clamp the computed width.
71
+ */
72
+ autoSizeMode?: AutoSizeMode;
60
73
  cellRenderer?: CellRenderer;
61
74
  chartOptions?: ChartOptions;
62
75
  children?: HeaderObject[];
@@ -92,6 +105,10 @@ type HeaderObject = {
92
105
  valueFormatter?: ValueFormatter;
93
106
  valueGetter?: ValueGetter;
94
107
  showWhen?: ShowWhen;
108
+ /**
109
+ * Column width. A number or px/fr/% string sets a fixed/proportional width.
110
+ * The special value `"auto"` sizes the column to fit its content (see `autoSizeMode`).
111
+ */
95
112
  width: number | string;
96
113
  maxWidth?: number | string;
97
114
  };
@@ -1,6 +1,6 @@
1
1
  import type Row from "../../types/Row";
2
2
  import type TableRow from "../../types/TableRow";
3
- import { AbsoluteBodyCell, CellRenderContext } from "./types";
3
+ import { AbsoluteBodyCell, CellRegistryEntry, CellRenderContext } from "./types";
4
4
  export interface CellLiveRef {
5
5
  row: Row;
6
6
  tableRow: TableRow;
@@ -8,6 +8,7 @@ export interface CellLiveRef {
8
8
  }
9
9
  export declare const cellLiveRefMap: WeakMap<HTMLElement, CellLiveRef>;
10
10
  export declare const untrackCellByRow: (rowId: string, cellElement: HTMLElement) => void;
11
+ export declare const unregisterCellFromRegistry: (cellElement: HTMLElement, cellRegistry?: Map<string, CellRegistryEntry>) => void;
11
12
  export declare const createBodyCellElement: (cell: AbsoluteBodyCell, context: CellRenderContext) => HTMLElement;
12
13
  export declare const updateBodyCellPosition: (cellElement: HTMLElement, cell: AbsoluteBodyCell) => void;
13
14
  export declare const updateBodyCellElement: (cellElement: HTMLElement, cell: AbsoluteBodyCell, context: CellRenderContext) => void;
@@ -1,4 +1,29 @@
1
1
  import HeaderObject, { Accessor } from "../types/HeaderObject";
2
+ /**
3
+ * Build the set of row indices to sample for auto-size measurement.
4
+ * Hybrid strategy: the first `headSize` rows (always include the top / initial
5
+ * viewport) plus `stridedSize` rows spread at an even stride across the rest of
6
+ * the dataset, so wide values deeper in the data are caught without scanning
7
+ * every row. Strided (not random) keeps the result deterministic across renders.
8
+ */
9
+ export declare const buildHybridSampleIndices: (rowCount: number, headSize: number, stridedSize: number) => number[];
10
+ /** Linear-interpolated percentile (0-100) of a numeric array. */
11
+ export declare const percentileOf: (values: number[], percentile: number) => number;
12
+ /**
13
+ * Choose a representative "content width" from sampled cell widths, clipping
14
+ * outliers only when they clearly stand apart from the bulk of the data.
15
+ *
16
+ * - Uses the true max when the column is uniform (max close to the percentile),
17
+ * so nothing legitimate gets truncated.
18
+ * - Falls back to the percentile width only when `max > p * (1 + threshold)` and
19
+ * there are enough samples to trust the percentile, so a single rogue 2000px
20
+ * row can't define the whole column.
21
+ */
22
+ export declare const selectContentWidthWithOutlierClip: (widths: number[], options?: {
23
+ percentile?: number;
24
+ threshold?: number;
25
+ minSampleForClip?: number;
26
+ }) => number;
2
27
  /**
3
28
  * Find all leaf headers (headers without children) in a header tree
4
29
  * Takes collapsed state into account - when a header is collapsed, only returns
@@ -9,6 +34,8 @@ export declare const findLeafHeaders: (header: HeaderObject, collapsedHeaders?:
9
34
  export declare const DEFAULT_FR_PX = 150;
10
35
  /** Default total table width used when normalizing fr/% if container width unknown */
11
36
  export declare const DEFAULT_TABLE_WIDTH = 800;
37
+ /** True when a header's width requests content-based auto-sizing (`width: "auto"`). */
38
+ export declare const isAutoWidth: (header: HeaderObject) => boolean;
12
39
  /**
13
40
  * Normalize header widths so that fr and % are converted to pixels.
14
41
  * Call this as soon as headers are received so the rest of the code can assume numeric widths.
@@ -52,7 +79,20 @@ export declare const calculateHeaderContentWidth: (accessor: Accessor, options?:
52
79
  rows?: any[];
53
80
  header?: HeaderObject;
54
81
  maxWidth?: number;
82
+ /** Leading rows always measured (default AUTO_SIZE_HEAD_SAMPLE_SIZE) */
83
+ headSampleSize?: number;
84
+ /** Rows sampled at an even stride across the rest (default AUTO_SIZE_STRIDED_SAMPLE_SIZE) */
85
+ stridedSampleSize?: number;
86
+ /** Back-compat: when set, used as the head sample size (strided defaults to 0) */
55
87
  sampleSize?: number;
88
+ /** Outlier clip percentile (default AUTO_SIZE_OUTLIER_PERCENTILE) */
89
+ outlierPercentile?: number;
90
+ /** Outlier clip threshold fraction (default AUTO_SIZE_OUTLIER_THRESHOLD) */
91
+ outlierThreshold?: number;
92
+ /** When "header", ignore cell content and fit the header label only */
93
+ autoSizeMode?: "content" | "header";
94
+ /** Theme passed to a custom cellRenderer during measurement */
95
+ theme?: any;
56
96
  /** Scope `.st-cell-content` font/padding sampling to this table instance */
57
97
  styleRoot?: ParentNode | null;
58
98
  }) => number;
@@ -0,0 +1,258 @@
1
+ /**
2
+ * AUTO-SIZE COLUMNS TESTS
3
+ *
4
+ * Comprehensive coverage for content-fit columns (`width: "auto"`):
5
+ * - shrink / grow, per-column `maxWidth` cap and `minWidth` floor
6
+ * - `autoSizeMode: "header"`
7
+ * - custom (vanilla) renderers, valueFormatter, valueGetter, chart columns
8
+ * - outliers (single giant value clipped) vs. a legitimate wide minority (kept)
9
+ * - very large datasets (50k rows) with bounded sampling
10
+ * - re-fit on data change, sort stability, empty data, mixed/pinned layouts
11
+ * - a no-flicker stability guard
12
+ * - an exhaustive deterministic unit block for the sampling/percentile/outlier helpers
13
+ *
14
+ * Width assertions rely on real layout, so these run under the Storybook
15
+ * test-runner (Playwright). The helper unit block is layout-independent.
16
+ */
17
+ import { SimpleTableVanilla } from "../../src/index";
18
+ import type { Meta } from "@storybook/html";
19
+ declare const meta: Meta;
20
+ export default meta;
21
+ export declare const AutoWidthShrinksNarrowColumn: {
22
+ parameters: {
23
+ tags: string[];
24
+ };
25
+ render: () => HTMLDivElement & {
26
+ _table?: SimpleTableVanilla | undefined;
27
+ };
28
+ play: ({ canvasElement }: {
29
+ canvasElement: HTMLElement;
30
+ }) => Promise<void>;
31
+ };
32
+ export declare const AutoWidthGrowsForLongContent: {
33
+ parameters: {
34
+ tags: string[];
35
+ };
36
+ render: () => HTMLDivElement & {
37
+ _table?: SimpleTableVanilla | undefined;
38
+ };
39
+ play: ({ canvasElement }: {
40
+ canvasElement: HTMLElement;
41
+ }) => Promise<void>;
42
+ };
43
+ export declare const AutoWidthRespectsMaxWidth: {
44
+ parameters: {
45
+ tags: string[];
46
+ };
47
+ render: () => HTMLDivElement & {
48
+ _table?: SimpleTableVanilla | undefined;
49
+ };
50
+ play: ({ canvasElement }: {
51
+ canvasElement: HTMLElement;
52
+ }) => Promise<void>;
53
+ };
54
+ export declare const AutoWidthRespectsMinWidth: {
55
+ parameters: {
56
+ tags: string[];
57
+ };
58
+ render: () => HTMLDivElement & {
59
+ _table?: SimpleTableVanilla | undefined;
60
+ };
61
+ play: ({ canvasElement }: {
62
+ canvasElement: HTMLElement;
63
+ }) => Promise<void>;
64
+ };
65
+ export declare const AutoSizeHeaderModeIgnoresCells: {
66
+ parameters: {
67
+ tags: string[];
68
+ };
69
+ render: () => HTMLDivElement & {
70
+ _table?: SimpleTableVanilla | undefined;
71
+ };
72
+ play: ({ canvasElement }: {
73
+ canvasElement: HTMLElement;
74
+ }) => Promise<void>;
75
+ };
76
+ export declare const AutoSizeMeasuresCustomRenderer: {
77
+ parameters: {
78
+ tags: string[];
79
+ };
80
+ render: () => HTMLDivElement & {
81
+ _table?: SimpleTableVanilla | undefined;
82
+ };
83
+ play: ({ canvasElement }: {
84
+ canvasElement: HTMLElement;
85
+ }) => Promise<void>;
86
+ };
87
+ export declare const AutoSizeMeasuresFormattedValue: {
88
+ parameters: {
89
+ tags: string[];
90
+ };
91
+ render: () => HTMLDivElement & {
92
+ _table?: SimpleTableVanilla | undefined;
93
+ };
94
+ play: ({ canvasElement }: {
95
+ canvasElement: HTMLElement;
96
+ }) => Promise<void>;
97
+ };
98
+ export declare const AutoSizeMeasuresValueGetter: {
99
+ parameters: {
100
+ tags: string[];
101
+ };
102
+ render: () => HTMLDivElement & {
103
+ _table?: SimpleTableVanilla | undefined;
104
+ };
105
+ play: ({ canvasElement }: {
106
+ canvasElement: HTMLElement;
107
+ }) => Promise<void>;
108
+ };
109
+ export declare const AutoSizeChartColumnUsesChartMinimum: {
110
+ parameters: {
111
+ tags: string[];
112
+ };
113
+ render: () => HTMLDivElement & {
114
+ _table?: SimpleTableVanilla | undefined;
115
+ };
116
+ play: ({ canvasElement }: {
117
+ canvasElement: HTMLElement;
118
+ }) => Promise<void>;
119
+ };
120
+ export declare const AutoSizeClipsSingleOutlier: {
121
+ parameters: {
122
+ tags: string[];
123
+ };
124
+ render: () => HTMLDivElement & {
125
+ _table?: SimpleTableVanilla | undefined;
126
+ };
127
+ play: ({ canvasElement }: {
128
+ canvasElement: HTMLElement;
129
+ }) => Promise<void>;
130
+ };
131
+ export declare const AutoSizeKeepsLegitimateWideMinority: {
132
+ parameters: {
133
+ tags: string[];
134
+ };
135
+ render: () => HTMLDivElement & {
136
+ _table?: SimpleTableVanilla | undefined;
137
+ };
138
+ play: ({ canvasElement }: {
139
+ canvasElement: HTMLElement;
140
+ }) => Promise<void>;
141
+ };
142
+ export declare const AutoSizeClipsFewOutliers: {
143
+ parameters: {
144
+ tags: string[];
145
+ };
146
+ render: () => HTMLDivElement & {
147
+ _table?: SimpleTableVanilla | undefined;
148
+ };
149
+ play: ({ canvasElement }: {
150
+ canvasElement: HTMLElement;
151
+ }) => Promise<void>;
152
+ };
153
+ export declare const AutoSize50kUniformShort: {
154
+ parameters: {
155
+ tags: string[];
156
+ };
157
+ render: () => HTMLDivElement & {
158
+ _table?: SimpleTableVanilla | undefined;
159
+ };
160
+ play: ({ canvasElement }: {
161
+ canvasElement: HTMLElement;
162
+ }) => Promise<void>;
163
+ };
164
+ export declare const AutoSize50kDeepOutlierStaysCompact: {
165
+ parameters: {
166
+ tags: string[];
167
+ };
168
+ render: () => HTMLDivElement & {
169
+ _table?: SimpleTableVanilla | undefined;
170
+ };
171
+ play: ({ canvasElement }: {
172
+ canvasElement: HTMLElement;
173
+ }) => Promise<void>;
174
+ };
175
+ export declare const AutoSize50kWidespreadLongGrows: {
176
+ parameters: {
177
+ tags: string[];
178
+ };
179
+ render: () => HTMLDivElement & {
180
+ _table?: SimpleTableVanilla | undefined;
181
+ };
182
+ play: ({ canvasElement }: {
183
+ canvasElement: HTMLElement;
184
+ }) => Promise<void>;
185
+ };
186
+ export declare const AutoSizeRefitsOnDataChange: {
187
+ parameters: {
188
+ tags: string[];
189
+ };
190
+ render: () => HTMLDivElement & {
191
+ _table?: SimpleTableVanilla | undefined;
192
+ };
193
+ play: ({ canvasElement }: {
194
+ canvasElement: HTMLElement;
195
+ }) => Promise<void>;
196
+ };
197
+ export declare const AutoSizeStableAcrossSort: {
198
+ parameters: {
199
+ tags: string[];
200
+ };
201
+ render: () => HTMLDivElement & {
202
+ _table?: SimpleTableVanilla | undefined;
203
+ };
204
+ play: ({ canvasElement }: {
205
+ canvasElement: HTMLElement;
206
+ }) => Promise<void>;
207
+ };
208
+ export declare const AutoSizeEmptyDataFitsHeader: {
209
+ parameters: {
210
+ tags: string[];
211
+ };
212
+ render: () => HTMLDivElement & {
213
+ _table?: SimpleTableVanilla | undefined;
214
+ };
215
+ play: ({ canvasElement }: {
216
+ canvasElement: HTMLElement;
217
+ }) => Promise<void>;
218
+ };
219
+ export declare const AutoSizeMixedColumnsLayout: {
220
+ parameters: {
221
+ tags: string[];
222
+ };
223
+ render: () => HTMLDivElement & {
224
+ _table?: SimpleTableVanilla | undefined;
225
+ };
226
+ play: ({ canvasElement }: {
227
+ canvasElement: HTMLElement;
228
+ }) => Promise<void>;
229
+ };
230
+ export declare const AutoSizePinnedColumn: {
231
+ parameters: {
232
+ tags: string[];
233
+ };
234
+ render: () => HTMLDivElement & {
235
+ _table?: SimpleTableVanilla | undefined;
236
+ };
237
+ play: ({ canvasElement }: {
238
+ canvasElement: HTMLElement;
239
+ }) => Promise<void>;
240
+ };
241
+ export declare const AutoSizeNoFlicker: {
242
+ parameters: {
243
+ tags: string[];
244
+ };
245
+ render: () => HTMLDivElement & {
246
+ _table?: SimpleTableVanilla | undefined;
247
+ };
248
+ play: ({ canvasElement }: {
249
+ canvasElement: HTMLElement;
250
+ }) => Promise<void>;
251
+ };
252
+ export declare const SamplingAndOutlierHelpers: {
253
+ parameters: {
254
+ tags: string[];
255
+ };
256
+ render: () => HTMLDivElement;
257
+ play: () => Promise<void>;
258
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-table-core",
3
- "version": "3.7.6",
3
+ "version": "3.8.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/index.es.js",
6
6
  "types": "dist/src/index.d.ts",