@echothink-ui/motion 0.1.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 (37) hide show
  1. package/README.md +5 -0
  2. package/dist/components/AgentThinkingAnimation.d.ts +2 -0
  3. package/dist/components/AttentionPulse.d.ts +2 -0
  4. package/dist/components/DAGStatusTransition.d.ts +2 -0
  5. package/dist/components/DocumentLockPulse.d.ts +2 -0
  6. package/dist/components/PipelineFlowAnimation.d.ts +2 -0
  7. package/dist/components/ProgressTransition.d.ts +2 -0
  8. package/dist/components/SkeletonLoadingPattern.d.ts +2 -0
  9. package/dist/components/StatusChangeAnimation.d.ts +2 -0
  10. package/dist/components/StepCompletionAnimation.d.ts +2 -0
  11. package/dist/components/StreamingText.d.ts +2 -0
  12. package/dist/components/SyncProgressAnimation.d.ts +2 -0
  13. package/dist/components/motionUtils.d.ts +5 -0
  14. package/dist/components/types.d.ts +82 -0
  15. package/dist/index.cjs +2381 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.js +2333 -0
  19. package/dist/index.js.map +1 -0
  20. package/package.json +38 -0
  21. package/src/components/AgentThinkingAnimation.tsx +59 -0
  22. package/src/components/AttentionPulse.tsx +57 -0
  23. package/src/components/DAGStatusTransition.tsx +292 -0
  24. package/src/components/DocumentLockPulse.tsx +72 -0
  25. package/src/components/PipelineFlowAnimation.tsx +243 -0
  26. package/src/components/ProgressTransition.tsx +51 -0
  27. package/src/components/SkeletonLoadingPattern.tsx +248 -0
  28. package/src/components/StatusChangeAnimation.test.tsx +20 -0
  29. package/src/components/StatusChangeAnimation.tsx +89 -0
  30. package/src/components/StepCompletionAnimation.tsx +75 -0
  31. package/src/components/StreamingText.tsx +77 -0
  32. package/src/components/SyncProgressAnimation.test.tsx +49 -0
  33. package/src/components/SyncProgressAnimation.tsx +256 -0
  34. package/src/components/motionUtils.tsx +942 -0
  35. package/src/components/types.ts +111 -0
  36. package/src/index.test.tsx +97 -0
  37. package/src/index.tsx +44 -0
@@ -0,0 +1,111 @@
1
+ import type * as React from "react";
2
+ import type { EthOperationalStatus, EthSeverity, SurfaceComponentProps } from "@echothink-ui/core";
3
+
4
+ export interface ProgressTransitionProps
5
+ extends Omit<React.HTMLAttributes<HTMLElement>, "children"> {
6
+ from?: number;
7
+ to: number;
8
+ durationMs?: number;
9
+ children?: (currentValue: number) => React.ReactNode;
10
+ }
11
+
12
+ export interface MotionFlowNode {
13
+ id: string;
14
+ label: string;
15
+ status: EthOperationalStatus;
16
+ x?: number;
17
+ y?: number;
18
+ }
19
+
20
+ export interface MotionFlowEdge {
21
+ from: string;
22
+ to: string;
23
+ active?: boolean;
24
+ status?: EthOperationalStatus;
25
+ }
26
+
27
+ export interface PipelineFlowAnimationProps extends React.HTMLAttributes<HTMLElement> {
28
+ nodes: MotionFlowNode[];
29
+ edges: MotionFlowEdge[];
30
+ }
31
+
32
+ export interface DAGStatusTransitionProps extends React.HTMLAttributes<HTMLElement> {
33
+ nodes: MotionFlowNode[];
34
+ previousNodes?: MotionFlowNode[];
35
+ edges?: MotionFlowEdge[];
36
+ }
37
+
38
+ export interface AgentThinkingAnimationProps extends React.HTMLAttributes<HTMLElement> {
39
+ label?: string;
40
+ }
41
+
42
+ export type SyncProgressAnimationStatus =
43
+ | "syncing"
44
+ | "synced"
45
+ | "paused"
46
+ | "stale"
47
+ | "failed"
48
+ | "error";
49
+
50
+ export interface SyncProgressAnimationProps extends React.HTMLAttributes<HTMLElement> {
51
+ completed?: number;
52
+ description?: React.ReactNode;
53
+ label?: React.ReactNode;
54
+ progress?: number;
55
+ rate?: React.ReactNode;
56
+ remaining?: React.ReactNode;
57
+ source?: React.ReactNode;
58
+ stage?: React.ReactNode;
59
+ status?: SyncProgressAnimationStatus;
60
+ target?: React.ReactNode;
61
+ total?: number;
62
+ variant?: "compact" | "detailed";
63
+ }
64
+
65
+ export type SkeletonLoadingPatternVariant = "article" | "card" | "list" | "table";
66
+
67
+ export interface SkeletonLoadingPatternProps extends React.HTMLAttributes<HTMLElement> {
68
+ columns?: number;
69
+ label?: string;
70
+ lines?: number;
71
+ rows?: number;
72
+ variant?: SkeletonLoadingPatternVariant;
73
+ }
74
+
75
+ export interface AttentionPulseProps extends React.HTMLAttributes<HTMLElement> {
76
+ severity?: EthSeverity;
77
+ maxRepeats?: number;
78
+ }
79
+
80
+ export interface DocumentLockPulseProps extends React.HTMLAttributes<HTMLElement> {
81
+ lockedBy?: React.ReactNode;
82
+ active?: boolean;
83
+ }
84
+
85
+ export interface StreamingTextProps
86
+ extends Omit<React.HTMLAttributes<HTMLElement>, "children" | "onDone"> {
87
+ text: string;
88
+ intervalMs?: number;
89
+ onDone?: () => void;
90
+ }
91
+
92
+ export type StepCompletionState = "completed" | "current" | "pending";
93
+
94
+ export interface StepCompletionAnimationProps extends React.HTMLAttributes<HTMLElement> {
95
+ completed?: boolean;
96
+ description?: React.ReactNode;
97
+ label?: React.ReactNode;
98
+ state?: StepCompletionState;
99
+ }
100
+
101
+ export interface StatusChangeAnimationProps extends React.HTMLAttributes<HTMLElement> {
102
+ status: EthOperationalStatus;
103
+ previousStatus?: EthOperationalStatus;
104
+ label?: React.ReactNode;
105
+ }
106
+
107
+ export type {
108
+ SurfaceComponentProps,
109
+ EthOperationalStatus,
110
+ EthSeverity
111
+ };
@@ -0,0 +1,97 @@
1
+ import { act, render, screen } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { SkeletonLoadingPattern, StepCompletionAnimation, StreamingText } from "./index";
4
+
5
+ describe("@echothink-ui/motion SkeletonLoadingPattern", () => {
6
+ it("renders a labelled article skeleton by default", () => {
7
+ render(<SkeletonLoadingPattern label="Loading request summary" lines={4} />);
8
+
9
+ const status = screen.getByRole("status", { name: "Loading request summary" });
10
+
11
+ expect(status.getAttribute("data-pattern")).toBe("article");
12
+ expect(status.querySelectorAll('[data-skeleton-segment="text-line"]')).toHaveLength(4);
13
+ });
14
+
15
+ it("renders table and list patterns for repeated loading surfaces", () => {
16
+ const { rerender } = render(
17
+ <SkeletonLoadingPattern columns={4} label="Loading audit table" rows={3} variant="table" />
18
+ );
19
+
20
+ const table = screen.getByRole("status", { name: "Loading audit table" });
21
+ expect(table.getAttribute("data-pattern")).toBe("table");
22
+ expect(table.querySelectorAll('[data-skeleton-row="table"]')).toHaveLength(3);
23
+ expect(table.querySelectorAll("[data-skeleton-cell]")).toHaveLength(12);
24
+
25
+ rerender(<SkeletonLoadingPattern label="Loading review queue" rows={3} variant="list" />);
26
+
27
+ const list = screen.getByRole("status", { name: "Loading review queue" });
28
+ expect(list.getAttribute("data-pattern")).toBe("list");
29
+ expect(list.querySelectorAll('[data-skeleton-row="list"]')).toHaveLength(3);
30
+ });
31
+ });
32
+
33
+ describe("@echothink-ui/motion StepCompletionAnimation", () => {
34
+ it("renders a completed step with semantic completion text and marker", () => {
35
+ render(<StepCompletionAnimation label="Plan approved" />);
36
+
37
+ const status = screen.getByRole("status");
38
+
39
+ expect(status.getAttribute("data-state")).toBe("completed");
40
+ expect(screen.getByText("Plan approved")).toBeTruthy();
41
+ expect(screen.getByText("Completed")).toBeTruthy();
42
+ expect(status.querySelector(".eth-motion-step-completion__check")).toBeTruthy();
43
+ });
44
+
45
+ it("renders current and pending states without marking them complete", () => {
46
+ const { rerender } = render(
47
+ <StepCompletionAnimation
48
+ description="Waiting on compliance reviewer"
49
+ label="Review gate"
50
+ state="current"
51
+ />
52
+ );
53
+
54
+ expect(screen.getByRole("status").getAttribute("data-state")).toBe("current");
55
+ expect(screen.getByText("In progress")).toBeTruthy();
56
+ expect(screen.getByText("Waiting on compliance reviewer")).toBeTruthy();
57
+
58
+ rerender(<StepCompletionAnimation completed={false} label="Publish" />);
59
+
60
+ expect(screen.getByRole("status").getAttribute("data-state")).toBe("pending");
61
+ expect(screen.getByText("Pending")).toBeTruthy();
62
+ });
63
+ });
64
+
65
+ describe("@echothink-ui/motion StreamingText", () => {
66
+ it("marks active streaming text as busy until the reveal completes", () => {
67
+ vi.useFakeTimers();
68
+ try {
69
+ const onDone = vi.fn();
70
+
71
+ render(
72
+ <StreamingText
73
+ aria-label="Draft stream"
74
+ intervalMs={10}
75
+ onDone={onDone}
76
+ text="Draft ready"
77
+ />
78
+ );
79
+
80
+ const status = screen.getByRole("status", { name: "Draft stream" });
81
+ expect(status.getAttribute("aria-busy")).toBe("true");
82
+ expect(status.getAttribute("data-streaming")).toBe("true");
83
+ expect(status.textContent).toBe("");
84
+
85
+ act(() => {
86
+ vi.advanceTimersByTime(110);
87
+ });
88
+
89
+ expect(status.textContent).toBe("Draft ready");
90
+ expect(status.getAttribute("aria-busy")).toBe("false");
91
+ expect(status.getAttribute("data-streaming")).toBe("false");
92
+ expect(onDone).toHaveBeenCalledTimes(1);
93
+ } finally {
94
+ vi.useRealTimers();
95
+ }
96
+ });
97
+ });
package/src/index.tsx ADDED
@@ -0,0 +1,44 @@
1
+ export type {
2
+ AgentThinkingAnimationProps,
3
+ AttentionPulseProps,
4
+ DAGStatusTransitionProps,
5
+ DocumentLockPulseProps,
6
+ MotionFlowEdge,
7
+ MotionFlowNode,
8
+ PipelineFlowAnimationProps,
9
+ ProgressTransitionProps,
10
+ SkeletonLoadingPatternVariant,
11
+ SkeletonLoadingPatternProps,
12
+ StatusChangeAnimationProps,
13
+ StepCompletionAnimationProps,
14
+ StepCompletionState,
15
+ StreamingTextProps,
16
+ SyncProgressAnimationProps,
17
+ SyncProgressAnimationStatus
18
+ } from "./components/types";
19
+ export { AgentThinkingAnimation } from "./components/AgentThinkingAnimation";
20
+ export { AttentionPulse } from "./components/AttentionPulse";
21
+ export { DAGStatusTransition } from "./components/DAGStatusTransition";
22
+ export { DocumentLockPulse } from "./components/DocumentLockPulse";
23
+ export { PipelineFlowAnimation } from "./components/PipelineFlowAnimation";
24
+ export { ProgressTransition } from "./components/ProgressTransition";
25
+ export { SkeletonLoadingPattern } from "./components/SkeletonLoadingPattern";
26
+ export { StatusChangeAnimation } from "./components/StatusChangeAnimation";
27
+ export { StepCompletionAnimation } from "./components/StepCompletionAnimation";
28
+ export { StreamingText } from "./components/StreamingText";
29
+ export { SyncProgressAnimation } from "./components/SyncProgressAnimation";
30
+
31
+ export const MotionComponentNames = [
32
+ "ProgressTransition",
33
+ "PipelineFlowAnimation",
34
+ "DAGStatusTransition",
35
+ "AgentThinkingAnimation",
36
+ "SyncProgressAnimation",
37
+ "SkeletonLoadingPattern",
38
+ "AttentionPulse",
39
+ "DocumentLockPulse",
40
+ "StreamingText",
41
+ "StepCompletionAnimation",
42
+ "StatusChangeAnimation"
43
+ ] as const;
44
+ export type MotionComponentName = (typeof MotionComponentNames)[number];