@agent-native/toolkit 0.18.0 → 0.18.1-nightly-20260829003721

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.
@@ -1,5 +1,5 @@
1
1
  export type CubeLoaderProps = Omit<React.SVGProps<SVGSVGElement>, "children"> & {
2
2
  size?: number | string;
3
3
  };
4
- export declare function CubeLoader({ className, size, width, height, ...props }: CubeLoaderProps): import("react").JSX.Element;
4
+ export declare function CubeLoader({ className, size, width, height, ref, ...props }: CubeLoaderProps): import("react").JSX.Element;
5
5
  //# sourceMappingURL=cube-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cube-loader.d.ts","sourceRoot":"","sources":["../../src/ui/cube-loader.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAC7B,UAAU,CACX,GAAG;IACF,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,KAAK,EACL,MAAM,EACN,GAAG,KAAK,EACT,EAAE,eAAe,+BA8CjB"}
1
+ {"version":3,"file":"cube-loader.d.ts","sourceRoot":"","sources":["../../src/ui/cube-loader.tsx"],"names":[],"mappings":"AAyBA,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAC7B,UAAU,CACX,GAAG;IACF,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,KAAK,EACL,MAAM,EACN,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAqDjB"}
@@ -1,11 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback } from "react";
2
3
  import { cn } from "../utils.js";
3
4
  const CUBE_DELAYS = [90, 180, 270, 0, 90, 180, 90, 180, 270];
4
- export function CubeLoader({ className, size, width, height, ...props }) {
5
+ const CUBE_ANIMATION_DURATION_MS = 650;
6
+ function setRef(ref, value) {
7
+ if (typeof ref === "function")
8
+ ref(value);
9
+ else if (ref)
10
+ ref.current = value;
11
+ }
12
+ function setCubeAnimationPhase(svg, ref) {
13
+ if (svg && typeof window !== "undefined") {
14
+ svg.style.setProperty("--an-cube-loader-phase", `${window.performance.now() % CUBE_ANIMATION_DURATION_MS}ms`);
15
+ }
16
+ setRef(ref, svg);
17
+ }
18
+ export function CubeLoader({ className, size, width, height, ref, ...props }) {
5
19
  const hasRole = Object.prototype.hasOwnProperty.call(props, "role");
6
20
  const hasExplicitSize = size !== undefined || width !== undefined || height !== undefined;
7
21
  const ariaLabel = props["aria-label"] ?? (hasRole ? undefined : "Loading");
8
- return (_jsxs("svg", { ...props, role: hasRole ? props.role : "status", "aria-label": ariaLabel, width: width ?? size ?? 24, height: height ?? size ?? 24, viewBox: "0 0 24 24", fill: "currentColor", className: cn(!hasExplicitSize && "size-4", className), "data-agent-native-cube-loader": "true", children: [_jsx("style", { children: `
22
+ const setRefs = useCallback((svg) => setCubeAnimationPhase(svg, ref), [ref]);
23
+ return (_jsxs("svg", { ...props, ref: setRefs, role: hasRole ? props.role : "status", "aria-label": ariaLabel, width: width ?? size ?? 24, height: height ?? size ?? 24, viewBox: "0 0 24 24", fill: "currentColor", className: cn(!hasExplicitSize && "size-4", className), "data-agent-native-cube-loader": "true", children: [_jsx("style", { children: `
9
24
  @keyframes an-cube-pulse {
10
25
  0%, 100% { opacity: 0.15; }
11
26
  50% { opacity: 0.95; }
@@ -18,6 +33,8 @@ export function CubeLoader({ className, size, width, height, ...props }) {
18
33
  @media (prefers-reduced-motion: reduce) {
19
34
  .an-cube-cell { animation: none; }
20
35
  }
21
- ` }), CUBE_DELAYS.map((delay, index) => (_jsx("rect", { className: "an-cube-cell", x: 2.5 + (index % 3) * 7, y: 2.5 + Math.floor(index / 3) * 7, width: 5, height: 5, rx: 1, style: { animationDelay: `${delay}ms` } }, index)))] }));
36
+ ` }), CUBE_DELAYS.map((delay, index) => (_jsx("rect", { className: "an-cube-cell", x: 2.5 + (index % 3) * 7, y: 2.5 + Math.floor(index / 3) * 7, width: 5, height: 5, rx: 1, style: {
37
+ animationDelay: `calc(${delay}ms - var(--an-cube-loader-phase, 0ms))`,
38
+ } }, index)))] }));
22
39
  }
23
40
  //# sourceMappingURL=cube-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cube-loader.js","sourceRoot":"","sources":["../../src/ui/cube-loader.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAS7D,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,KAAK,EACL,MAAM,EACN,GAAG,KAAK,EACQ;IAChB,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACpE,MAAM,eAAe,GACnB,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC;IACpE,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE3E,OAAO,CACL,kBACM,KAAK,EACT,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,gBACzB,SAAS,EACrB,KAAK,EAAE,KAAK,IAAI,IAAI,IAAI,EAAE,EAC1B,MAAM,EAAE,MAAM,IAAI,IAAI,IAAI,EAAE,EAC5B,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EACnB,SAAS,EAAE,EAAE,CAAC,CAAC,eAAe,IAAI,QAAQ,EAAE,SAAS,CAAC,mCACxB,MAAM,aAEpC,0BAAQ;;;;;;;;;;;;;OAaP,GAAS,EACT,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACjC,eAEE,SAAS,EAAC,cAAc,EACxB,CAAC,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,EACxB,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,EAClC,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,EACT,EAAE,EAAE,CAAC,EACL,KAAK,EAAE,EAAE,cAAc,EAAE,GAAG,KAAK,IAAI,EAAE,IAPlC,KAAK,CAQV,CACH,CAAC,IACE,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cube-loader.js","sourceRoot":"","sources":["../../src/ui/cube-loader.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7D,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAEvC,SAAS,MAAM,CAAU,GAA6B,EAAE,KAAqB;IAC3E,IAAI,OAAO,GAAG,KAAK,UAAU;QAAE,GAAG,CAAC,KAAK,CAAC,CAAC;SACrC,IAAI,GAAG;QAAE,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;AACpC,CAAC;AAED,SAAS,qBAAqB,CAC5B,GAAyB,EACzB,GAAwB;IAExB,IAAI,GAAG,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QACzC,GAAG,CAAC,KAAK,CAAC,WAAW,CACnB,wBAAwB,EACxB,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,0BAA0B,IAAI,CAC7D,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACnB,CAAC;AASD,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,KAAK,EACL,MAAM,EACN,GAAG,EACH,GAAG,KAAK,EACQ;IAChB,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACpE,MAAM,eAAe,GACnB,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC;IACpE,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,GAAyB,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,EAC9D,CAAC,GAAG,CAAC,CACN,CAAC;IAEF,OAAO,CACL,kBACM,KAAK,EACT,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,gBACzB,SAAS,EACrB,KAAK,EAAE,KAAK,IAAI,IAAI,IAAI,EAAE,EAC1B,MAAM,EAAE,MAAM,IAAI,IAAI,IAAI,EAAE,EAC5B,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EACnB,SAAS,EAAE,EAAE,CAAC,CAAC,eAAe,IAAI,QAAQ,EAAE,SAAS,CAAC,mCACxB,MAAM,aAEpC,0BAAQ;;;;;;;;;;;;;OAaP,GAAS,EACT,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACjC,eAEE,SAAS,EAAC,cAAc,EACxB,CAAC,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,EACxB,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,EAClC,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,EACT,EAAE,EAAE,CAAC,EACL,KAAK,EAAE;oBACL,cAAc,EAAE,QAAQ,KAAK,wCAAwC;iBACtE,IATI,KAAK,CAUV,CACH,CAAC,IACE,CACP,CAAC;AACJ,CAAC"}
@@ -1,23 +1,57 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @vitest-environment happy-dom
3
+ import { act } from "react";
4
+ import { createElement } from "react";
5
+ import { createRoot } from "react-dom/client";
6
+ import { renderToStaticMarkup } from "react-dom/server";
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
8
  import { CubeLoader } from "./cube-loader.js";
3
9
  describe("CubeLoader", () => {
10
+ let container;
11
+ let root;
12
+ beforeEach(() => {
13
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
14
+ container = document.createElement("div");
15
+ document.body.appendChild(container);
16
+ root = createRoot(container);
17
+ });
18
+ afterEach(() => {
19
+ act(() => root.unmount());
20
+ container.remove();
21
+ vi.unstubAllGlobals();
22
+ });
4
23
  it("honors explicit sizing and decorative semantics", () => {
5
- const loader = CubeLoader({
24
+ const loader = renderToStaticMarkup(createElement(CubeLoader, {
6
25
  "aria-label": undefined,
7
26
  role: undefined,
8
27
  size: 10,
9
- });
10
- expect(loader.props.width).toBe(10);
11
- expect(loader.props.height).toBe(10);
12
- expect(loader.props.className).not.toContain("size-4");
13
- expect(loader.props.role).toBeUndefined();
14
- expect(loader.props["aria-label"]).toBeUndefined();
28
+ }));
29
+ expect(loader).toContain('width="10"');
30
+ expect(loader).toContain('height="10"');
31
+ expect(loader).not.toContain('class="size-4"');
32
+ expect(loader).not.toContain("role=");
33
+ expect(loader).not.toContain("aria-label=");
15
34
  });
16
35
  it("adds accessible defaults when role semantics are omitted", () => {
17
- const loader = CubeLoader({});
18
- expect(loader.props.role).toBe("status");
19
- expect(loader.props["aria-label"]).toBe("Loading");
20
- expect(loader.props.className).toContain("size-4");
36
+ const loader = renderToStaticMarkup(createElement(CubeLoader));
37
+ expect(loader).toContain('role="status"');
38
+ expect(loader).toContain('aria-label="Loading"');
39
+ expect(loader).toContain('class="size-4"');
40
+ });
41
+ it("anchors its cells to the page timeline when mounted again", () => {
42
+ expect(renderToStaticMarkup(createElement(CubeLoader))).toContain("calc(90ms - var(--an-cube-loader-phase, 0ms))");
43
+ });
44
+ it("preserves a caller ref without reattaching on rerenders", () => {
45
+ const callerRef = vi.fn();
46
+ act(() => {
47
+ root.render(_jsx(CubeLoader, { ref: callerRef, "data-testid": "first-render" }));
48
+ });
49
+ expect(callerRef).toHaveBeenCalledTimes(1);
50
+ expect(callerRef).toHaveBeenLastCalledWith(expect.any(SVGSVGElement));
51
+ act(() => {
52
+ root.render(_jsx(CubeLoader, { ref: callerRef, "data-testid": "second-render" }));
53
+ });
54
+ expect(callerRef).toHaveBeenCalledTimes(1);
21
55
  });
22
56
  });
23
57
  //# sourceMappingURL=cube-loader.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cube-loader.spec.js","sourceRoot":"","sources":["../../src/ui/cube-loader.spec.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,MAAM,GAAG,UAAU,CAAC;YACxB,YAAY,EAAE,SAAS;YACvB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE;SACT,CAA0C,CAAC;QAE5C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,CAA0C,CAAC;QAEvE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cube-loader.spec.js","sourceRoot":"","sources":["../../src/ui/cube-loader.spec.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/toolkit",
3
- "version": "0.18.0",
3
+ "version": "0.18.1-nightly-20260829003721",
4
4
  "description": "Reusable app-building UI and helpers for Agent-Native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -1,28 +1,73 @@
1
- import type { ReactElement } from "react";
2
- import { describe, expect, it } from "vitest";
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { createElement } from "react";
5
+ import { createRoot, type Root } from "react-dom/client";
6
+ import { renderToStaticMarkup } from "react-dom/server";
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
8
 
4
9
  import { CubeLoader } from "./cube-loader.js";
5
10
 
6
11
  describe("CubeLoader", () => {
12
+ let container: HTMLDivElement;
13
+ let root: Root;
14
+
15
+ beforeEach(() => {
16
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
17
+ container = document.createElement("div");
18
+ document.body.appendChild(container);
19
+ root = createRoot(container);
20
+ });
21
+
22
+ afterEach(() => {
23
+ act(() => root.unmount());
24
+ container.remove();
25
+ vi.unstubAllGlobals();
26
+ });
27
+
7
28
  it("honors explicit sizing and decorative semantics", () => {
8
- const loader = CubeLoader({
9
- "aria-label": undefined,
10
- role: undefined,
11
- size: 10,
12
- }) as ReactElement<Record<string, unknown>>;
13
-
14
- expect(loader.props.width).toBe(10);
15
- expect(loader.props.height).toBe(10);
16
- expect(loader.props.className).not.toContain("size-4");
17
- expect(loader.props.role).toBeUndefined();
18
- expect(loader.props["aria-label"]).toBeUndefined();
29
+ const loader = renderToStaticMarkup(
30
+ createElement(CubeLoader, {
31
+ "aria-label": undefined,
32
+ role: undefined,
33
+ size: 10,
34
+ }),
35
+ );
36
+
37
+ expect(loader).toContain('width="10"');
38
+ expect(loader).toContain('height="10"');
39
+ expect(loader).not.toContain('class="size-4"');
40
+ expect(loader).not.toContain("role=");
41
+ expect(loader).not.toContain("aria-label=");
19
42
  });
20
43
 
21
44
  it("adds accessible defaults when role semantics are omitted", () => {
22
- const loader = CubeLoader({}) as ReactElement<Record<string, unknown>>;
45
+ const loader = renderToStaticMarkup(createElement(CubeLoader));
46
+
47
+ expect(loader).toContain('role="status"');
48
+ expect(loader).toContain('aria-label="Loading"');
49
+ expect(loader).toContain('class="size-4"');
50
+ });
51
+
52
+ it("anchors its cells to the page timeline when mounted again", () => {
53
+ expect(renderToStaticMarkup(createElement(CubeLoader))).toContain(
54
+ "calc(90ms - var(--an-cube-loader-phase, 0ms))",
55
+ );
56
+ });
57
+
58
+ it("preserves a caller ref without reattaching on rerenders", () => {
59
+ const callerRef = vi.fn();
60
+
61
+ act(() => {
62
+ root.render(<CubeLoader ref={callerRef} data-testid="first-render" />);
63
+ });
64
+ expect(callerRef).toHaveBeenCalledTimes(1);
65
+ expect(callerRef).toHaveBeenLastCalledWith(expect.any(SVGSVGElement));
66
+
67
+ act(() => {
68
+ root.render(<CubeLoader ref={callerRef} data-testid="second-render" />);
69
+ });
23
70
 
24
- expect(loader.props.role).toBe("status");
25
- expect(loader.props["aria-label"]).toBe("Loading");
26
- expect(loader.props.className).toContain("size-4");
71
+ expect(callerRef).toHaveBeenCalledTimes(1);
27
72
  });
28
73
  });
@@ -1,6 +1,27 @@
1
+ import { useCallback, type Ref } from "react";
2
+
1
3
  import { cn } from "../utils.js";
2
4
 
3
5
  const CUBE_DELAYS = [90, 180, 270, 0, 90, 180, 90, 180, 270];
6
+ const CUBE_ANIMATION_DURATION_MS = 650;
7
+
8
+ function setRef<Element>(ref: Ref<Element> | undefined, value: Element | null) {
9
+ if (typeof ref === "function") ref(value);
10
+ else if (ref) ref.current = value;
11
+ }
12
+
13
+ function setCubeAnimationPhase(
14
+ svg: SVGSVGElement | null,
15
+ ref?: Ref<SVGSVGElement>,
16
+ ) {
17
+ if (svg && typeof window !== "undefined") {
18
+ svg.style.setProperty(
19
+ "--an-cube-loader-phase",
20
+ `${window.performance.now() % CUBE_ANIMATION_DURATION_MS}ms`,
21
+ );
22
+ }
23
+ setRef(ref, svg);
24
+ }
4
25
 
5
26
  export type CubeLoaderProps = Omit<
6
27
  React.SVGProps<SVGSVGElement>,
@@ -14,16 +35,22 @@ export function CubeLoader({
14
35
  size,
15
36
  width,
16
37
  height,
38
+ ref,
17
39
  ...props
18
40
  }: CubeLoaderProps) {
19
41
  const hasRole = Object.prototype.hasOwnProperty.call(props, "role");
20
42
  const hasExplicitSize =
21
43
  size !== undefined || width !== undefined || height !== undefined;
22
44
  const ariaLabel = props["aria-label"] ?? (hasRole ? undefined : "Loading");
45
+ const setRefs = useCallback(
46
+ (svg: SVGSVGElement | null) => setCubeAnimationPhase(svg, ref),
47
+ [ref],
48
+ );
23
49
 
24
50
  return (
25
51
  <svg
26
52
  {...props}
53
+ ref={setRefs}
27
54
  role={hasRole ? props.role : "status"}
28
55
  aria-label={ariaLabel}
29
56
  width={width ?? size ?? 24}
@@ -56,7 +83,9 @@ export function CubeLoader({
56
83
  width={5}
57
84
  height={5}
58
85
  rx={1}
59
- style={{ animationDelay: `${delay}ms` }}
86
+ style={{
87
+ animationDelay: `calc(${delay}ms - var(--an-cube-loader-phase, 0ms))`,
88
+ }}
60
89
  />
61
90
  ))}
62
91
  </svg>