@wallarm-org/design-system 1.31.0 → 1.32.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.
Files changed (64) hide show
  1. package/dist/components/Stepper/Stepper.d.ts +22 -0
  2. package/dist/components/Stepper/Stepper.figma.d.ts +1 -0
  3. package/dist/components/Stepper/Stepper.figma.js +59 -0
  4. package/dist/components/Stepper/Stepper.js +34 -0
  5. package/dist/components/Stepper/StepperContent.d.ts +16 -0
  6. package/dist/components/Stepper/StepperContent.js +28 -0
  7. package/dist/components/Stepper/StepperContext.d.ts +58 -0
  8. package/dist/components/Stepper/StepperContext.js +37 -0
  9. package/dist/components/Stepper/StepperDescription.d.ts +8 -0
  10. package/dist/components/Stepper/StepperDescription.js +30 -0
  11. package/dist/components/Stepper/StepperIndicator.d.ts +9 -0
  12. package/dist/components/Stepper/StepperIndicator.js +34 -0
  13. package/dist/components/Stepper/StepperItem.d.ts +19 -0
  14. package/dist/components/Stepper/StepperItem.js +48 -0
  15. package/dist/components/Stepper/StepperList.d.ts +12 -0
  16. package/dist/components/Stepper/StepperList.js +28 -0
  17. package/dist/components/Stepper/StepperNextTrigger.d.ts +14 -0
  18. package/dist/components/Stepper/StepperNextTrigger.js +28 -0
  19. package/dist/components/Stepper/StepperPrevTrigger.d.ts +13 -0
  20. package/dist/components/Stepper/StepperPrevTrigger.js +31 -0
  21. package/dist/components/Stepper/StepperRootProvider.d.ts +18 -0
  22. package/dist/components/Stepper/StepperRootProvider.js +25 -0
  23. package/dist/components/Stepper/StepperSeparator.d.ts +7 -0
  24. package/dist/components/Stepper/StepperSeparator.js +28 -0
  25. package/dist/components/Stepper/StepperTitle.d.ts +8 -0
  26. package/dist/components/Stepper/StepperTitle.js +32 -0
  27. package/dist/components/Stepper/StepperTrigger.d.ts +16 -0
  28. package/dist/components/Stepper/StepperTrigger.js +32 -0
  29. package/dist/components/Stepper/classes.d.ts +14 -0
  30. package/dist/components/Stepper/classes.js +37 -0
  31. package/dist/components/Stepper/constants.d.ts +22 -0
  32. package/dist/components/Stepper/constants.js +23 -0
  33. package/dist/components/Stepper/index.d.ts +15 -0
  34. package/dist/components/Stepper/index.js +14 -0
  35. package/dist/components/Stepper/takeAsChildDisabled.d.ts +11 -0
  36. package/dist/components/Stepper/takeAsChildDisabled.js +17 -0
  37. package/dist/components/Stepper/types.d.ts +11 -0
  38. package/dist/components/Stepper/types.js +0 -0
  39. package/dist/components/Stepper/useStepper.d.ts +38 -0
  40. package/dist/components/Stepper/useStepper.js +58 -0
  41. package/dist/components/Table/TableBody/TableBodyVirtualizedCore.js +1 -1
  42. package/dist/components/Table/TableBody/TableBodyVirtualizedWindow.js +30 -8
  43. package/dist/components/Table/TableInner/TableInnerWindow.js +7 -2
  44. package/dist/components/Table/hooks/index.d.ts +1 -0
  45. package/dist/components/Table/hooks/index.js +1 -0
  46. package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.d.ts +4 -2
  47. package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.js +6 -2
  48. package/dist/components/Table/hooks/infiniteScroll/useInitialAnchor.d.ts +7 -1
  49. package/dist/components/Table/hooks/infiniteScroll/useInitialAnchor.js +4 -3
  50. package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.d.ts +4 -1
  51. package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.js +13 -11
  52. package/dist/components/Table/hooks/infiniteScroll/useScrollEdge.d.ts +4 -1
  53. package/dist/components/Table/hooks/infiniteScroll/useScrollEdge.js +7 -18
  54. package/dist/components/Table/hooks/useWindowScrollRoot.d.ts +26 -0
  55. package/dist/components/Table/hooks/useWindowScrollRoot.js +62 -0
  56. package/dist/components/Table/lib/index.d.ts +1 -0
  57. package/dist/components/Table/lib/index.js +1 -0
  58. package/dist/components/Table/lib/scrollRoot.d.ts +24 -0
  59. package/dist/components/Table/lib/scrollRoot.js +22 -0
  60. package/dist/components/Table/types.d.ts +4 -1
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.js +1 -0
  63. package/dist/metadata/components.json +4097 -426
  64. package/package.json +1 -1
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
3
+ import { useTestId } from "../../utils/testId.js";
4
+ import { OverflowTooltip, OverflowTooltipContent, OverflowTooltipTrigger } from "../OverflowTooltip/index.js";
5
+ import { stepperTitleVariants } from "./classes.js";
6
+ import { useStepperItemContext } from "./StepperContext.js";
7
+ const StepperTitle = ({ children, className, ref, 'data-testid': testIdProp, ...rest })=>{
8
+ const { status } = useStepperItemContext();
9
+ const testId = useTestId('title', testIdProp);
10
+ return /*#__PURE__*/ jsxs(OverflowTooltip, {
11
+ children: [
12
+ /*#__PURE__*/ jsx(OverflowTooltipTrigger, {
13
+ ref: ref,
14
+ children: /*#__PURE__*/ jsx("span", {
15
+ ...rest,
16
+ "data-slot": "stepper-title",
17
+ "data-status": status,
18
+ "data-testid": testId,
19
+ className: cn(stepperTitleVariants({
20
+ status
21
+ }), className),
22
+ children: children
23
+ })
24
+ }),
25
+ /*#__PURE__*/ jsx(OverflowTooltipContent, {
26
+ children: children
27
+ })
28
+ ]
29
+ });
30
+ };
31
+ StepperTitle.displayName = 'StepperTitle';
32
+ export { StepperTitle };
@@ -0,0 +1,16 @@
1
+ import type { ButtonHTMLAttributes, FC, ReactNode, Ref } from 'react';
2
+ import { type TestableProps } from '../../utils/testId';
3
+ export interface StepperTriggerProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'>, TestableProps {
4
+ /** `StepperIndicator`, `StepperTitle` and an optional `StepperDescription`. */
5
+ children: ReactNode;
6
+ ref?: Ref<HTMLButtonElement>;
7
+ }
8
+ /**
9
+ * The step's button: a click makes the step current. Carries `aria-current="step"` while
10
+ * current, a screen-reader-only status suffix (", Completed" / ", Has errors") and every
11
+ * consumer attribute, so analytics land on the real `<button>`.
12
+ *
13
+ * Set its `id` with the root `ids.triggerId` prop: `StepperContent` is named by the root's trigger
14
+ * id, so an `id` passed here leaves the body's `aria-labelledby` pointing at nothing.
15
+ */
16
+ export declare const StepperTrigger: FC<StepperTriggerProps>;
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Steps } from "@ark-ui/react/steps";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { useTestId } from "../../utils/testId.js";
5
+ import { stepperTriggerVariants } from "./classes.js";
6
+ import { ARK_TAB_ARIA_RESET } from "./constants.js";
7
+ import { useStepperItemContext } from "./StepperContext.js";
8
+ const StepperTrigger = ({ children, className, ref, 'data-testid': testIdProp, ...rest })=>{
9
+ const { current, status, statusLabel } = useStepperItemContext();
10
+ const testId = useTestId('trigger', testIdProp);
11
+ if ('production' !== process.env.NODE_ENV && void 0 !== rest.id) console.warn(`[Stepper] id="${rest.id}" on StepperTrigger: set trigger ids with the root \`ids.triggerId\` prop, or StepperContent's aria-labelledby points at nothing.`);
12
+ return /*#__PURE__*/ jsxs(Steps.Trigger, {
13
+ ...ARK_TAB_ARIA_RESET,
14
+ "aria-current": current ? 'step' : void 0,
15
+ ...rest,
16
+ ref: ref,
17
+ type: "button",
18
+ "data-slot": "stepper-trigger",
19
+ "data-status": status,
20
+ "data-testid": testId,
21
+ className: cn(stepperTriggerVariants(), className),
22
+ children: [
23
+ children,
24
+ statusLabel && /*#__PURE__*/ jsx("span", {
25
+ className: "sr-only",
26
+ children: `, ${statusLabel}`
27
+ })
28
+ ]
29
+ });
30
+ };
31
+ StepperTrigger.displayName = 'StepperTrigger';
32
+ export { StepperTrigger };
@@ -0,0 +1,14 @@
1
+ export declare const stepperVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
2
+ export declare const stepperListVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
3
+ export declare const stepperItemVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
4
+ export declare const stepperTriggerVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
5
+ export declare const stepperIndicatorVariants: (props?: ({
6
+ status?: "active" | "completed" | "danger" | "upcoming" | null | undefined;
7
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
+ export declare const stepperTitleVariants: (props?: ({
9
+ status?: "active" | "completed" | "danger" | "upcoming" | null | undefined;
10
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
+ export declare const stepperDescriptionVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
12
+ export declare const stepperSeparatorVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
13
+ export declare const stepperSeparatorLineClassNames = "block h-px w-full bg-border-primary";
14
+ export declare const stepperContentVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
@@ -0,0 +1,37 @@
1
+ import { cva } from "class-variance-authority";
2
+ import { cn } from "../../utils/cn.js";
3
+ const stepperVariants = cva('min-w-0');
4
+ const stepperListVariants = cva('m-0 flex min-w-0 list-none items-start border-b-1 border-border-primary-light px-24 py-8');
5
+ const stepperItemVariants = cva('flex min-w-0 items-start');
6
+ const stepperTriggerVariants = cva(cn('grid min-w-0 cursor-pointer grid-cols-[auto_minmax(0,1fr)] grid-rows-[20px] items-start gap-x-4 bg-transparent p-0 text-left', 'rounded-6 focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-focus-primary'));
7
+ const stepperIndicatorVariants = cva("col-start-1 row-span-2 row-start-1 inline-flex h-20 min-w-16 shrink-0 items-center justify-center rounded-full font-mono font-medium text-xs [font-feature-settings:'liga'_0]", {
8
+ variants: {
9
+ status: {
10
+ active: 'bg-states-brand-active px-4 py-2 text-text-brand',
11
+ danger: 'bg-states-danger-active px-4 py-2 text-text-danger',
12
+ completed: 'bg-states-primary-active px-2 py-2 text-icon-primary',
13
+ upcoming: 'border-1 border-border-primary bg-component-outline-button-bg px-3 py-1 text-text-primary'
14
+ }
15
+ },
16
+ defaultVariants: {
17
+ status: 'upcoming'
18
+ }
19
+ });
20
+ const stepperTitleVariants = cva('col-start-2 row-start-1 block min-w-0 max-w-320 truncate font-sans font-normal text-sm', {
21
+ variants: {
22
+ status: {
23
+ active: 'text-text-brand',
24
+ danger: 'text-text-danger',
25
+ completed: 'text-text-primary',
26
+ upcoming: 'text-text-primary'
27
+ }
28
+ },
29
+ defaultVariants: {
30
+ status: 'upcoming'
31
+ }
32
+ });
33
+ const stepperDescriptionVariants = cva('col-start-2 row-start-2 -mt-2 block min-w-0 max-w-320 truncate font-sans text-text-secondary text-xs');
34
+ const stepperSeparatorVariants = cva('flex h-20 w-32 shrink-0 items-center px-8');
35
+ const stepperSeparatorLineClassNames = 'block h-px w-full bg-border-primary';
36
+ const stepperContentVariants = cva('min-w-0');
37
+ export { stepperContentVariants, stepperDescriptionVariants, stepperIndicatorVariants, stepperItemVariants, stepperListVariants, stepperSeparatorLineClassNames, stepperSeparatorVariants, stepperTitleVariants, stepperTriggerVariants, stepperVariants };
@@ -0,0 +1,22 @@
1
+ export declare const ARK_LIST_ARIA_RESET: {
2
+ readonly role: undefined;
3
+ readonly 'aria-owns': undefined;
4
+ readonly 'aria-orientation': undefined;
5
+ };
6
+ export declare const ARK_ITEM_ARIA_RESET: {
7
+ readonly 'aria-current': undefined;
8
+ };
9
+ export declare const ARK_TAB_ARIA_RESET: {
10
+ readonly role: undefined;
11
+ readonly 'aria-selected': undefined;
12
+ readonly 'aria-controls': undefined;
13
+ };
14
+ export declare const ARK_CONTENT_ARIA_RESET: {
15
+ readonly tabIndex: undefined;
16
+ };
17
+ /** The design supports up to six steps; more logs a dev warning. */
18
+ export declare const STEPPER_MAX_STEPS = 6;
19
+ export declare const DEFAULT_STEPPER_STATUS_LABELS: {
20
+ readonly completed: 'Completed';
21
+ readonly danger: 'Has errors';
22
+ };
@@ -0,0 +1,23 @@
1
+ const REMOVE = null;
2
+ const ARK_LIST_ARIA_RESET = {
3
+ role: REMOVE,
4
+ 'aria-owns': REMOVE,
5
+ 'aria-orientation': REMOVE
6
+ };
7
+ const ARK_ITEM_ARIA_RESET = {
8
+ 'aria-current': REMOVE
9
+ };
10
+ const ARK_TAB_ARIA_RESET = {
11
+ role: REMOVE,
12
+ 'aria-selected': REMOVE,
13
+ 'aria-controls': REMOVE
14
+ };
15
+ const ARK_CONTENT_ARIA_RESET = {
16
+ tabIndex: REMOVE
17
+ };
18
+ const STEPPER_MAX_STEPS = 6;
19
+ const DEFAULT_STEPPER_STATUS_LABELS = {
20
+ completed: 'Completed',
21
+ danger: 'Has errors'
22
+ };
23
+ export { ARK_CONTENT_ARIA_RESET, ARK_ITEM_ARIA_RESET, ARK_LIST_ARIA_RESET, ARK_TAB_ARIA_RESET, DEFAULT_STEPPER_STATUS_LABELS, STEPPER_MAX_STEPS };
@@ -0,0 +1,15 @@
1
+ export { Stepper, type StepperProps } from './Stepper';
2
+ export { StepperContent, type StepperContentProps } from './StepperContent';
3
+ export { type StepperItemContextValue, type UseStepperContextReturn, useStepperContext, useStepperItemContext, } from './StepperContext';
4
+ export { StepperDescription, type StepperDescriptionProps } from './StepperDescription';
5
+ export { StepperIndicator, type StepperIndicatorProps } from './StepperIndicator';
6
+ export { StepperItem, type StepperItemProps } from './StepperItem';
7
+ export { StepperList, type StepperListProps } from './StepperList';
8
+ export { StepperNextTrigger, type StepperNextTriggerProps } from './StepperNextTrigger';
9
+ export { StepperPrevTrigger, type StepperPrevTriggerProps } from './StepperPrevTrigger';
10
+ export { StepperRootProvider, type StepperRootProviderProps } from './StepperRootProvider';
11
+ export { StepperSeparator, type StepperSeparatorProps } from './StepperSeparator';
12
+ export { StepperTitle, type StepperTitleProps } from './StepperTitle';
13
+ export { StepperTrigger, type StepperTriggerProps } from './StepperTrigger';
14
+ export type { StepperItemStatus, StepperStatusLabels, StepperVisualStatus } from './types';
15
+ export { type UseStepperProps, type UseStepperReturn, useStepper } from './useStepper';
@@ -0,0 +1,14 @@
1
+ export { Stepper } from "./Stepper.js";
2
+ export { StepperContent } from "./StepperContent.js";
3
+ export { useStepperContext, useStepperItemContext } from "./StepperContext.js";
4
+ export { StepperDescription } from "./StepperDescription.js";
5
+ export { StepperIndicator } from "./StepperIndicator.js";
6
+ export { StepperItem } from "./StepperItem.js";
7
+ export { StepperList } from "./StepperList.js";
8
+ export { StepperNextTrigger } from "./StepperNextTrigger.js";
9
+ export { StepperPrevTrigger } from "./StepperPrevTrigger.js";
10
+ export { StepperRootProvider } from "./StepperRootProvider.js";
11
+ export { StepperSeparator } from "./StepperSeparator.js";
12
+ export { StepperTitle } from "./StepperTitle.js";
13
+ export { StepperTrigger } from "./StepperTrigger.js";
14
+ export { useStepper } from "./useStepper.js";
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * With `asChild`, Ark merges the child's props over the trigger's, so a child `disabled={false}`
4
+ * (e.g. `<Button disabled={isSaving}>`) would re-enable Next on the last step or Prev on the first.
5
+ * This takes the child's `disabled` out, for the trigger to fold into its own, so the trigger's
6
+ * computed value always wins.
7
+ */
8
+ export declare const takeAsChildDisabled: (asChild: boolean | undefined, children: ReactNode) => {
9
+ children: ReactNode;
10
+ childDisabled?: boolean;
11
+ };
@@ -0,0 +1,17 @@
1
+ import { cloneElement, isValidElement } from "react";
2
+ const takeAsChildDisabled = (asChild, children)=>{
3
+ if (!asChild || !isValidElement(children)) return {
4
+ children
5
+ };
6
+ const { disabled } = children.props;
7
+ if (void 0 === disabled) return {
8
+ children
9
+ };
10
+ return {
11
+ children: cloneElement(children, {
12
+ disabled: void 0
13
+ }),
14
+ childDisabled: disabled
15
+ };
16
+ };
17
+ export { takeAsChildDisabled };
@@ -0,0 +1,11 @@
1
+ /** Status set by the page. `'active'` is never passed: it is derived from the current step. */
2
+ export type StepperItemStatus = 'upcoming' | 'completed' | 'danger';
3
+ /** Rendered state, exposed as `data-status`. */
4
+ export type StepperVisualStatus = 'active' | StepperItemStatus;
5
+ /** Screen-reader-only status suffixes (i18n). */
6
+ export interface StepperStatusLabels {
7
+ /** Default `'Completed'`. */
8
+ completed?: string;
9
+ /** Default `'Has errors'`. */
10
+ danger?: string;
11
+ }
File without changes
@@ -0,0 +1,38 @@
1
+ import { type UseStepsProps, type UseStepsReturn } from '@ark-ui/react/steps';
2
+ /** Ark props the Stepper does not support: it never blocks a step and is horizontal only. */
3
+ export type StepperOmittedArkProps = 'linear' | 'isStepValid' | 'isStepSkippable' | 'onStepInvalid' | 'onStepComplete' | 'orientation';
4
+ export interface StepperGuardProps {
5
+ /** Number of steps, 1–6. Required: each `StepperItem` and `StepperContent` takes its `index`. */
6
+ count: number;
7
+ /** Controlled current step, 0-based. Clamped to `[0, count - 1]`. */
8
+ step?: number;
9
+ /** Uncontrolled initial step. Default `0`. Clamped to `[0, count - 1]`. */
10
+ defaultStep?: number;
11
+ /**
12
+ * Fires when a click changes the step, back or forward: `({ step }) => void`. A click on the
13
+ * current step does not fire it; use the trigger's `onClick` to observe that.
14
+ */
15
+ onStepChange?: UseStepsProps['onStepChange'];
16
+ }
17
+ /**
18
+ * The root guards shared by `Stepper` and `useStepper`: clamps `step` / `defaultStep`, warns in
19
+ * development, and never reports a step past the last one (Ark's "completed" state).
20
+ */
21
+ export declare const useStepperGuards: ({ count, step, defaultStep, onStepChange }: StepperGuardProps) => {
22
+ count: number;
23
+ step?: number | undefined;
24
+ defaultStep?: number | undefined;
25
+ onStepChange: ((details: {
26
+ step: number;
27
+ }) => void) | undefined;
28
+ };
29
+ export interface UseStepperProps extends Omit<UseStepsProps, StepperOmittedArkProps | keyof StepperGuardProps>, StepperGuardProps {
30
+ }
31
+ export type UseStepperReturn = UseStepsReturn;
32
+ /**
33
+ * Creates the Stepper machine outside the component, for `StepperRootProvider`. Wraps Ark's
34
+ * `useSteps` with the same guards as `Stepper` and `useStepperContext`: the step is clamped to
35
+ * `[0, count - 1]`, `setStep` clamps too, and Next / Back move from the shown step, so the machine
36
+ * never reaches Ark's "completed" state (`isCompleted` is always `false`).
37
+ */
38
+ export declare const useStepper: ({ count, step, defaultStep, onStepChange, ...rest }: UseStepperProps) => UseStepperReturn;
@@ -0,0 +1,58 @@
1
+ import { useSteps } from "@ark-ui/react/steps";
2
+ import { STEPPER_MAX_STEPS } from "./constants.js";
3
+ import { clampStep } from "./StepperContext.js";
4
+ const warn = (message)=>console.warn(`[Stepper] ${message}`);
5
+ const useStepperGuards = ({ count, step, defaultStep, onStepChange })=>{
6
+ const clamp = (value)=>void 0 === value ? void 0 : clampStep(value, count);
7
+ const clampedStep = clamp(step);
8
+ const clampedDefaultStep = clamp(defaultStep);
9
+ if ('production' !== process.env.NODE_ENV) {
10
+ if (count > STEPPER_MAX_STEPS) warn(`${count} steps; the design supports up to ${STEPPER_MAX_STEPS}.`);
11
+ if (count < 1) warn(`count=${count}; a Stepper needs at least one step.`);
12
+ if (void 0 !== step && step !== clampedStep) warn(`step=${step} is out of range [0, ${count - 1}]; clamped.`);
13
+ if (void 0 !== defaultStep && defaultStep !== clampedDefaultStep) warn(`defaultStep=${defaultStep} is out of range [0, ${count - 1}]; clamped.`);
14
+ }
15
+ return {
16
+ count,
17
+ ...void 0 !== clampedStep && {
18
+ step: clampedStep
19
+ },
20
+ ...void 0 !== clampedDefaultStep && {
21
+ defaultStep: clampedDefaultStep
22
+ },
23
+ onStepChange: onStepChange ? (details)=>{
24
+ if (details.step <= count - 1) onStepChange(details);
25
+ } : void 0
26
+ };
27
+ };
28
+ const useStepper = ({ count, step, defaultStep, onStepChange, ...rest })=>{
29
+ const guarded = useStepperGuards({
30
+ count,
31
+ step,
32
+ defaultStep,
33
+ onStepChange
34
+ });
35
+ const api = useSteps({
36
+ ...rest,
37
+ ...guarded
38
+ });
39
+ const value = clampStep(api.value, api.count);
40
+ const hasNextStep = value < api.count - 1;
41
+ const hasPrevStep = value > 0;
42
+ return {
43
+ ...api,
44
+ value,
45
+ percent: api.count > 0 ? value / api.count * 100 : 0,
46
+ isCompleted: false,
47
+ hasNextStep,
48
+ hasPrevStep,
49
+ goToNextStep: ()=>{
50
+ if (hasNextStep) api.setStep(value + 1);
51
+ },
52
+ goToPrevStep: ()=>{
53
+ if (hasPrevStep) api.setStep(value - 1);
54
+ },
55
+ setStep: (next)=>api.setStep(clampStep(next, api.count))
56
+ };
57
+ };
58
+ export { useStepper, useStepperGuards };
@@ -47,7 +47,7 @@ const TableBodyVirtualizedCore = ({ tbodyRef, virtualizer })=>{
47
47
  virtualRows.length > 0 && /*#__PURE__*/ jsx(Tr, {
48
48
  children: /*#__PURE__*/ jsx(Td, {
49
49
  style: {
50
- height: `${totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0)}px`,
50
+ height: `${totalSize + scrollMargin - (virtualRows[virtualRows.length - 1]?.end ?? 0)}px`,
51
51
  padding: 0,
52
52
  border: 'none'
53
53
  },
@@ -1,20 +1,43 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { useCallback, useEffect } from "react";
3
- import { useWindowVirtualizer } from "@tanstack/react-virtual";
4
- import { TABLE_VIRTUALIZATION_OVERSCAN, getRowKey } from "../lib/index.js";
2
+ import { useCallback, useContext, useEffect, useLayoutEffect, useRef } from "react";
3
+ import { elementScroll, observeElementOffset, observeElementRect, observeWindowOffset, observeWindowRect, useVirtualizer } from "@tanstack/react-virtual";
4
+ import { WindowScrollRootContext } from "../hooks/useWindowScrollRoot.js";
5
+ import { TABLE_VIRTUALIZATION_OVERSCAN, getOffsetTopInScrollRoot, getRowKey, getScrollMetrics, isWindowScrollRoot } from "../lib/index.js";
5
6
  import { useTableContext } from "../TableContext/index.js";
6
- import { getDocumentOffsetTop } from "./lib/getDocumentOffsetTop.js";
7
7
  import { measureRowElement } from "./lib/measureRowElement.js";
8
8
  import { TableBodyVirtualizedCore } from "./TableBodyVirtualizedCore.js";
9
9
  import { useResetVirtualizerOnDataChange } from "./useResetVirtualizerOnDataChange.js";
10
10
  import { useSmoothScrollOnSort } from "./useSmoothScrollOnSort.js";
11
+ const isWindowRoot = (instance)=>instance.scrollElement === window;
12
+ const observeRootRect = (instance, cb)=>isWindowRoot(instance) ? observeWindowRect(instance, cb) : observeElementRect(instance, cb);
13
+ const observeRootOffset = (instance, cb)=>isWindowRoot(instance) ? observeWindowOffset(instance, cb) : observeElementOffset(instance, cb);
11
14
  const TableBodyVirtualizedWindow = ()=>{
12
15
  const { table, estimateRowHeight, overscan, tbodyRef, virtualizerRef } = useTableContext();
13
- const virtualizer = useWindowVirtualizer({
16
+ const scrollRoot = useContext(WindowScrollRootContext);
17
+ const scrollRootRef = useRef(scrollRoot);
18
+ scrollRootRef.current = scrollRoot;
19
+ const getScrollElement = useCallback(()=>scrollRootRef.current, []);
20
+ useLayoutEffect(()=>{
21
+ const instance = virtualizerRef.current;
22
+ if (instance && instance.scrollElement !== scrollRoot) instance.scrollOffset = null;
23
+ });
24
+ const virtualizer = useVirtualizer({
14
25
  count: table.getRowModel().rows.length,
26
+ getScrollElement: getScrollElement,
27
+ observeElementRect: observeRootRect,
28
+ observeElementOffset: observeRootOffset,
29
+ scrollToFn: elementScroll,
30
+ initialRect: scrollRoot && isWindowScrollRoot(scrollRoot) ? {
31
+ width: scrollRoot.innerWidth,
32
+ height: scrollRoot.innerHeight
33
+ } : void 0,
34
+ initialOffset: ()=>{
35
+ const root = getScrollElement() ?? ("u" < typeof window ? null : window);
36
+ return root ? getScrollMetrics(root).scrollTop : 0;
37
+ },
15
38
  estimateSize: estimateRowHeight ?? (()=>40),
16
39
  overscan: overscan ?? TABLE_VIRTUALIZATION_OVERSCAN,
17
- scrollMargin: tbodyRef.current ? getDocumentOffsetTop(tbodyRef.current) : 0,
40
+ scrollMargin: tbodyRef.current && scrollRoot ? getOffsetTopInScrollRoot(tbodyRef.current, scrollRoot) : 0,
18
41
  getItemKey: useCallback((index)=>getRowKey(table.getRowModel().rows, index), [
19
42
  table
20
43
  ]),
@@ -27,8 +50,7 @@ const TableBodyVirtualizedWindow = ()=>{
27
50
  virtualizerRef
28
51
  ]);
29
52
  useResetVirtualizerOnDataChange(table, virtualizer);
30
- const getScrollTarget = useCallback(()=>window, []);
31
- useSmoothScrollOnSort(table, getScrollTarget);
53
+ useSmoothScrollOnSort(table, getScrollElement);
32
54
  return /*#__PURE__*/ jsx(TableBodyVirtualizedCore, {
33
55
  tbodyRef: tbodyRef,
34
56
  virtualizer: virtualizer
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef } from "react";
3
3
  import { useTestId } from "../../../utils/testId.js";
4
4
  import { ScrollArea, ScrollAreaScrollbar, ScrollAreaViewport } from "../../ScrollArea/index.js";
5
- import { useInfiniteScroll, useWheelHorizontalScroll } from "../hooks/index.js";
5
+ import { WindowScrollRootContext, useInfiniteScroll, useWheelHorizontalScroll, useWindowScrollRoot } from "../hooks/index.js";
6
6
  import { useContainerWidth } from "../lib/index.js";
7
7
  import { StickyGroupParent } from "../StickyGroupParent.js";
8
8
  import { TableBody } from "../TableBody/index.js";
@@ -15,8 +15,10 @@ const TableInnerWindow = ({ isEmpty, showSettings, hasConsumerSettingsMenu, aria
15
15
  const testId = useTestId('window');
16
16
  const rootRef = useRef(null);
17
17
  const containerWidth = useContainerWidth(rootRef);
18
+ const { scrollRoot, isSettled } = useWindowScrollRoot(containerRef, !isEmpty && table.getRowModel().rows.length > 0);
18
19
  useInfiniteScroll({
19
20
  mode: 'window',
21
+ scrollRoot: isSettled ? scrollRoot : null,
20
22
  table,
21
23
  virtualizerRef,
22
24
  tbodyRef,
@@ -102,7 +104,10 @@ const TableInnerWindow = ({ isEmpty, showSettings, hasConsumerSettingsMenu, aria
102
104
  /*#__PURE__*/ jsx(TableColGroup, {
103
105
  tableWidth: tableWidth
104
106
  }),
105
- !isEmpty && /*#__PURE__*/ jsx(TableBody, {})
107
+ !isEmpty && /*#__PURE__*/ jsx(WindowScrollRootContext, {
108
+ value: scrollRoot,
109
+ children: /*#__PURE__*/ jsx(TableBody, {})
110
+ })
106
111
  ]
107
112
  }),
108
113
  children
@@ -4,3 +4,4 @@ export { useMasterCell } from './useMasterCell';
4
4
  export { useStickyGroupParent } from './useStickyGroupParent';
5
5
  export { useTableState } from './useTableState';
6
6
  export { useWheelHorizontalScroll } from './useWheelHorizontalScroll';
7
+ export { useWindowScrollRoot, WindowScrollRootContext } from './useWindowScrollRoot';
@@ -4,3 +4,4 @@ export { useMasterCell } from "./useMasterCell.js";
4
4
  export { useStickyGroupParent } from "./useStickyGroupParent.js";
5
5
  export { useTableState } from "./useTableState.js";
6
6
  export { useWheelHorizontalScroll } from "./useWheelHorizontalScroll.js";
7
+ export { WindowScrollRootContext, useWindowScrollRoot } from "./useWindowScrollRoot.js";
@@ -1,11 +1,13 @@
1
1
  import type { RefObject } from 'react';
2
2
  import type { RowData, Table } from '@tanstack/react-table';
3
- import { type DSTableFeatures } from '../../lib';
3
+ import { type DSTableFeatures, type ScrollRoot } from '../../lib';
4
4
  import type { TableVirtualizerInstance } from '../../TableContext/types';
5
5
  interface UseInfiniteScrollOptions<T extends RowData> {
6
6
  mode: 'container' | 'window';
7
7
  /** Scroll element ref — required for `container` mode */
8
8
  scrollRef?: RefObject<HTMLElement | null>;
9
+ /** `window` mode: the resolved scroll root (see `useWindowScrollRoot`) */
10
+ scrollRoot?: ScrollRoot | null;
9
11
  table: Table<DSTableFeatures, T>;
10
12
  virtualizerRef: RefObject<TableVirtualizerInstance | null>;
11
13
  tbodyRef?: RefObject<HTMLTableSectionElement | null>;
@@ -18,5 +20,5 @@ interface UseInfiniteScrollOptions<T extends RowData> {
18
20
  isLoadingPrevious?: boolean;
19
21
  }
20
22
  /** Single entry point for bidirectional infinite scroll behavior. */
21
- export declare const useInfiniteScroll: <T extends RowData>({ mode, scrollRef, table, virtualizerRef, tbodyRef, onStartReached, onStartReachedThreshold, onEndReached, onEndReachedThreshold, initialScrollToRowId, isLoadingPrevious, }: UseInfiniteScrollOptions<T>) => void;
23
+ export declare const useInfiniteScroll: <T extends RowData>({ mode, scrollRef, scrollRoot, table, virtualizerRef, tbodyRef, onStartReached, onStartReachedThreshold, onEndReached, onEndReachedThreshold, initialScrollToRowId, isLoadingPrevious, }: UseInfiniteScrollOptions<T>) => void;
22
24
  export {};
@@ -2,16 +2,18 @@ import { TABLE_END_REACHED_THRESHOLD, TABLE_START_REACHED_THRESHOLD } from "../.
2
2
  import { useInitialAnchor } from "./useInitialAnchor.js";
3
3
  import { usePrependScrollAnchor } from "./usePrependScrollAnchor.js";
4
4
  import { useScrollEdge } from "./useScrollEdge.js";
5
- const useInfiniteScroll = ({ mode, scrollRef, table, virtualizerRef, tbodyRef, onStartReached, onStartReachedThreshold, onEndReached, onEndReachedThreshold, initialScrollToRowId, isLoadingPrevious })=>{
5
+ const useInfiniteScroll = ({ mode, scrollRef, scrollRoot, table, virtualizerRef, tbodyRef, onStartReached, onStartReachedThreshold, onEndReached, onEndReachedThreshold, initialScrollToRowId, isLoadingPrevious })=>{
6
6
  const rows = table.getRowModel().rows;
7
7
  const ready = useInitialAnchor({
8
8
  initialScrollToRowId,
9
9
  rows,
10
- virtualizerRef
10
+ virtualizerRef,
11
+ enabled: 'window' !== mode || null != scrollRoot
11
12
  });
12
13
  usePrependScrollAnchor({
13
14
  mode,
14
15
  scrollRef,
16
+ scrollRoot,
15
17
  rows,
16
18
  virtualizerRef,
17
19
  tbodyRef,
@@ -21,6 +23,7 @@ const useInfiniteScroll = ({ mode, scrollRef, table, virtualizerRef, tbodyRef, o
21
23
  edge: 'start',
22
24
  mode,
23
25
  scrollRef,
26
+ scrollRoot,
24
27
  onReached: onStartReached,
25
28
  threshold: onStartReachedThreshold ?? TABLE_START_REACHED_THRESHOLD,
26
29
  enabled: ready
@@ -29,6 +32,7 @@ const useInfiniteScroll = ({ mode, scrollRef, table, virtualizerRef, tbodyRef, o
29
32
  edge: 'end',
30
33
  mode,
31
34
  scrollRef,
35
+ scrollRoot,
32
36
  onReached: onEndReached,
33
37
  threshold: onEndReachedThreshold ?? TABLE_END_REACHED_THRESHOLD,
34
38
  enabled: ready
@@ -6,11 +6,17 @@ interface UseInitialAnchorOptions {
6
6
  id: string;
7
7
  }[];
8
8
  virtualizerRef: RefObject<TableVirtualizerInstance | null>;
9
+ /**
10
+ * When false, holds the scroll. `window` mode waits for its scroll root to
11
+ * settle: in a pane host the root is still the window at mount, and an
12
+ * anchor scrolled there never reaches the pane.
13
+ */
14
+ enabled?: boolean;
9
15
  }
10
16
  /**
11
17
  * Scrolls to the anchor row once on mount and returns `ready`, which gates the
12
18
  * edge detectors until the initial scroll has settled. Without this, a table
13
19
  * mounted at scrollTop 0 would fire `onStartReached` immediately.
14
20
  */
15
- export declare const useInitialAnchor: ({ initialScrollToRowId, rows, virtualizerRef, }: UseInitialAnchorOptions) => boolean;
21
+ export declare const useInitialAnchor: ({ initialScrollToRowId, rows, virtualizerRef, enabled, }: UseInitialAnchorOptions) => boolean;
16
22
  export {};
@@ -1,9 +1,9 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- const useInitialAnchor = ({ initialScrollToRowId, rows, virtualizerRef })=>{
2
+ const useInitialAnchor = ({ initialScrollToRowId, rows, virtualizerRef, enabled = true })=>{
3
3
  const [ready, setReady] = useState(!initialScrollToRowId);
4
4
  const doneRef = useRef(false);
5
5
  useEffect(()=>{
6
- if (doneRef.current || !initialScrollToRowId) return;
6
+ if (doneRef.current || !initialScrollToRowId || !enabled) return;
7
7
  const virtualizer = virtualizerRef.current;
8
8
  if (!virtualizer) {
9
9
  doneRef.current = true;
@@ -21,7 +21,8 @@ const useInitialAnchor = ({ initialScrollToRowId, rows, virtualizerRef })=>{
21
21
  }, [
22
22
  initialScrollToRowId,
23
23
  rows,
24
- virtualizerRef
24
+ virtualizerRef,
25
+ enabled
25
26
  ]);
26
27
  return ready;
27
28
  };
@@ -1,8 +1,11 @@
1
1
  import { type RefObject } from 'react';
2
+ import { type ScrollRoot } from '../../lib';
2
3
  import type { TableVirtualizerInstance } from '../../TableContext/types';
3
4
  interface UsePrependScrollAnchorOptions {
4
5
  mode: 'container' | 'window';
5
6
  scrollRef?: RefObject<HTMLElement | null>;
7
+ /** `window` mode: the resolved scroll root (see `useWindowScrollRoot`) */
8
+ scrollRoot?: ScrollRoot | null;
6
9
  rows: {
7
10
  id: string;
8
11
  }[];
@@ -31,5 +34,5 @@ interface UsePrependScrollAnchorOptions {
31
34
  * commit (hence no dep array) and its collapse joins the delta when the prepend
32
35
  * lands in the same commit.
33
36
  */
34
- export declare const usePrependScrollAnchor: ({ mode, scrollRef, rows, virtualizerRef, tbodyRef, isLoadingPrevious, }: UsePrependScrollAnchorOptions) => void;
37
+ export declare const usePrependScrollAnchor: ({ mode, scrollRef, scrollRoot, rows, virtualizerRef, tbodyRef, isLoadingPrevious, }: UsePrependScrollAnchorOptions) => void;
35
38
  export {};