@fanvue/ui 2.12.1 → 2.14.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.
- package/dist/cjs/components/ChatInput/ChatInput.cjs +75 -27
- package/dist/cjs/components/ChatInput/ChatInput.cjs.map +1 -1
- package/dist/cjs/components/CreatorCard/CreatorCard.cjs +81 -0
- package/dist/cjs/components/CreatorCard/CreatorCard.cjs.map +1 -0
- package/dist/cjs/components/CreatorCover/CreatorCover.cjs +83 -0
- package/dist/cjs/components/CreatorCover/CreatorCover.cjs.map +1 -0
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs +64 -0
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs.map +1 -0
- package/dist/cjs/components/CyclingText/CyclingText.cjs +137 -0
- package/dist/cjs/components/CyclingText/CyclingText.cjs.map +1 -0
- package/dist/cjs/components/CyclingText/useCyclingCycle.cjs +212 -0
- package/dist/cjs/components/CyclingText/useCyclingCycle.cjs.map +1 -0
- package/dist/cjs/components/CyclingText/useCyclingTextTrackWidth.cjs +55 -0
- package/dist/cjs/components/CyclingText/useCyclingTextTrackWidth.cjs.map +1 -0
- package/dist/cjs/components/CyclingText/usePageVisibility.cjs +38 -0
- package/dist/cjs/components/CyclingText/usePageVisibility.cjs.map +1 -0
- package/dist/cjs/components/CyclingText/usePrefersReducedMotion.cjs +39 -0
- package/dist/cjs/components/CyclingText/usePrefersReducedMotion.cjs.map +1 -0
- package/dist/cjs/components/EmptyState/EmptyState.cjs +19 -3
- package/dist/cjs/components/EmptyState/EmptyState.cjs.map +1 -1
- package/dist/cjs/index.cjs +8 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/ChatInput/ChatInput.mjs +75 -27
- package/dist/components/ChatInput/ChatInput.mjs.map +1 -1
- package/dist/components/CreatorCard/CreatorCard.mjs +64 -0
- package/dist/components/CreatorCard/CreatorCard.mjs.map +1 -0
- package/dist/components/CreatorCover/CreatorCover.mjs +66 -0
- package/dist/components/CreatorCover/CreatorCover.mjs.map +1 -0
- package/dist/components/CreatorTile/CreatorTile.mjs +47 -0
- package/dist/components/CreatorTile/CreatorTile.mjs.map +1 -0
- package/dist/components/CyclingText/CyclingText.mjs +120 -0
- package/dist/components/CyclingText/CyclingText.mjs.map +1 -0
- package/dist/components/CyclingText/useCyclingCycle.mjs +195 -0
- package/dist/components/CyclingText/useCyclingCycle.mjs.map +1 -0
- package/dist/components/CyclingText/useCyclingTextTrackWidth.mjs +38 -0
- package/dist/components/CyclingText/useCyclingTextTrackWidth.mjs.map +1 -0
- package/dist/components/CyclingText/usePageVisibility.mjs +21 -0
- package/dist/components/CyclingText/usePageVisibility.mjs.map +1 -0
- package/dist/components/CyclingText/usePrefersReducedMotion.mjs +22 -0
- package/dist/components/CyclingText/usePrefersReducedMotion.mjs.map +1 -0
- package/dist/components/EmptyState/EmptyState.mjs +19 -3
- package/dist/components/EmptyState/EmptyState.mjs.map +1 -1
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
const initialCycleState = {
|
|
4
|
+
currentIndex: 0,
|
|
5
|
+
incomingIndex: null,
|
|
6
|
+
incomingEntered: false,
|
|
7
|
+
transitioning: false
|
|
8
|
+
};
|
|
9
|
+
function cycleReducer(state, action) {
|
|
10
|
+
switch (action.type) {
|
|
11
|
+
case "tick": {
|
|
12
|
+
if (action.itemCount <= 1) return state;
|
|
13
|
+
const next = (state.currentIndex + 1) % action.itemCount;
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
incomingIndex: next,
|
|
17
|
+
incomingEntered: false,
|
|
18
|
+
transitioning: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
case "incoming_entered":
|
|
22
|
+
return { ...state, incomingEntered: true };
|
|
23
|
+
case "transition_complete": {
|
|
24
|
+
if (!state.transitioning || state.incomingIndex === null) return state;
|
|
25
|
+
return {
|
|
26
|
+
currentIndex: state.incomingIndex,
|
|
27
|
+
incomingIndex: null,
|
|
28
|
+
incomingEntered: false,
|
|
29
|
+
transitioning: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
case "pause_clear":
|
|
33
|
+
return {
|
|
34
|
+
...state,
|
|
35
|
+
incomingIndex: null,
|
|
36
|
+
incomingEntered: false,
|
|
37
|
+
transitioning: false
|
|
38
|
+
};
|
|
39
|
+
case "clamp_after_items_change": {
|
|
40
|
+
if (action.itemCount === 0) return state;
|
|
41
|
+
const idx = state.currentIndex >= action.itemCount ? 0 : state.currentIndex;
|
|
42
|
+
return {
|
|
43
|
+
currentIndex: idx,
|
|
44
|
+
incomingIndex: null,
|
|
45
|
+
incomingEntered: false,
|
|
46
|
+
transitioning: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
default:
|
|
50
|
+
return state;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function useCyclingCycle(items, sizing, intervalMs, paused, docVisible, transitionMs) {
|
|
54
|
+
const [cycle, dispatch] = React.useReducer(cycleReducer, initialCycleState);
|
|
55
|
+
const enterOuterFrameRef = React.useRef(null);
|
|
56
|
+
const enterInnerFrameRef = React.useRef(null);
|
|
57
|
+
const fallbackTimerRef = React.useRef(null);
|
|
58
|
+
const cycleTimeoutRef = React.useRef(null);
|
|
59
|
+
const clearCycleTimeout = React.useCallback(() => {
|
|
60
|
+
if (cycleTimeoutRef.current !== null) {
|
|
61
|
+
clearTimeout(cycleTimeoutRef.current);
|
|
62
|
+
cycleTimeoutRef.current = null;
|
|
63
|
+
}
|
|
64
|
+
}, []);
|
|
65
|
+
const clearAnimationArtifacts = React.useCallback(() => {
|
|
66
|
+
if (enterOuterFrameRef.current !== null) {
|
|
67
|
+
cancelAnimationFrame(enterOuterFrameRef.current);
|
|
68
|
+
enterOuterFrameRef.current = null;
|
|
69
|
+
}
|
|
70
|
+
if (enterInnerFrameRef.current !== null) {
|
|
71
|
+
cancelAnimationFrame(enterInnerFrameRef.current);
|
|
72
|
+
enterInnerFrameRef.current = null;
|
|
73
|
+
}
|
|
74
|
+
if (fallbackTimerRef.current !== null) {
|
|
75
|
+
clearTimeout(fallbackTimerRef.current);
|
|
76
|
+
fallbackTimerRef.current = null;
|
|
77
|
+
}
|
|
78
|
+
clearCycleTimeout();
|
|
79
|
+
}, [clearCycleTimeout]);
|
|
80
|
+
const itemCount = items.length;
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
if (itemCount === 0) return;
|
|
83
|
+
dispatch({ type: "clamp_after_items_change", itemCount });
|
|
84
|
+
}, [itemCount]);
|
|
85
|
+
const safeCurrentIndex = itemCount === 0 ? 0 : cycle.currentIndex % itemCount;
|
|
86
|
+
const safeIncomingIndex = cycle.incomingIndex === null || itemCount === 0 ? null : cycle.incomingIndex % itemCount;
|
|
87
|
+
const currentLabel = itemCount === 0 ? "" : items[safeCurrentIndex] ?? "";
|
|
88
|
+
const incomingLabel = safeIncomingIndex === null ? null : items[safeIncomingIndex] ?? "";
|
|
89
|
+
const longestItem = React.useMemo(() => {
|
|
90
|
+
if (itemCount === 0) return "";
|
|
91
|
+
let longest = items[0] ?? "";
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
if (item.length > longest.length) longest = item;
|
|
94
|
+
}
|
|
95
|
+
return longest;
|
|
96
|
+
}, [items, itemCount]);
|
|
97
|
+
const sizingLabel = sizing === "longest" ? longestItem : incomingLabel && incomingLabel.length > currentLabel.length ? incomingLabel : currentLabel;
|
|
98
|
+
const shouldCycle = !paused && docVisible && itemCount > 1;
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (!shouldCycle || itemCount <= 1) {
|
|
101
|
+
clearCycleTimeout();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (cycle.transitioning) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
cycleTimeoutRef.current = setTimeout(() => {
|
|
108
|
+
cycleTimeoutRef.current = null;
|
|
109
|
+
dispatch({ type: "tick", itemCount });
|
|
110
|
+
}, intervalMs);
|
|
111
|
+
return () => {
|
|
112
|
+
clearCycleTimeout();
|
|
113
|
+
};
|
|
114
|
+
}, [shouldCycle, itemCount, intervalMs, cycle.transitioning, clearCycleTimeout]);
|
|
115
|
+
React.useEffect(() => {
|
|
116
|
+
if (paused) {
|
|
117
|
+
clearAnimationArtifacts();
|
|
118
|
+
dispatch({ type: "pause_clear" });
|
|
119
|
+
}
|
|
120
|
+
}, [paused, clearAnimationArtifacts]);
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
return () => {
|
|
123
|
+
clearAnimationArtifacts();
|
|
124
|
+
};
|
|
125
|
+
}, [clearAnimationArtifacts]);
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
if (!cycle.transitioning || cycle.incomingIndex === null || cycle.incomingEntered) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (enterOuterFrameRef.current !== null) {
|
|
131
|
+
cancelAnimationFrame(enterOuterFrameRef.current);
|
|
132
|
+
}
|
|
133
|
+
if (enterInnerFrameRef.current !== null) {
|
|
134
|
+
cancelAnimationFrame(enterInnerFrameRef.current);
|
|
135
|
+
}
|
|
136
|
+
enterOuterFrameRef.current = requestAnimationFrame(() => {
|
|
137
|
+
enterOuterFrameRef.current = null;
|
|
138
|
+
enterInnerFrameRef.current = requestAnimationFrame(() => {
|
|
139
|
+
enterInnerFrameRef.current = null;
|
|
140
|
+
dispatch({ type: "incoming_entered" });
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
return () => {
|
|
144
|
+
if (enterOuterFrameRef.current !== null) {
|
|
145
|
+
cancelAnimationFrame(enterOuterFrameRef.current);
|
|
146
|
+
enterOuterFrameRef.current = null;
|
|
147
|
+
}
|
|
148
|
+
if (enterInnerFrameRef.current !== null) {
|
|
149
|
+
cancelAnimationFrame(enterInnerFrameRef.current);
|
|
150
|
+
enterInnerFrameRef.current = null;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}, [cycle.transitioning, cycle.incomingIndex, cycle.incomingEntered]);
|
|
154
|
+
React.useEffect(() => {
|
|
155
|
+
if (!cycle.transitioning) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (fallbackTimerRef.current !== null) {
|
|
159
|
+
clearTimeout(fallbackTimerRef.current);
|
|
160
|
+
}
|
|
161
|
+
fallbackTimerRef.current = setTimeout(() => {
|
|
162
|
+
dispatch({ type: "transition_complete" });
|
|
163
|
+
fallbackTimerRef.current = null;
|
|
164
|
+
}, transitionMs);
|
|
165
|
+
return () => {
|
|
166
|
+
if (fallbackTimerRef.current !== null) {
|
|
167
|
+
clearTimeout(fallbackTimerRef.current);
|
|
168
|
+
fallbackTimerRef.current = null;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}, [cycle.transitioning, transitionMs]);
|
|
172
|
+
const onOutgoingTransitionEnd = React.useCallback(
|
|
173
|
+
(event) => {
|
|
174
|
+
if (!cycle.transitioning) return;
|
|
175
|
+
if (event.propertyName !== "opacity") return;
|
|
176
|
+
if (fallbackTimerRef.current !== null) {
|
|
177
|
+
clearTimeout(fallbackTimerRef.current);
|
|
178
|
+
fallbackTimerRef.current = null;
|
|
179
|
+
}
|
|
180
|
+
dispatch({ type: "transition_complete" });
|
|
181
|
+
},
|
|
182
|
+
[cycle.transitioning]
|
|
183
|
+
);
|
|
184
|
+
return {
|
|
185
|
+
cycle,
|
|
186
|
+
currentLabel,
|
|
187
|
+
incomingLabel,
|
|
188
|
+
sizingLabel,
|
|
189
|
+
onOutgoingTransitionEnd
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
useCyclingCycle
|
|
194
|
+
};
|
|
195
|
+
//# sourceMappingURL=useCyclingCycle.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCyclingCycle.mjs","sources":["../../../src/components/CyclingText/useCyclingCycle.ts"],"sourcesContent":["import * as React from \"react\";\n\ntype CyclingTextSizingOption = \"longest\" | \"current\";\n\ntype CycleState = {\n currentIndex: number;\n incomingIndex: number | null;\n incomingEntered: boolean;\n transitioning: boolean;\n};\n\ntype CycleAction =\n | { type: \"tick\"; itemCount: number }\n | { type: \"incoming_entered\" }\n | { type: \"transition_complete\" }\n | { type: \"pause_clear\" }\n | { type: \"clamp_after_items_change\"; itemCount: number };\n\nconst initialCycleState: CycleState = {\n currentIndex: 0,\n incomingIndex: null,\n incomingEntered: false,\n transitioning: false,\n};\n\nfunction cycleReducer(state: CycleState, action: CycleAction): CycleState {\n switch (action.type) {\n case \"tick\": {\n if (action.itemCount <= 1) return state;\n const next = (state.currentIndex + 1) % action.itemCount;\n return {\n ...state,\n incomingIndex: next,\n incomingEntered: false,\n transitioning: true,\n };\n }\n case \"incoming_entered\":\n return { ...state, incomingEntered: true };\n case \"transition_complete\": {\n if (!state.transitioning || state.incomingIndex === null) return state;\n return {\n currentIndex: state.incomingIndex,\n incomingIndex: null,\n incomingEntered: false,\n transitioning: false,\n };\n }\n case \"pause_clear\":\n return {\n ...state,\n incomingIndex: null,\n incomingEntered: false,\n transitioning: false,\n };\n case \"clamp_after_items_change\": {\n if (action.itemCount === 0) return state;\n const idx = state.currentIndex >= action.itemCount ? 0 : state.currentIndex;\n return {\n currentIndex: idx,\n incomingIndex: null,\n incomingEntered: false,\n transitioning: false,\n };\n }\n default:\n return state;\n }\n}\n\nexport function useCyclingCycle(\n items: readonly string[],\n sizing: CyclingTextSizingOption,\n intervalMs: number,\n paused: boolean,\n docVisible: boolean,\n transitionMs: number,\n) {\n const [cycle, dispatch] = React.useReducer(cycleReducer, initialCycleState);\n\n const enterOuterFrameRef = React.useRef<ReturnType<typeof requestAnimationFrame> | null>(null);\n const enterInnerFrameRef = React.useRef<ReturnType<typeof requestAnimationFrame> | null>(null);\n const fallbackTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const cycleTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearCycleTimeout = React.useCallback(() => {\n if (cycleTimeoutRef.current !== null) {\n clearTimeout(cycleTimeoutRef.current);\n cycleTimeoutRef.current = null;\n }\n }, []);\n\n const clearAnimationArtifacts = React.useCallback(() => {\n if (enterOuterFrameRef.current !== null) {\n cancelAnimationFrame(enterOuterFrameRef.current);\n enterOuterFrameRef.current = null;\n }\n if (enterInnerFrameRef.current !== null) {\n cancelAnimationFrame(enterInnerFrameRef.current);\n enterInnerFrameRef.current = null;\n }\n if (fallbackTimerRef.current !== null) {\n clearTimeout(fallbackTimerRef.current);\n fallbackTimerRef.current = null;\n }\n clearCycleTimeout();\n }, [clearCycleTimeout]);\n\n const itemCount = items.length;\n\n React.useEffect(() => {\n if (itemCount === 0) return;\n dispatch({ type: \"clamp_after_items_change\", itemCount });\n }, [itemCount]);\n\n const safeCurrentIndex = itemCount === 0 ? 0 : cycle.currentIndex % itemCount;\n const safeIncomingIndex =\n cycle.incomingIndex === null || itemCount === 0 ? null : cycle.incomingIndex % itemCount;\n\n const currentLabel = itemCount === 0 ? \"\" : (items[safeCurrentIndex] ?? \"\");\n const incomingLabel = safeIncomingIndex === null ? null : (items[safeIncomingIndex] ?? \"\");\n\n const longestItem = React.useMemo(() => {\n if (itemCount === 0) return \"\";\n let longest = items[0] ?? \"\";\n for (const item of items) {\n if (item.length > longest.length) longest = item;\n }\n return longest;\n }, [items, itemCount]);\n\n const sizingLabel =\n sizing === \"longest\"\n ? longestItem\n : incomingLabel && incomingLabel.length > currentLabel.length\n ? incomingLabel\n : currentLabel;\n\n const shouldCycle = !paused && docVisible && itemCount > 1;\n\n React.useEffect(() => {\n if (!shouldCycle || itemCount <= 1) {\n clearCycleTimeout();\n return;\n }\n\n if (cycle.transitioning) {\n return;\n }\n\n cycleTimeoutRef.current = setTimeout(() => {\n cycleTimeoutRef.current = null;\n dispatch({ type: \"tick\", itemCount });\n }, intervalMs);\n\n return () => {\n clearCycleTimeout();\n };\n }, [shouldCycle, itemCount, intervalMs, cycle.transitioning, clearCycleTimeout]);\n\n React.useEffect(() => {\n if (paused) {\n clearAnimationArtifacts();\n dispatch({ type: \"pause_clear\" });\n }\n }, [paused, clearAnimationArtifacts]);\n\n React.useEffect(() => {\n return () => {\n clearAnimationArtifacts();\n };\n }, [clearAnimationArtifacts]);\n\n React.useEffect(() => {\n if (!cycle.transitioning || cycle.incomingIndex === null || cycle.incomingEntered) {\n return;\n }\n\n if (enterOuterFrameRef.current !== null) {\n cancelAnimationFrame(enterOuterFrameRef.current);\n }\n if (enterInnerFrameRef.current !== null) {\n cancelAnimationFrame(enterInnerFrameRef.current);\n }\n\n enterOuterFrameRef.current = requestAnimationFrame(() => {\n enterOuterFrameRef.current = null;\n enterInnerFrameRef.current = requestAnimationFrame(() => {\n enterInnerFrameRef.current = null;\n dispatch({ type: \"incoming_entered\" });\n });\n });\n\n return () => {\n if (enterOuterFrameRef.current !== null) {\n cancelAnimationFrame(enterOuterFrameRef.current);\n enterOuterFrameRef.current = null;\n }\n if (enterInnerFrameRef.current !== null) {\n cancelAnimationFrame(enterInnerFrameRef.current);\n enterInnerFrameRef.current = null;\n }\n };\n }, [cycle.transitioning, cycle.incomingIndex, cycle.incomingEntered]);\n\n React.useEffect(() => {\n if (!cycle.transitioning) {\n return;\n }\n if (fallbackTimerRef.current !== null) {\n clearTimeout(fallbackTimerRef.current);\n }\n fallbackTimerRef.current = setTimeout(() => {\n dispatch({ type: \"transition_complete\" });\n fallbackTimerRef.current = null;\n }, transitionMs);\n\n return () => {\n if (fallbackTimerRef.current !== null) {\n clearTimeout(fallbackTimerRef.current);\n fallbackTimerRef.current = null;\n }\n };\n }, [cycle.transitioning, transitionMs]);\n\n const onOutgoingTransitionEnd = React.useCallback(\n (event: React.TransitionEvent<HTMLSpanElement>) => {\n if (!cycle.transitioning) return;\n if (event.propertyName !== \"opacity\") return;\n if (fallbackTimerRef.current !== null) {\n clearTimeout(fallbackTimerRef.current);\n fallbackTimerRef.current = null;\n }\n dispatch({ type: \"transition_complete\" });\n },\n [cycle.transitioning],\n );\n\n return {\n cycle,\n currentLabel,\n incomingLabel,\n sizingLabel,\n onOutgoingTransitionEnd,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useCyclingTextTrackWidth() {
|
|
4
|
+
const sizingLabelRef = React.useRef(null);
|
|
5
|
+
const [trackWidth, setTrackWidth] = React.useState(null);
|
|
6
|
+
const widthFrameRef = React.useRef(null);
|
|
7
|
+
React.useEffect(() => {
|
|
8
|
+
const node = sizingLabelRef.current;
|
|
9
|
+
if (!node) return;
|
|
10
|
+
const measure = () => {
|
|
11
|
+
const next = Math.ceil(node.getBoundingClientRect().width);
|
|
12
|
+
if (!next) return;
|
|
13
|
+
if (widthFrameRef.current !== null) {
|
|
14
|
+
cancelAnimationFrame(widthFrameRef.current);
|
|
15
|
+
}
|
|
16
|
+
widthFrameRef.current = requestAnimationFrame(() => {
|
|
17
|
+
setTrackWidth(next);
|
|
18
|
+
widthFrameRef.current = null;
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
measure();
|
|
22
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
23
|
+
const observer = new ResizeObserver(measure);
|
|
24
|
+
observer.observe(node);
|
|
25
|
+
return () => {
|
|
26
|
+
observer.disconnect();
|
|
27
|
+
if (widthFrameRef.current !== null) {
|
|
28
|
+
cancelAnimationFrame(widthFrameRef.current);
|
|
29
|
+
widthFrameRef.current = null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
return { sizingLabelRef, trackWidth };
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
useCyclingTextTrackWidth
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=useCyclingTextTrackWidth.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCyclingTextTrackWidth.mjs","sources":["../../../src/components/CyclingText/useCyclingTextTrackWidth.ts"],"sourcesContent":["import * as React from \"react\";\n\n/**\n * Observes the measured label node and stores its width so the wrapper can animate width\n * without layout thrash when fonts load or the parent resizes.\n */\nexport function useCyclingTextTrackWidth() {\n const sizingLabelRef = React.useRef<HTMLSpanElement | null>(null);\n const [trackWidth, setTrackWidth] = React.useState<number | null>(null);\n const widthFrameRef = React.useRef<ReturnType<typeof requestAnimationFrame> | null>(null);\n\n React.useEffect(() => {\n const node = sizingLabelRef.current;\n if (!node) return;\n\n const measure = () => {\n const next = Math.ceil(node.getBoundingClientRect().width);\n if (!next) return;\n if (widthFrameRef.current !== null) {\n cancelAnimationFrame(widthFrameRef.current);\n }\n widthFrameRef.current = requestAnimationFrame(() => {\n setTrackWidth(next);\n widthFrameRef.current = null;\n });\n };\n\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(node);\n\n return () => {\n observer.disconnect();\n if (widthFrameRef.current !== null) {\n cancelAnimationFrame(widthFrameRef.current);\n widthFrameRef.current = null;\n }\n };\n }, []);\n\n return { sizingLabelRef, trackWidth };\n}\n"],"names":[],"mappings":";;AAMO,SAAS,2BAA2B;AACzC,QAAM,iBAAiB,MAAM,OAA+B,IAAI;AAChE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAwB,IAAI;AACtE,QAAM,gBAAgB,MAAM,OAAwD,IAAI;AAExF,QAAM,UAAU,MAAM;AACpB,UAAM,OAAO,eAAe;AAC5B,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,MAAM;AACpB,YAAM,OAAO,KAAK,KAAK,KAAK,sBAAA,EAAwB,KAAK;AACzD,UAAI,CAAC,KAAM;AACX,UAAI,cAAc,YAAY,MAAM;AAClC,6BAAqB,cAAc,OAAO;AAAA,MAC5C;AACA,oBAAc,UAAU,sBAAsB,MAAM;AAClD,sBAAc,IAAI;AAClB,sBAAc,UAAU;AAAA,MAC1B,CAAC;AAAA,IACH;AAEA,YAAA;AAEA,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,IAAI;AAErB,WAAO,MAAM;AACX,eAAS,WAAA;AACT,UAAI,cAAc,YAAY,MAAM;AAClC,6BAAqB,cAAc,OAAO;AAC1C,sBAAc,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,SAAO,EAAE,gBAAgB,WAAA;AAC3B;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function usePageVisibility() {
|
|
4
|
+
const [visible, setVisible] = React.useState(true);
|
|
5
|
+
React.useEffect(() => {
|
|
6
|
+
if (typeof document === "undefined") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const update = () => {
|
|
10
|
+
setVisible(document.visibilityState === "visible");
|
|
11
|
+
};
|
|
12
|
+
update();
|
|
13
|
+
document.addEventListener("visibilitychange", update);
|
|
14
|
+
return () => document.removeEventListener("visibilitychange", update);
|
|
15
|
+
}, []);
|
|
16
|
+
return visible;
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
usePageVisibility
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=usePageVisibility.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePageVisibility.mjs","sources":["../../../src/components/CyclingText/usePageVisibility.ts"],"sourcesContent":["import * as React from \"react\";\n\n/**\n * Tracks whether the browser tab is visible (`document.visibilityState === \"visible\"`).\n * Cycling animations use this to pause while the document is hidden so timers do not queue\n * many swaps while the user is away.\n */\nexport function usePageVisibility(): boolean {\n const [visible, setVisible] = React.useState(true);\n\n React.useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n const update = () => {\n setVisible(document.visibilityState === \"visible\");\n };\n update();\n document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n\n return visible;\n}\n"],"names":[],"mappings":";;AAOO,SAAS,oBAA6B;AAC3C,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AAEjD,QAAM,UAAU,MAAM;AACpB,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AACA,UAAM,SAAS,MAAM;AACnB,iBAAW,SAAS,oBAAoB,SAAS;AAAA,IACnD;AACA,WAAA;AACA,aAAS,iBAAiB,oBAAoB,MAAM;AACpD,WAAO,MAAM,SAAS,oBAAoB,oBAAoB,MAAM;AAAA,EACtE,GAAG,CAAA,CAAE;AAEL,SAAO;AACT;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function usePrefersReducedMotion() {
|
|
4
|
+
const [reduced, setReduced] = React.useState(false);
|
|
5
|
+
React.useEffect(() => {
|
|
6
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
10
|
+
const sync = () => {
|
|
11
|
+
setReduced(mq.matches);
|
|
12
|
+
};
|
|
13
|
+
sync();
|
|
14
|
+
mq.addEventListener("change", sync);
|
|
15
|
+
return () => mq.removeEventListener("change", sync);
|
|
16
|
+
}, []);
|
|
17
|
+
return reduced;
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
usePrefersReducedMotion
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=usePrefersReducedMotion.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrefersReducedMotion.mjs","sources":["../../../src/components/CyclingText/usePrefersReducedMotion.ts"],"sourcesContent":["import * as React from \"react\";\n\nexport function usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(false);\n\n React.useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const sync = () => {\n setReduced(mq.matches);\n };\n sync();\n mq.addEventListener(\"change\", sync);\n return () => mq.removeEventListener(\"change\", sync);\n }, []);\n\n return reduced;\n}\n"],"names":[],"mappings":";;AAEO,SAAS,0BAAmC;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAElD,QAAM,UAAU,MAAM;AACpB,QAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E;AAAA,IACF;AACA,UAAM,KAAK,OAAO,WAAW,kCAAkC;AAC/D,UAAM,OAAO,MAAM;AACjB,iBAAW,GAAG,OAAO;AAAA,IACvB;AACA,SAAA;AACA,OAAG,iBAAiB,UAAU,IAAI;AAClC,WAAO,MAAM,GAAG,oBAAoB,UAAU,IAAI;AAAA,EACpD,GAAG,CAAA,CAAE;AAEL,SAAO;AACT;"}
|
|
@@ -3,6 +3,20 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../utils/cn.mjs";
|
|
5
5
|
import { Button } from "../Button/Button.mjs";
|
|
6
|
+
const titleSizeClass = {
|
|
7
|
+
xs: "typography-bold-heading-xs",
|
|
8
|
+
sm: "typography-bold-heading-sm",
|
|
9
|
+
md: "typography-bold-heading-md",
|
|
10
|
+
lg: "typography-bold-heading-lg",
|
|
11
|
+
xl: "typography-bold-heading-xl"
|
|
12
|
+
};
|
|
13
|
+
const mediaSizeClass = {
|
|
14
|
+
xs: "h-[80px]",
|
|
15
|
+
sm: "h-[160px]",
|
|
16
|
+
md: "h-[200px]",
|
|
17
|
+
lg: "h-[280px]",
|
|
18
|
+
xl: "h-[360px]"
|
|
19
|
+
};
|
|
6
20
|
function isNonEmptyString(value) {
|
|
7
21
|
return typeof value === "string" && value.length > 0;
|
|
8
22
|
}
|
|
@@ -20,8 +34,10 @@ const EmptyState = React.forwardRef(
|
|
|
20
34
|
className,
|
|
21
35
|
variant = "default",
|
|
22
36
|
title,
|
|
37
|
+
titleSize = "lg",
|
|
23
38
|
description,
|
|
24
39
|
media,
|
|
40
|
+
mediaSize = "lg",
|
|
25
41
|
primaryAction,
|
|
26
42
|
secondaryAction,
|
|
27
43
|
...props
|
|
@@ -31,11 +47,11 @@ const EmptyState = React.forwardRef(
|
|
|
31
47
|
const regionLabelledBy = hasSlotContent(title) ? titleId : void 0;
|
|
32
48
|
const renderedPrimary = primaryAction === void 0 || primaryAction === null || primaryAction === false || primaryAction === "" ? null : isNonEmptyString(primaryAction) ? /* @__PURE__ */ jsx(Button, { variant: "brand", fullWidth: true, children: primaryAction }) : primaryAction;
|
|
33
49
|
const renderedSecondary = secondaryAction === void 0 || secondaryAction === null || secondaryAction === false || secondaryAction === "" ? null : isNonEmptyString(secondaryAction) ? /* @__PURE__ */ jsx(Button, { variant: "secondary", fullWidth: true, children: secondaryAction }) : secondaryAction;
|
|
34
|
-
const renderedTitle = title === void 0 || title === null || title === false || title === "" ? null : isNonEmptyString(title) ? /* @__PURE__ */ jsx("h2", { id: titleId, className: "m-0
|
|
50
|
+
const renderedTitle = title === void 0 || title === null || title === false || title === "" ? null : isNonEmptyString(title) ? /* @__PURE__ */ jsx("h2", { id: titleId, className: cn("m-0 text-content-primary", titleSizeClass[titleSize]), children: title }) : /* @__PURE__ */ jsx(
|
|
35
51
|
"div",
|
|
36
52
|
{
|
|
37
53
|
id: titleId,
|
|
38
|
-
className: "
|
|
54
|
+
className: cn("text-content-primary min-w-0 w-full", titleSizeClass[titleSize]),
|
|
39
55
|
children: title
|
|
40
56
|
}
|
|
41
57
|
);
|
|
@@ -64,7 +80,7 @@ const EmptyState = React.forwardRef(
|
|
|
64
80
|
),
|
|
65
81
|
...props,
|
|
66
82
|
children: [
|
|
67
|
-
showMedia && /* @__PURE__ */ jsx("div", { className: "
|
|
83
|
+
showMedia && /* @__PURE__ */ jsx("div", { className: cn("w-full overflow-hidden rounded-md", mediaSizeClass[mediaSize]), children: renderedMedia }),
|
|
68
84
|
/* @__PURE__ */ jsxs(
|
|
69
85
|
"div",
|
|
70
86
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\n/** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */\nexport type EmptyStateSlot = string | React.ReactNode;\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasSlotContent(value: EmptyStateSlot | undefined): boolean {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n if (typeof value === \"string\") {\n return value.length > 0;\n }\n return true;\n}\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. Strings use library heading styles; pass a node for full control. */\n title?: EmptyStateSlot;\n /** Supporting body copy. Strings use library body styles; pass a node for rich text. */\n description?: EmptyStateSlot;\n /**\n * Top visual / illustration slot.\n * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.\n */\n media?: EmptyStateSlot;\n /**\n * Primary call to action.\n * A string renders a brand `Button` with that label; pass a node for links, loading, etc.\n */\n primaryAction?: EmptyStateSlot;\n /**\n * Secondary action below the primary.\n * A string renders a secondary `Button` with that label; pass a node when you need more control.\n */\n secondaryAction?: EmptyStateSlot;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n description,\n media,\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy = hasSlotContent(title) ? titleId : undefined;\n\n const renderedPrimary =\n primaryAction === undefined ||\n primaryAction === null ||\n primaryAction === false ||\n primaryAction === \"\" ? null : isNonEmptyString(primaryAction) ? (\n <Button variant=\"brand\" fullWidth>\n {primaryAction}\n </Button>\n ) : (\n primaryAction\n );\n\n const renderedSecondary =\n secondaryAction === undefined ||\n secondaryAction === null ||\n secondaryAction === false ||\n secondaryAction === \"\" ? null : isNonEmptyString(secondaryAction) ? (\n <Button variant=\"secondary\" fullWidth>\n {secondaryAction}\n </Button>\n ) : (\n secondaryAction\n );\n\n const renderedTitle =\n title === undefined ||\n title === null ||\n title === false ||\n title === \"\" ? null : isNonEmptyString(title) ? (\n <h2 id={titleId} className
|
|
1
|
+
{"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\nexport type EmptyStateTitleSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst titleSizeClass: Record<EmptyStateTitleSize, string> = {\n xs: \"typography-bold-heading-xs\",\n sm: \"typography-bold-heading-sm\",\n md: \"typography-bold-heading-md\",\n lg: \"typography-bold-heading-lg\",\n xl: \"typography-bold-heading-xl\",\n};\n\nexport type EmptyStateMediaSize = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst mediaSizeClass: Record<EmptyStateMediaSize, string> = {\n xs: \"h-[80px]\",\n sm: \"h-[160px]\",\n md: \"h-[200px]\",\n lg: \"h-[280px]\",\n xl: \"h-[360px]\",\n};\n\n/** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */\nexport type EmptyStateSlot = string | React.ReactNode;\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasSlotContent(value: EmptyStateSlot | undefined): boolean {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n if (typeof value === \"string\") {\n return value.length > 0;\n }\n return true;\n}\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. Strings use library heading styles; pass a node for full control. */\n title?: EmptyStateSlot;\n /**\n * Size of the title heading — mapped internally to bold heading typography tokens.\n * @default \"lg\"\n */\n titleSize?: EmptyStateTitleSize;\n /** Supporting body copy. Strings use library body styles; pass a node for rich text. */\n description?: EmptyStateSlot;\n /**\n * Top visual / illustration slot.\n * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.\n */\n media?: EmptyStateSlot;\n /**\n * Height of the media container.\n * @default \"lg\"\n */\n mediaSize?: EmptyStateMediaSize;\n /**\n * Primary call to action.\n * A string renders a brand `Button` with that label; pass a node for links, loading, etc.\n */\n primaryAction?: EmptyStateSlot;\n /**\n * Secondary action below the primary.\n * A string renders a secondary `Button` with that label; pass a node when you need more control.\n */\n secondaryAction?: EmptyStateSlot;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n titleSize = \"lg\",\n description,\n media,\n mediaSize = \"lg\",\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy = hasSlotContent(title) ? titleId : undefined;\n\n const renderedPrimary =\n primaryAction === undefined ||\n primaryAction === null ||\n primaryAction === false ||\n primaryAction === \"\" ? null : isNonEmptyString(primaryAction) ? (\n <Button variant=\"brand\" fullWidth>\n {primaryAction}\n </Button>\n ) : (\n primaryAction\n );\n\n const renderedSecondary =\n secondaryAction === undefined ||\n secondaryAction === null ||\n secondaryAction === false ||\n secondaryAction === \"\" ? null : isNonEmptyString(secondaryAction) ? (\n <Button variant=\"secondary\" fullWidth>\n {secondaryAction}\n </Button>\n ) : (\n secondaryAction\n );\n\n const renderedTitle =\n title === undefined ||\n title === null ||\n title === false ||\n title === \"\" ? null : isNonEmptyString(title) ? (\n <h2 id={titleId} className={cn(\"m-0 text-content-primary\", titleSizeClass[titleSize])}>\n {title}\n </h2>\n ) : (\n <div\n id={titleId}\n className={cn(\"text-content-primary min-w-0 w-full\", titleSizeClass[titleSize])}\n >\n {title}\n </div>\n );\n\n const renderedDescription =\n description === undefined ||\n description === null ||\n description === false ||\n description === \"\" ? null : isNonEmptyString(description) ? (\n <p className=\"m-0 typography-regular-body-lg text-content-secondary\">{description}</p>\n ) : (\n <div className=\"typography-regular-body-lg text-content-secondary min-w-0 w-full\">\n {description}\n </div>\n );\n\n const renderedMedia =\n media === undefined ||\n media === null ||\n media === false ||\n media === \"\" ? null : isNonEmptyString(media) ? (\n <img\n src={media}\n alt=\"\"\n decoding=\"async\"\n className=\"block h-full w-full object-contain object-center\"\n />\n ) : (\n media\n );\n\n const showMedia = renderedMedia !== null;\n const showActions = renderedPrimary !== null || renderedSecondary !== null;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {showMedia && (\n <div className={cn(\"w-full overflow-hidden rounded-md\", mediaSizeClass[mediaSize])}>\n {renderedMedia}\n </div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {renderedTitle}\n {renderedDescription}\n </div>\n\n {showActions ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {renderedPrimary}\n {renderedSecondary}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":[],"mappings":"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"}
|