simple-table-core 3.0.13 → 3.2.1

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 (49) hide show
  1. package/README.md +22 -0
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/cjs/src/core/SimpleTableVanilla.d.ts +25 -0
  4. package/dist/cjs/src/core/rendering/RenderOrchestrator.d.ts +4 -0
  5. package/dist/cjs/src/core/rendering/SectionRenderer.d.ts +35 -0
  6. package/dist/cjs/src/core/rendering/TableRenderer.d.ts +4 -0
  7. package/dist/cjs/src/index.d.ts +2 -1
  8. package/dist/cjs/src/managers/AnimationCoordinator.d.ts +142 -0
  9. package/dist/cjs/src/types/AnimationsConfig.d.ts +14 -0
  10. package/dist/cjs/src/types/SimpleTableConfig.d.ts +2 -0
  11. package/dist/cjs/src/types/SimpleTableProps.d.ts +2 -0
  12. package/dist/cjs/src/types/TableRow.d.ts +9 -0
  13. package/dist/cjs/src/utils/bodyCell/types.d.ts +6 -0
  14. package/dist/cjs/src/utils/bodyCellRenderer.d.ts +2 -1
  15. package/dist/cjs/src/utils/headerCell/styling.d.ts +2 -0
  16. package/dist/cjs/src/utils/rowUtils.d.ts +25 -0
  17. package/dist/cjs/stories/examples/BasicExample.d.ts +1 -0
  18. package/dist/cjs/stories/examples/sales-example/SalesExample.d.ts +3 -0
  19. package/dist/cjs/stories/tests/32-ThemesTests.stories.d.ts +19 -0
  20. package/dist/cjs/stories/tests/41-CellAnimationsTests.stories.d.ts +237 -0
  21. package/dist/cjs/stories/tests/42-CellAnimationsVirtualizationTests.stories.d.ts +251 -0
  22. package/dist/cjs/styles.css +1 -1
  23. package/dist/index.es.js +1 -1
  24. package/dist/src/core/SimpleTableVanilla.d.ts +25 -0
  25. package/dist/src/core/rendering/RenderOrchestrator.d.ts +4 -0
  26. package/dist/src/core/rendering/SectionRenderer.d.ts +35 -0
  27. package/dist/src/core/rendering/TableRenderer.d.ts +4 -0
  28. package/dist/src/index.d.ts +2 -1
  29. package/dist/src/managers/AnimationCoordinator.d.ts +142 -0
  30. package/dist/src/types/AnimationsConfig.d.ts +14 -0
  31. package/dist/src/types/SimpleTableConfig.d.ts +2 -0
  32. package/dist/src/types/SimpleTableProps.d.ts +2 -0
  33. package/dist/src/types/TableRow.d.ts +9 -0
  34. package/dist/src/utils/bodyCell/types.d.ts +6 -0
  35. package/dist/src/utils/bodyCellRenderer.d.ts +2 -1
  36. package/dist/src/utils/headerCell/styling.d.ts +2 -0
  37. package/dist/src/utils/rowUtils.d.ts +25 -0
  38. package/dist/stories/examples/BasicExample.d.ts +1 -0
  39. package/dist/stories/examples/sales-example/SalesExample.d.ts +3 -0
  40. package/dist/stories/tests/32-ThemesTests.stories.d.ts +19 -0
  41. package/dist/stories/tests/41-CellAnimationsTests.stories.d.ts +237 -0
  42. package/dist/stories/tests/42-CellAnimationsVirtualizationTests.stories.d.ts +251 -0
  43. package/dist/styles.css +1 -1
  44. package/package.json +27 -3
  45. package/src/styles/base.css +15 -0
  46. package/src/styles/themes/frost.css +3 -2
  47. package/src/styles/themes/modern-dark.css +3 -2
  48. package/src/styles/themes/modern-light.css +4 -3
  49. package/src/styles/themes/theme-custom.css +4 -3
@@ -0,0 +1,237 @@
1
+ /**
2
+ * CELL ANIMATIONS TESTS
3
+ *
4
+ * Covers FLIP-style animations on:
5
+ * - Programmatic column reorder via the API (cells shift `left`).
6
+ * - Sort change (rows shift `top`) when `getRowId` is provided so cell
7
+ * identity is row-stable.
8
+ *
9
+ * Cells that newly enter the visible band slide in from their actual pre-
10
+ * change off-screen position; cells that leave the visible band slide out
11
+ * to their actual post-change off-screen position. The body container's
12
+ * overflow clip turns those long off-screen translates into "appears to
13
+ * slide in from the viewport edge" visually.
14
+ *
15
+ * Animations default to `true`. Live drag reorder is intentionally not
16
+ * animated (we don't want to fight the user's pointer mid-drag).
17
+ */
18
+ import { SimpleTableVanilla } from "../../src/index";
19
+ import type { Meta } from "@storybook/html";
20
+ declare const meta: Meta;
21
+ export default meta;
22
+ export declare const ProgrammaticReorderAnimation: {
23
+ render: () => HTMLDivElement & {
24
+ _table?: SimpleTableVanilla | undefined;
25
+ };
26
+ play: ({ canvasElement }: {
27
+ canvasElement: HTMLElement;
28
+ }) => Promise<void>;
29
+ };
30
+ /**
31
+ * Simplest possible reorder animation test: a 3 columns × 3 rows table.
32
+ *
33
+ * Step 1 is the straightforward case — move the center column (B) to the
34
+ * rightmost position by swapping B and C via
35
+ * `table.update({ defaultHeaders: [...] })`. After that animation settles,
36
+ * the play function chains four more reorders (5 total) so we exercise:
37
+ *
38
+ * - Pairwise neighbour swaps (B↔C, A↔B).
39
+ * - Long-distance swaps (first ↔ last column).
40
+ * - A "rotate back to original" reset.
41
+ *
42
+ * For every step we synchronously read the FLIP "First" frame and assert
43
+ * that each cell's inverse `transform-X` exactly matches `oldLeft − newLeft`
44
+ * (≤ 1.5px tolerance), then wait past `SLOW_DURATION` and assert no leftover
45
+ * transforms and no retained ghosts before moving on to the next step.
46
+ *
47
+ * Expected FLIP behaviour for each reorder:
48
+ * - Cells whose `left` increases slide RIGHT, so their inverse transform-X
49
+ * is NEGATIVE (`oldLeft − newLeft < 0`).
50
+ * - Cells whose `left` decreases slide LEFT, so their inverse transform-X
51
+ * is POSITIVE (`oldLeft − newLeft > 0`).
52
+ * - Cells whose column index is unchanged have no transform.
53
+ */
54
+ export declare const SimpleThreeByThreeCenterToRightSwap: {
55
+ render: () => HTMLDivElement & {
56
+ _table?: SimpleTableVanilla | undefined;
57
+ };
58
+ play: ({ canvasElement }: {
59
+ canvasElement: HTMLElement;
60
+ }) => Promise<void>;
61
+ };
62
+ /**
63
+ * Regression test: HEADER cells must FLIP-animate during a column reorder
64
+ * the same way body cells do. Header cells live in their own per-section
65
+ * tracking registry (separate WeakMap from body cells) and used to be
66
+ * invisible to the AnimationCoordinator, so on a reorder they would teleport
67
+ * while the body cells underneath them slid into place.
68
+ *
69
+ * For each of five chained programmatic reorders the test:
70
+ * 1. Snapshots every header cell's `style.left` BEFORE the update.
71
+ * 2. Calls `table.update({ defaultHeaders })` then synchronously reads
72
+ * the FLIP "First" frame transform on every header.
73
+ * 3. Asserts the inverse `transform-X` equals `oldLeft − newLeft` (±1.5px),
74
+ * that headers whose index didn't change have no transform, and that
75
+ * swapped headers carry opposite-sign transforms (one slid left, one
76
+ * slid right).
77
+ * 4. After SETTLE_PAUSE asserts no leftover transforms or transitions
78
+ * remain on any header cell.
79
+ */
80
+ export declare const HeaderCellsAnimateOnColumnReorder: {
81
+ render: () => HTMLDivElement & {
82
+ _table?: SimpleTableVanilla | undefined;
83
+ };
84
+ play: ({ canvasElement }: {
85
+ canvasElement: HTMLElement;
86
+ }) => Promise<void>;
87
+ };
88
+ /**
89
+ * Drag-and-drop variant of {@link HeaderCellsAnimateOnColumnReorder}. The
90
+ * regression we're guarding against is a setup where body cells animate on
91
+ * every dragover but header cells teleport because the AnimationCoordinator
92
+ * has never been told about the header containers.
93
+ */
94
+ export declare const HeaderCellsAnimateDuringDragReorder: {
95
+ render: () => HTMLDivElement & {
96
+ _table?: SimpleTableVanilla | undefined;
97
+ };
98
+ play: ({ canvasElement }: {
99
+ canvasElement: HTMLElement;
100
+ }) => Promise<void>;
101
+ };
102
+ /**
103
+ * Regression test: while a drag-reorder swap is animating, dragover events
104
+ * targeting the still-animating cells must NOT keep firing additional
105
+ * swaps. Previously, the moving header would slide back under the user's
106
+ * cursor, the browser would re-fire dragover on it, and the column order
107
+ * would oscillate visibly (a fast back-and-forth flicker).
108
+ *
109
+ * The fix: in-flight cells get `pointer-events: none` so the browser's
110
+ * hit-testing skips them. dispatchEvent bypasses hit-testing, so this test
111
+ * resolves the cursor's target via `document.elementFromPoint` (which DOES
112
+ * honor pointer-events: none) — i.e. simulates what the browser would do.
113
+ *
114
+ * Asserts:
115
+ * 1. Headers that move have `pointer-events: none` while in flight.
116
+ * 2. The unchanged header keeps default pointer-events.
117
+ * 3. After many dragover events sustained at the same screen point during
118
+ * the animation, the column order does NOT oscillate (at most one swap
119
+ * from the starting state, never the original-→-swapped-→-original
120
+ * ping-pong the regression produced).
121
+ * 4. After the animation settles, pointer-events on the previously-moving
122
+ * headers is restored to default.
123
+ */
124
+ export declare const HeaderDragDoesNotFlickerDuringAnimation: {
125
+ render: () => HTMLDivElement & {
126
+ _table?: SimpleTableVanilla | undefined;
127
+ };
128
+ play: ({ canvasElement }: {
129
+ canvasElement: HTMLElement;
130
+ }) => Promise<void>;
131
+ };
132
+ export declare const ReorderWithoutAnimations: {
133
+ render: () => HTMLDivElement & {
134
+ _table?: SimpleTableVanilla | undefined;
135
+ };
136
+ play: ({ canvasElement }: {
137
+ canvasElement: HTMLElement;
138
+ }) => Promise<void>;
139
+ };
140
+ export declare const SortAnimationDemo: {
141
+ render: () => HTMLDivElement & {
142
+ _table?: SimpleTableVanilla | undefined;
143
+ };
144
+ play: ({ canvasElement }: {
145
+ canvasElement: HTMLElement;
146
+ }) => Promise<void>;
147
+ };
148
+ export declare const AnimationsPropWiring: {
149
+ render: () => HTMLDivElement & {
150
+ _table?: SimpleTableVanilla | undefined;
151
+ };
152
+ play: ({ canvasElement }: {
153
+ canvasElement: HTMLElement;
154
+ }) => Promise<void>;
155
+ };
156
+ /**
157
+ * Cells should animate from THEIR PREVIOUS position, not all from the same
158
+ * direction. This story snapshots every cell's pre-reorder geometry and then
159
+ * verifies, mid-flight, that:
160
+ *
161
+ * 1. Cells that move RIGHT (new left > old left) start with a NEGATIVE
162
+ * transform-X (so they appear at their previous-left and slide rightward).
163
+ * 2. Cells that move LEFT (new left < old left) start with a POSITIVE
164
+ * transform-X (so they appear at their previous-right and slide leftward).
165
+ * 3. Both directions appear in the same animation tick — no "everyone slides
166
+ * from the same edge" regression.
167
+ * 4. The translate-X delta for each cell exactly equals (oldLeft - newLeft),
168
+ * so the FLIP "First" frame really lands at the previous on-screen pixel
169
+ * position rather than at some shared anchor.
170
+ */
171
+ export declare const ReorderAnimatesFromPreviousPositionPerCell: {
172
+ render: () => HTMLDivElement & {
173
+ _table?: SimpleTableVanilla | undefined;
174
+ };
175
+ play: ({ canvasElement }: {
176
+ canvasElement: HTMLElement;
177
+ }) => Promise<void>;
178
+ };
179
+ /**
180
+ * Off-screen rows still slide. When the viewport only renders a slice of
181
+ * all rows, sorting causes some cells to enter the visible band (rows that
182
+ * were below the fold pre-sort) and others to leave it (rows that were on
183
+ * screen pre-sort). The coordinator captures positions for ALL rows in the
184
+ * dataset, so:
185
+ *
186
+ * - Incoming cells get created at their new (visible) position and FLIP
187
+ * in from their actual pre-change off-screen `top`. Visually the cell
188
+ * appears to slide in from the viewport edge.
189
+ * - Outgoing cells get retained at their pre-change visible position and
190
+ * slide to their actual post-change off-screen `top`, then are removed.
191
+ * Visually the cell appears to slide out past the viewport edge.
192
+ *
193
+ * Both kinds of slides use `transform: translate3d` (no opacity).
194
+ */
195
+ export declare const SortSlidesRowsCrossingTheViewportBoundary: {
196
+ render: () => HTMLDivElement & {
197
+ _table?: SimpleTableVanilla | undefined;
198
+ };
199
+ play: ({ canvasElement }: {
200
+ canvasElement: HTMLElement;
201
+ }) => Promise<void>;
202
+ };
203
+ /**
204
+ * Drag-and-drop column reorder must FLIP-animate the displaced body cells
205
+ * (and header cells) on EVERY `dragover` swap — not just on the final
206
+ * `dragend`. Visually: while the user drags column B sideways, column C
207
+ * should glide left to make room as soon as B's center crosses C's center,
208
+ * the same way a programmatic `table.update({ defaultHeaders })` swap
209
+ * animates.
210
+ *
211
+ * How the host pulls this off:
212
+ * - The drag handler (`headerCell/dragging.ts`) calls
213
+ * `context.onTableHeaderDragEnd(newHeaders)` from inside `dragover` once
214
+ * the cursor has moved enough to trigger a swap. That callback resolves
215
+ * to `setHeaders(newHeaders)` + `onRender()`.
216
+ * - `setHeaders` first calls `captureAnimationSnapshot()` (no live-drag
217
+ * skip), then mutates the headers. The next render commits cells to
218
+ * their new absolute positions.
219
+ * - `render()`'s final `play()` consumes the snapshot, computes the
220
+ * pre→post deltas, and FLIPs every cell that moved — including the one
221
+ * in the column the user is dragging.
222
+ *
223
+ * This test renders a 3 cols × 3 rows table and dispatches the drag
224
+ * sequence inline so it can poll the cells between `dragover` events and
225
+ * assert that a FLIP transform or `transition: transform` was observed
226
+ * BEFORE `dragend` ever fires. Asserting only "saw FLIP within N ms
227
+ * after dragend" wouldn't distinguish the desired behaviour from a
228
+ * single settle animation that runs only on drop.
229
+ */
230
+ export declare const DragAndDropColumnReorderShouldAnimate: {
231
+ render: () => HTMLDivElement & {
232
+ _table?: SimpleTableVanilla | undefined;
233
+ };
234
+ play: ({ canvasElement }: {
235
+ canvasElement: HTMLElement;
236
+ }) => Promise<void>;
237
+ };
@@ -0,0 +1,251 @@
1
+ /**
2
+ * CELL ANIMATIONS — VIRTUALIZATION & SCALE TESTS (slow & visible)
3
+ *
4
+ * Stress-tests the FLIP animation coordinator at scale (500 rows × 30 cols
5
+ * in a constrained viewport) with `animations.duration` cranked up so the
6
+ * play function is *visible* when watched in Storybook. Each play function
7
+ * runs many sequential interactions with explicit pauses between them so
8
+ * you can see each animation phase fire.
9
+ *
10
+ * Slides apply both within and across the visible band:
11
+ * - Persistent cells (visible before AND after) slide from old → new.
12
+ * - Incoming cells (off-screen before, in DOM after) FLIP in from their
13
+ * true pre-change off-screen position, clipped by the body's overflow.
14
+ * - Outgoing cells (in DOM before, off-screen after) are retained as
15
+ * `data-animating-out` ghosts and slide to their true post-change
16
+ * off-screen position before being removed — visually they appear to
17
+ * slide out past the viewport edge.
18
+ *
19
+ * Animate-out into virtualized space is regression-tested in four flavours:
20
+ * 1. Vertical / downward (sort col_0 desc at scrollTop=0): top-band rows
21
+ * sort to the bottom of the table → ghosts slide DOWN past bottom edge.
22
+ * → {@link SortRetainsCellsThatExitVirtualizedBand}
23
+ * 2. Vertical / upward (sort col_0 desc while scrolled to bottom): visible
24
+ * bottom-band rows sort to the top of the table → ghosts slide UP past
25
+ * top edge.
26
+ * → {@link SortRetainsCellsThatExitUpwardWhenScrolled}
27
+ * 3. Vertical / re-aim mid-flight (rapid double sort): cells already
28
+ * animating out from a first sort must be re-aimed by a second sort
29
+ * that fires before the first finishes, with all ghosts torn down once
30
+ * everything settles.
31
+ * → {@link OverlappingSortsRetainAndReaimGhosts}
32
+ * 4. Horizontal / leftward (column reverse at right-most scrollLeft):
33
+ * visible right-side cells reorder to the left side of the table → if
34
+ * the new `left` is outside `getVisibleBodyCells`'s post-reorder band,
35
+ * the cells are retained as ghosts that slide LEFT past the viewport
36
+ * edge.
37
+ * → {@link ReorderAfterHorizontalScrollRetainsExitingCellsAsGhosts}
38
+ *
39
+ * Why horizontal animate-out only manifests under horizontal scroll: at
40
+ * scrollLeft=0, `getVisibleBodyCells` keeps every non-pinned cell in the
41
+ * DOM (the visible band's right edge equals scrollLeft + mainWidth, which
42
+ * is the full content width), so column reorder never *removes* a cell —
43
+ * the same DOM node persists and slides to its new `left` via FLIP, with
44
+ * the body's overflow clipping the off-screen portion. Once scrollLeft > 0
45
+ * the band shifts and reorders can push cells outside it; that's when the
46
+ * outgoing ghost path kicks in horizontally as well.
47
+ */
48
+ import type { Meta } from "@storybook/html";
49
+ declare const meta: Meta;
50
+ export default meta;
51
+ /**
52
+ * Large-scale reorder round-trip: reverse → reset on a 30-col × 500-row
53
+ * table, asserting that >50 cells are mid-flight after each step and that
54
+ * everything settles cleanly with no leftover ghosts.
55
+ *
56
+ * Strict per-cell FLIP transform-X correctness on a contained neighbour
57
+ * swap is covered by `ContainedNeighborSwapAnimation` (immediately below)
58
+ * on the same constrained table, so we don't repeat it here.
59
+ */
60
+ export declare const SlowColumnReorderMarathon: {
61
+ render: () => HTMLElement;
62
+ play: ({ canvasElement }: {
63
+ canvasElement: HTMLElement;
64
+ }) => Promise<void>;
65
+ };
66
+ /**
67
+ * Visually obvious "two cells trading places" demo. Uses neighbour columns
68
+ * that are both fully inside the viewport (so the FLIP motion is contained
69
+ * on-screen, no off-screen sweeps). The play function:
70
+ *
71
+ * 1. Captures both cells' pre-swap left positions.
72
+ * 2. Calls `table.update({ defaultHeaders: swapped })`.
73
+ * 3. Synchronously asserts each cell's FLIP transform-X equals
74
+ * (oldLeft - newLeft) — i.e. cell A starts where B was, cell B starts
75
+ * where A was, and they slide toward each other in opposite directions.
76
+ *
77
+ * If a future change causes cells to animate from a single shared anchor
78
+ * (e.g. the right edge), this story fails immediately.
79
+ */
80
+ export declare const ContainedNeighborSwapAnimation: {
81
+ render: () => HTMLElement;
82
+ play: ({ canvasElement }: {
83
+ canvasElement: HTMLElement;
84
+ }) => Promise<void>;
85
+ };
86
+ /**
87
+ * Vertical scroll → reorder → vertical scroll → reorder, demonstrating that
88
+ * the snapshot reflects the *currently visible* row band each time.
89
+ */
90
+ export declare const ReorderAtMultipleScrollPositions: {
91
+ render: () => HTMLElement;
92
+ play: ({ canvasElement }: {
93
+ canvasElement: HTMLElement;
94
+ }) => Promise<void>;
95
+ };
96
+ /**
97
+ * Per-cell FLIP correctness check at scale.
98
+ *
99
+ * When reversing 30 columns:
100
+ * - Cells whose pre-reverse position is currently on-screen (or whose true
101
+ * journey fits within ~one viewport) must FLIP exactly to that position
102
+ * (`txX === oldLeft - newLeft` to within sub-pixel rounding).
103
+ * - Cells whose pre-reverse position is far off-screen are scaled by
104
+ * `AnimationCoordinator.scaleFlipDistance` so the visible slide stays
105
+ * bounded. For those, we relax the strict equality to: same sign as the
106
+ * true journey, magnitude < the true journey, and magnitude inside the
107
+ * `[viewport, ~2 × viewport]` band the scaler produces.
108
+ *
109
+ * Catches regressions where the snapshot is captured against the post-
110
+ * mutation layout, where preLayouts overwrites live DOM positions, where
111
+ * some cells get skipped from the FLIP pass, or where horizontal scaling
112
+ * collapses the journey too aggressively (e.g. dropping it to 0).
113
+ */
114
+ export declare const ReorderAtScaleAnimatesFromPreviousPositionPerCell: {
115
+ render: () => HTMLElement;
116
+ play: ({ canvasElement }: {
117
+ canvasElement: HTMLElement;
118
+ }) => Promise<void>;
119
+ };
120
+ /**
121
+ * Sort cleanup at scale. Verifies that after a sort animation settles on a
122
+ * 500-row × 30-col table, every retained ghost is torn down and no cell is
123
+ * left with a stuck transform or transition.
124
+ *
125
+ * Per-sort FLIP correctness, ghost retention, mid-flight re-aim, and the
126
+ * upward / horizontal exit cases each have their own focused regression
127
+ * tests below — this one only proves cleanup holds at scale across a
128
+ * round-trip (sort → clear).
129
+ */
130
+ export declare const SortMarathon: {
131
+ render: () => HTMLElement;
132
+ play: ({ canvasElement }: {
133
+ canvasElement: HTMLElement;
134
+ }) => Promise<void>;
135
+ };
136
+ /**
137
+ * REGRESSION TEST FOR ANIMATE-OUT INTO VIRTUALIZED SPACE.
138
+ *
139
+ * When a sort moves a *currently visible* row to a position that's outside
140
+ * the visible band (e.g. row at position 0 sorts to position 499 in a 500
141
+ * row table), the cell for that row should NOT be removed from the DOM
142
+ * immediately. Instead the renderer should hand it to the animation
143
+ * coordinator as a retained "ghost" that:
144
+ *
145
+ * 1. Stays in the DOM with `data-animating-out="true"`.
146
+ * 2. Has its `style.top` updated to its new (off-screen) position.
147
+ * 3. Plays a FLIP slide from its old (on-screen) position to that new
148
+ * off-screen position. The body's `overflow: hidden` clips the part
149
+ * that crosses the viewport edge, so it visually appears to slide
150
+ * out past the bottom edge.
151
+ * 4. Is removed from the DOM only after the slide completes.
152
+ *
153
+ * If the cell is dropped immediately, the user sees it pop out of existence
154
+ * in place — a visible jank during sort.
155
+ *
156
+ * The test grabs any cell in the top-most visible row, records its on-screen
157
+ * rect, triggers a sort that pushes it far off-screen, and then samples
158
+ * shortly after — well before the long animation could finish. With a
159
+ * working slide-out, the cell is still in the DOM and visually only a small
160
+ * fraction of the way to its new position. With a broken slide-out, the
161
+ * cell is either missing from the DOM (removed instantly) or already at its
162
+ * far-off-screen target (snapped). After the animation settles we also
163
+ * confirm the ghost was cleaned up.
164
+ */
165
+ export declare const SortRetainsCellsThatExitVirtualizedBand: {
166
+ tags: string[];
167
+ render: () => HTMLElement;
168
+ play: ({ canvasElement }: {
169
+ canvasElement: HTMLElement;
170
+ }) => Promise<void>;
171
+ };
172
+ /**
173
+ * REGRESSION TEST FOR ANIMATE-OUT WHEN SCROLLED — UP DIRECTION.
174
+ *
175
+ * Companion to {@link SortRetainsCellsThatExitVirtualizedBand} that exercises
176
+ * cells exiting via the *top* of the viewport rather than the bottom.
177
+ *
178
+ * If you scroll mid-way through the table and then sort, rows that are
179
+ * currently visible can be sorted to a position ABOVE the visible band (i.e.
180
+ * a small `top` value while the scroller is at a large `scrollTop`). The
181
+ * cells representing those rows must still be retained as ghosts and slide
182
+ * UPWARD past the top edge — not pop out in place.
183
+ *
184
+ * Concretely: scroll to the bottom of the table (rows ~485–499 visible),
185
+ * then sort `col_0 desc` (descending by col_0 value, where row-0 has the
186
+ * lowest value). The currently-visible rows have HIGH col_0 values, so on
187
+ * desc they sort to the TOP of the table (small `top`). Their old visual
188
+ * position was below the viewport edge of the scroller; their new visual
189
+ * position is above it. They must be retained and slide upward, with an
190
+ * inverse FLIP `transform` whose Y component is positive (= old.top -
191
+ * new.top, where old > new ⇒ tx_y > 0).
192
+ */
193
+ export declare const SortRetainsCellsThatExitUpwardWhenScrolled: {
194
+ tags: string[];
195
+ render: () => HTMLElement;
196
+ play: ({ canvasElement }: {
197
+ canvasElement: HTMLElement;
198
+ }) => Promise<void>;
199
+ };
200
+ /**
201
+ * REGRESSION TEST FOR ANIMATE-OUT WHEN SORTS OVERLAP.
202
+ *
203
+ * If the user clicks a sort header twice in rapid succession (toggling the
204
+ * direction before the previous animation finishes), the second sort's
205
+ * snapshot fires while the first sort's retained ghosts are still mid-flight.
206
+ * Those mid-flight ghosts are themselves "currently visible" cells (as far
207
+ * as the user is concerned) and should also be retained / re-aimed for the
208
+ * second sort — not orphaned in place at the wrong position.
209
+ *
210
+ * The test:
211
+ * 1. Sorts col_0 desc — capturing the wave of bottom-bound ghosts.
212
+ * 2. Half-way through the slide, sorts col_0 asc — which should send
213
+ * everything back the other way.
214
+ * 3. Verifies the active cells (now sorting back ascending) still get a
215
+ * FLIP transform (i.e. they slide rather than snap), and that all
216
+ * ghosts are torn down once both animations settle.
217
+ */
218
+ export declare const OverlappingSortsRetainAndReaimGhosts: {
219
+ tags: string[];
220
+ render: () => HTMLElement;
221
+ play: ({ canvasElement }: {
222
+ canvasElement: HTMLElement;
223
+ }) => Promise<void>;
224
+ };
225
+ /**
226
+ * REGRESSION TEST FOR HORIZONTAL ANIMATE-OUT WHEN HORIZONTALLY SCROLLED.
227
+ *
228
+ * When the user has scrolled the body horizontally so that left-side
229
+ * columns are outside the rendered cell band, then performs a column
230
+ * reorder that pushes the *currently visible* right-side columns to the
231
+ * left side, those right-side cells exit the band horizontally and need
232
+ * to be retained as ghosts that slide LEFT past the viewport edge — not
233
+ * popped out of existence in place.
234
+ *
235
+ * Concretely:
236
+ * 1. Scroll the body container all the way to the right (rightmost
237
+ * columns visible, leftmost columns out of band).
238
+ * 2. Reverse all 31 columns. The cells that were on-screen on the right
239
+ * now belong on the left side of the table — outside the band that
240
+ * `getVisibleBodyCells` keeps in the DOM at the new scroll position.
241
+ * 3. Verify those cells are retained as `data-animating-out` ghosts at
242
+ * their new (off-screen-left) `style.left`, with an inverse FLIP X
243
+ * transform so they visually start at their old on-screen `left`.
244
+ */
245
+ export declare const ReorderAfterHorizontalScrollRetainsExitingCellsAsGhosts: {
246
+ tags: string[];
247
+ render: () => HTMLElement;
248
+ play: ({ canvasElement }: {
249
+ canvasElement: HTMLElement;
250
+ }) => Promise<void>;
251
+ };