/** * CELL ANIMATIONS — VIRTUALIZATION & SCALE TESTS (slow & visible) * * Stress-tests the FLIP animation coordinator at scale (500 rows × 30 cols * in a constrained viewport) with `animations.duration` cranked up so the * play function is *visible* when watched in Storybook. Each play function * runs many sequential interactions with explicit pauses between them so * you can see each animation phase fire. * * Slides apply both within and across the visible band: * - Persistent cells (visible before AND after) slide from old → new. * - Incoming cells (off-screen before, in DOM after) FLIP in from their * true pre-change off-screen position, clipped by the body's overflow. * - Outgoing cells (in DOM before, off-screen after) are retained as * `data-animating-out` ghosts and slide to their true post-change * off-screen position before being removed — visually they appear to * slide out past the viewport edge. * * Animate-out into virtualized space is regression-tested in four flavours: * 1. Vertical / downward (sort col_0 desc at scrollTop=0): top-band rows * sort to the bottom of the table → ghosts slide DOWN past bottom edge. * → {@link SortRetainsCellsThatExitVirtualizedBand} * 2. Vertical / upward (sort col_0 desc while scrolled to bottom): visible * bottom-band rows sort to the top of the table → ghosts slide UP past * top edge. * → {@link SortRetainsCellsThatExitUpwardWhenScrolled} * 3. Vertical / re-aim mid-flight (rapid double sort): cells already * animating out from a first sort must be re-aimed by a second sort * that fires before the first finishes, with all ghosts torn down once * everything settles. * → {@link OverlappingSortsRetainAndReaimGhosts} * 4. Horizontal / leftward (column reverse at right-most scrollLeft): * visible right-side cells reorder to the left side of the table → if * the new `left` is outside `getVisibleBodyCells`'s post-reorder band, * the cells are retained as ghosts that slide LEFT past the viewport * edge. * → {@link ReorderAfterHorizontalScrollRetainsExitingCellsAsGhosts} * * Why horizontal animate-out only manifests under horizontal scroll: at * scrollLeft=0, `getVisibleBodyCells` keeps every non-pinned cell in the * DOM (the visible band's right edge equals scrollLeft + mainWidth, which * is the full content width), so column reorder never *removes* a cell — * the same DOM node persists and slides to its new `left` via FLIP, with * the body's overflow clipping the off-screen portion. Once scrollLeft > 0 * the band shifts and reorders can push cells outside it; that's when the * outgoing ghost path kicks in horizontally as well. */ import type { Meta } from "@storybook/html"; declare const meta: Meta; export default meta; /** * Large-scale reorder round-trip: reverse → reset on a 30-col × 500-row * table, asserting that >50 cells are mid-flight after each step and that * everything settles cleanly with no leftover ghosts. * * Strict per-cell FLIP transform-X correctness on a contained neighbour * swap is covered by `ContainedNeighborSwapAnimation` (immediately below) * on the same constrained table, so we don't repeat it here. */ export declare const SlowColumnReorderMarathon: { render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * Visually obvious "two cells trading places" demo. Uses neighbour columns * that are both fully inside the viewport (so the FLIP motion is contained * on-screen, no off-screen sweeps). The play function: * * 1. Captures both cells' pre-swap left positions. * 2. Calls `table.update({ defaultHeaders: swapped })`. * 3. Synchronously asserts each cell's FLIP transform-X equals * (oldLeft - newLeft) — i.e. cell A starts where B was, cell B starts * where A was, and they slide toward each other in opposite directions. * * If a future change causes cells to animate from a single shared anchor * (e.g. the right edge), this story fails immediately. */ export declare const ContainedNeighborSwapAnimation: { render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * Vertical scroll → reorder → vertical scroll → reorder, demonstrating that * the snapshot reflects the *currently visible* row band each time. */ export declare const ReorderAtMultipleScrollPositions: { render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * Per-cell FLIP correctness check at scale. * * When reversing 30 columns: * - Cells whose pre-reverse position is currently on-screen (or whose true * journey fits within ~one viewport) must FLIP exactly to that position * (`txX === oldLeft - newLeft` to within sub-pixel rounding). * - Cells whose pre-reverse position is far off-screen are scaled by * `AnimationCoordinator.scaleFlipDistance` so the visible slide stays * bounded. For those, we relax the strict equality to: same sign as the * true journey, magnitude < the true journey, and magnitude inside the * `[viewport, ~2 × viewport]` band the scaler produces. * * Catches regressions where the snapshot is captured against the post- * mutation layout, where preLayouts overwrites live DOM positions, where * some cells get skipped from the FLIP pass, or where horizontal scaling * collapses the journey too aggressively (e.g. dropping it to 0). */ export declare const ReorderAtScaleAnimatesFromPreviousPositionPerCell: { render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * Sort cleanup at scale. Verifies that after a sort animation settles on a * 500-row × 30-col table, every retained ghost is torn down and no cell is * left with a stuck transform or transition. * * Per-sort FLIP correctness, ghost retention, mid-flight re-aim, and the * upward / horizontal exit cases each have their own focused regression * tests below — this one only proves cleanup holds at scale across a * round-trip (sort → clear). */ export declare const SortMarathon: { render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * REGRESSION TEST FOR ANIMATE-OUT INTO VIRTUALIZED SPACE. * * When a sort moves a *currently visible* row to a position that's outside * the visible band (e.g. row at position 0 sorts to position 499 in a 500 * row table), the cell for that row should NOT be removed from the DOM * immediately. Instead the renderer should hand it to the animation * coordinator as a retained "ghost" that: * * 1. Stays in the DOM with `data-animating-out="true"`. * 2. Has its `style.top` updated to its new (off-screen) position. * 3. Plays a FLIP slide from its old (on-screen) position to that new * off-screen position. The body's `overflow: hidden` clips the part * that crosses the viewport edge, so it visually appears to slide * out past the bottom edge. * 4. Is removed from the DOM only after the slide completes. * * If the cell is dropped immediately, the user sees it pop out of existence * in place — a visible jank during sort. * * The test grabs any cell in the top-most visible row, records its on-screen * rect, triggers a sort that pushes it far off-screen, and then samples * shortly after — well before the long animation could finish. With a * working slide-out, the cell is still in the DOM and visually only a small * fraction of the way to its new position. With a broken slide-out, the * cell is either missing from the DOM (removed instantly) or already at its * far-off-screen target (snapped). After the animation settles we also * confirm the ghost was cleaned up. */ export declare const SortRetainsCellsThatExitVirtualizedBand: { tags: string[]; render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * REGRESSION TEST FOR ANIMATE-OUT WHEN SCROLLED — UP DIRECTION. * * Companion to {@link SortRetainsCellsThatExitVirtualizedBand} that exercises * cells exiting via the *top* of the viewport rather than the bottom. * * If you scroll mid-way through the table and then sort, rows that are * currently visible can be sorted to a position ABOVE the visible band (i.e. * a small `top` value while the scroller is at a large `scrollTop`). The * cells representing those rows must still be retained as ghosts and slide * UPWARD past the top edge — not pop out in place. * * Concretely: scroll to the bottom of the table (rows ~485–499 visible), * then sort `col_0 desc` (descending by col_0 value, where row-0 has the * lowest value). The currently-visible rows have HIGH col_0 values, so on * desc they sort to the TOP of the table (small `top`). Their old visual * position was below the viewport edge of the scroller; their new visual * position is above it. They must be retained and slide upward, with an * inverse FLIP `transform` whose Y component is positive (= old.top - * new.top, where old > new ⇒ tx_y > 0). */ export declare const SortRetainsCellsThatExitUpwardWhenScrolled: { tags: string[]; render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * REGRESSION TEST FOR ANIMATE-OUT WHEN SORTS OVERLAP. * * If the user clicks a sort header twice in rapid succession (toggling the * direction before the previous animation finishes), the second sort's * snapshot fires while the first sort's retained ghosts are still mid-flight. * Those mid-flight ghosts are themselves "currently visible" cells (as far * as the user is concerned) and should also be retained / re-aimed for the * second sort — not orphaned in place at the wrong position. * * The test: * 1. Sorts col_0 desc — capturing the wave of bottom-bound ghosts. * 2. Half-way through the slide, sorts col_0 asc — which should send * everything back the other way. * 3. Verifies the active cells (now sorting back ascending) still get a * FLIP transform (i.e. they slide rather than snap), and that all * ghosts are torn down once both animations settle. */ export declare const OverlappingSortsRetainAndReaimGhosts: { tags: string[]; render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; }; /** * REGRESSION TEST FOR HORIZONTAL ANIMATE-OUT WHEN HORIZONTALLY SCROLLED. * * When the user has scrolled the body horizontally so that left-side * columns are outside the rendered cell band, then performs a column * reorder that pushes the *currently visible* right-side columns to the * left side, those right-side cells exit the band horizontally and need * to be retained as ghosts that slide LEFT past the viewport edge — not * popped out of existence in place. * * Concretely: * 1. Scroll the body container all the way to the right (rightmost * columns visible, leftmost columns out of band). * 2. Reverse all 31 columns. The cells that were on-screen on the right * now belong on the left side of the table — outside the band that * `getVisibleBodyCells` keeps in the DOM at the new scroll position. * 3. Verify those cells are retained as `data-animating-out` ghosts at * their new (off-screen-left) `style.left`, with an inverse FLIP X * transform so they visually start at their old on-screen `left`. */ export declare const ReorderAfterHorizontalScrollRetainsExitingCellsAsGhosts: { tags: string[]; render: () => HTMLElement; play: ({ canvasElement }: { canvasElement: HTMLElement; }) => Promise; };