@mks2508/mks-ui 0.2.1 → 0.3.1
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/react-ui/hooks/Animation/UseAutoHeight.js +7 -7
- package/dist/react-ui/hooks/DOM/UseIsInView.js +3 -3
- package/dist/react-ui/hooks/Formatting/UseListFormat.d.ts +49 -0
- package/dist/react-ui/hooks/Formatting/UseListFormat.d.ts.map +1 -0
- package/dist/react-ui/hooks/Formatting/UseListFormat.js +105 -0
- package/dist/react-ui/hooks/State/UseControlledState.js +4 -4
- package/dist/react-ui/hooks/State/UseDataState.js +5 -5
- package/dist/react-ui/hooks/index.d.ts +2 -0
- package/dist/react-ui/hooks/index.d.ts.map +1 -1
- package/dist/react-ui/hooks/index.js +1 -0
- package/dist/react-ui/index.js +22 -2
- package/dist/react-ui/lib/get-strict-context.js +3 -3
- package/dist/react-ui/primitives/CountingNumber/index.js +3 -3
- package/dist/react-ui/primitives/Highlight/index.js +26 -26
- package/dist/react-ui/primitives/Slot/index.js +3 -3
- package/dist/react-ui/primitives/index.d.ts +1 -0
- package/dist/react-ui/primitives/index.d.ts.map +1 -1
- package/dist/react-ui/primitives/index.js +18 -0
- package/dist/react-ui/primitives/waapi/Morph/Morph.types.d.ts +76 -0
- package/dist/react-ui/primitives/waapi/Morph/Morph.types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/MorphContext.d.ts +11 -0
- package/dist/react-ui/primitives/waapi/Morph/MorphContext.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/MorphContext.js +19 -0
- package/dist/react-ui/primitives/waapi/Morph/index.d.ts +23 -0
- package/dist/react-ui/primitives/waapi/Morph/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/index.js +45 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/index.d.ts +12 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.d.ts +38 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.js +78 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.d.ts +23 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.js +140 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.d.ts +28 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.js +77 -0
- package/dist/react-ui/primitives/waapi/Morph/useMorph.d.ts +27 -0
- package/dist/react-ui/primitives/waapi/Morph/useMorph.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Morph/useMorph.js +86 -0
- package/dist/react-ui/primitives/waapi/Reorder/Reorder.types.d.ts +168 -0
- package/dist/react-ui/primitives/waapi/Reorder/Reorder.types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Reorder/index.d.ts +25 -0
- package/dist/react-ui/primitives/waapi/Reorder/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Reorder/index.js +186 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorder.d.ts +26 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorder.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorder.js +48 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.d.ts +33 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Reorder/useReorderPresence.js +137 -0
- package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.d.ts +47 -0
- package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.js +72 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.d.ts +10 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.d.ts +74 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/index.d.ts +33 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingNumber/index.js +354 -0
- package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.styles.d.ts +25 -0
- package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.styles.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.types.d.ts +57 -0
- package/dist/react-ui/primitives/waapi/SlidingText/SlidingText.types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingText/index.d.ts +26 -0
- package/dist/react-ui/primitives/waapi/SlidingText/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/SlidingText/index.js +105 -0
- package/dist/react-ui/primitives/waapi/core/animationConstants.d.ts +156 -0
- package/dist/react-ui/primitives/waapi/core/animationConstants.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/animationConstants.js +180 -0
- package/dist/react-ui/primitives/waapi/core/index.d.ts +16 -0
- package/dist/react-ui/primitives/waapi/core/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/index.js +5 -0
- package/dist/react-ui/primitives/waapi/core/types.d.ts +143 -0
- package/dist/react-ui/primitives/waapi/core/types.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.d.ts +32 -0
- package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/useAnimationOrchestrator.js +322 -0
- package/dist/react-ui/primitives/waapi/core/useElementRegistry.d.ts +21 -0
- package/dist/react-ui/primitives/waapi/core/useElementRegistry.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/useElementRegistry.js +65 -0
- package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.d.ts +20 -0
- package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/useFLIPAnimation.js +99 -0
- package/dist/react-ui/primitives/waapi/core/usePositionCapture.d.ts +24 -0
- package/dist/react-ui/primitives/waapi/core/usePositionCapture.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/core/usePositionCapture.js +75 -0
- package/dist/react-ui/primitives/waapi/index.d.ts +33 -0
- package/dist/react-ui/primitives/waapi/index.d.ts.map +1 -0
- package/dist/react-ui/primitives/waapi/index.js +18 -0
- package/dist/react-ui/ui/Accordion/index.js +3 -3
- package/dist/react-ui/ui/Button/index.js +8 -8
- package/dist/react-ui/ui/Combobox/index.js +2 -2
- package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts +35 -0
- package/dist/react-ui/ui/DataCard/DataCard.styles.d.ts.map +1 -0
- package/dist/react-ui/ui/DataCard/DataCard.styles.js +114 -0
- package/dist/react-ui/ui/DataCard/DataCard.types.d.ts +135 -0
- package/dist/react-ui/ui/DataCard/DataCard.types.d.ts.map +1 -0
- package/dist/react-ui/ui/DataCard/index.d.ts +129 -0
- package/dist/react-ui/ui/DataCard/index.d.ts.map +1 -0
- package/dist/react-ui/ui/DataCard/index.js +276 -0
- package/dist/react-ui/ui/Menu/index.js +2 -2
- package/dist/react-ui/ui/Switch/index.js +3 -3
- package/dist/react-ui/ui/Tabs/index.js +3 -3
- package/dist/react-ui/ui/TextFlow/TextFlow.styles.d.ts +16 -0
- package/dist/react-ui/ui/TextFlow/TextFlow.styles.d.ts.map +1 -0
- package/dist/react-ui/ui/TextFlow/TextFlow.types.d.ts +101 -0
- package/dist/react-ui/ui/TextFlow/TextFlow.types.d.ts.map +1 -0
- package/dist/react-ui/ui/TextFlow/index.d.ts +26 -0
- package/dist/react-ui/ui/TextFlow/index.d.ts.map +1 -0
- package/dist/react-ui/ui/TextFlow/index.js +187 -0
- package/dist/react-ui/ui/index.d.ts +2 -1
- package/dist/react-ui/ui/index.d.ts.map +1 -1
- package/dist/react-ui/ui/index.js +3 -1
- package/package.json +6 -2
- package/src/react-ui/hooks/Formatting/UseListFormat.ts +134 -0
- package/src/react-ui/hooks/index.ts +3 -0
- package/src/react-ui/primitives/index.ts +3 -0
- package/src/react-ui/primitives/waapi/Morph/Morph.types.ts +106 -0
- package/src/react-ui/primitives/waapi/Morph/MorphContext.tsx +21 -0
- package/src/react-ui/primitives/waapi/Morph/index.tsx +56 -0
- package/src/react-ui/primitives/waapi/Morph/techniques/index.ts +12 -0
- package/src/react-ui/primitives/waapi/Morph/techniques/useCSSGridMorph.ts +88 -0
- package/src/react-ui/primitives/waapi/Morph/techniques/useFLIPClipPath.ts +175 -0
- package/src/react-ui/primitives/waapi/Morph/techniques/useViewTransitions.ts +86 -0
- package/src/react-ui/primitives/waapi/Morph/useMorph.ts +100 -0
- package/src/react-ui/primitives/waapi/Reorder/Reorder.types.ts +177 -0
- package/src/react-ui/primitives/waapi/Reorder/index.tsx +260 -0
- package/src/react-ui/primitives/waapi/Reorder/useReorder.ts +46 -0
- package/src/react-ui/primitives/waapi/Reorder/useReorderPresence.ts +208 -0
- package/src/react-ui/primitives/waapi/Reorder/utils/separatorCoordination.ts +104 -0
- package/src/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.styles.ts +14 -0
- package/src/react-ui/primitives/waapi/SlidingNumber/SlidingNumber.types.ts +84 -0
- package/src/react-ui/primitives/waapi/SlidingNumber/index.tsx +474 -0
- package/src/react-ui/primitives/waapi/SlidingText/SlidingText.styles.ts +32 -0
- package/src/react-ui/primitives/waapi/SlidingText/SlidingText.types.ts +69 -0
- package/src/react-ui/primitives/waapi/SlidingText/index.tsx +140 -0
- package/src/react-ui/primitives/waapi/core/animationConstants.ts +215 -0
- package/src/react-ui/primitives/waapi/core/index.ts +53 -0
- package/src/react-ui/primitives/waapi/core/types.ts +200 -0
- package/src/react-ui/primitives/waapi/core/useAnimationOrchestrator.ts +429 -0
- package/src/react-ui/primitives/waapi/core/useElementRegistry.ts +80 -0
- package/src/react-ui/primitives/waapi/core/useFLIPAnimation.ts +137 -0
- package/src/react-ui/primitives/waapi/core/usePositionCapture.ts +105 -0
- package/src/react-ui/primitives/waapi/index.ts +116 -0
- package/src/react-ui/styles/animations.css +369 -0
- package/src/react-ui/ui/DataCard/DataCard.styles.ts +150 -0
- package/src/react-ui/ui/DataCard/DataCard.types.ts +146 -0
- package/src/react-ui/ui/DataCard/index.tsx +406 -0
- package/src/react-ui/ui/TextFlow/TextFlow.styles.ts +36 -0
- package/src/react-ui/ui/TextFlow/TextFlow.types.ts +118 -0
- package/src/react-ui/ui/TextFlow/index.tsx +276 -0
- package/src/react-ui/ui/index.ts +4 -1
- /package/dist/react-ui/components/MorphingPopover/{morphing-popover.module-CgbYV_HS.css → morphing-popover.module-BycNI8nU.css} +0 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import React, { useEffect, useLayoutEffect, useMemo, useRef } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/react-ui/primitives/waapi/SlidingNumber/index.tsx
|
|
8
|
+
/**
|
|
9
|
+
* SlidingNumber - Animated number transitions with format preservation
|
|
10
|
+
*
|
|
11
|
+
* Uses WAAPI slot-machine-style animation with optional motion blur.
|
|
12
|
+
* Supports Intl.NumberFormat for currency, percentage, and decimal formatting.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // Basic counter
|
|
17
|
+
* <SlidingNumber value={1234} />
|
|
18
|
+
*
|
|
19
|
+
* // Currency with animation
|
|
20
|
+
* <SlidingNumber
|
|
21
|
+
* value={99.99}
|
|
22
|
+
* format={{ style: 'currency', currency: 'USD' }}
|
|
23
|
+
* duration={700}
|
|
24
|
+
* />
|
|
25
|
+
*
|
|
26
|
+
* // Percentage
|
|
27
|
+
* <SlidingNumber
|
|
28
|
+
* value={75.5}
|
|
29
|
+
* format={{ style: 'percent' }}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
const SlidingNumber = ({ value, duration = 700, fontSize = "3rem", fontWeight = "700", color = "#000", digitHeight = 60, stagger = 30, motionBlur = true, format, trend = 0, animationConfig }) => {
|
|
34
|
+
const computedEasing = useMemo(() => {
|
|
35
|
+
if (animationConfig) {
|
|
36
|
+
const { overshoot = 1, stiffness = 1, mass = 1 } = animationConfig;
|
|
37
|
+
const p1 = .34 * stiffness;
|
|
38
|
+
const p2 = 1 + .56 * overshoot;
|
|
39
|
+
const p3 = .64 / mass;
|
|
40
|
+
return `cubic-bezier(${Math.min(p1, 1).toFixed(2)}, ${Math.min(p2, 2).toFixed(2)}, ${Math.min(p3, 1).toFixed(2)}, 1)`;
|
|
41
|
+
}
|
|
42
|
+
return "cubic-bezier(0.34, 1.56, 0.64, 1)";
|
|
43
|
+
}, [animationConfig]);
|
|
44
|
+
const currentChars = useMemo(() => parseNumberToChars(value, format), [value, format]);
|
|
45
|
+
const prevKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
46
|
+
const isFirstRenderRef = useRef(true);
|
|
47
|
+
const enteringKeys = useMemo(() => {
|
|
48
|
+
if (isFirstRenderRef.current) return /* @__PURE__ */ new Set();
|
|
49
|
+
const entering = /* @__PURE__ */ new Set();
|
|
50
|
+
currentChars.forEach((c) => {
|
|
51
|
+
if (!prevKeysRef.current.has(c.key)) entering.add(c.key);
|
|
52
|
+
});
|
|
53
|
+
return entering;
|
|
54
|
+
}, [currentChars]);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
isFirstRenderRef.current = false;
|
|
57
|
+
prevKeysRef.current = new Set(currentChars.map((c) => c.key));
|
|
58
|
+
}, [currentChars]);
|
|
59
|
+
const getDelay = (position) => {
|
|
60
|
+
return (Math.abs(position) - 1) * stagger;
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
className: cn("waapi-sliding-number"),
|
|
64
|
+
style: {
|
|
65
|
+
display: "inline-flex",
|
|
66
|
+
alignItems: "center",
|
|
67
|
+
fontSize,
|
|
68
|
+
fontWeight,
|
|
69
|
+
color,
|
|
70
|
+
fontVariantNumeric: "tabular-nums",
|
|
71
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
72
|
+
overflow: "hidden"
|
|
73
|
+
},
|
|
74
|
+
children: currentChars.map((item) => {
|
|
75
|
+
const isEntering = enteringKeys.has(item.key);
|
|
76
|
+
if (item.isDigit) return /* @__PURE__ */ jsx(Digit, {
|
|
77
|
+
digit: Number.parseInt(item.char),
|
|
78
|
+
duration,
|
|
79
|
+
digitHeight,
|
|
80
|
+
delay: getDelay(item.position),
|
|
81
|
+
motionBlur,
|
|
82
|
+
easing: computedEasing,
|
|
83
|
+
isEntering,
|
|
84
|
+
trend
|
|
85
|
+
}, item.key);
|
|
86
|
+
return /* @__PURE__ */ jsx(Symbol, {
|
|
87
|
+
char: item.char,
|
|
88
|
+
isEntering,
|
|
89
|
+
duration,
|
|
90
|
+
easing: computedEasing
|
|
91
|
+
}, item.key);
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
SlidingNumber.displayName = "SlidingNumber";
|
|
96
|
+
function parseNumberToChars(value, format) {
|
|
97
|
+
const locale = format?.locale || "en-US";
|
|
98
|
+
const options = {
|
|
99
|
+
style: format?.style || "decimal",
|
|
100
|
+
minimumFractionDigits: format?.minimumFractionDigits,
|
|
101
|
+
maximumFractionDigits: format?.maximumFractionDigits,
|
|
102
|
+
useGrouping: format?.useGrouping ?? true
|
|
103
|
+
};
|
|
104
|
+
if (format?.style === "currency" && format?.currency) options.currency = format.currency;
|
|
105
|
+
const parts = new Intl.NumberFormat(locale, options).formatToParts(value);
|
|
106
|
+
const result = [];
|
|
107
|
+
let integerDigitCount = 0;
|
|
108
|
+
parts.forEach((part) => {
|
|
109
|
+
if (part.type === "integer") integerDigitCount += part.value.length;
|
|
110
|
+
});
|
|
111
|
+
let integerIndex = integerDigitCount;
|
|
112
|
+
let fractionIndex = 0;
|
|
113
|
+
let groupCount = 0;
|
|
114
|
+
parts.forEach((part, partIdx) => {
|
|
115
|
+
if (part.type === "integer") for (const char of part.value) {
|
|
116
|
+
result.push({
|
|
117
|
+
char,
|
|
118
|
+
key: `int-${integerIndex}`,
|
|
119
|
+
isDigit: true,
|
|
120
|
+
position: integerIndex
|
|
121
|
+
});
|
|
122
|
+
integerIndex--;
|
|
123
|
+
}
|
|
124
|
+
else if (part.type === "fraction") for (const char of part.value) {
|
|
125
|
+
fractionIndex++;
|
|
126
|
+
result.push({
|
|
127
|
+
char,
|
|
128
|
+
key: `frac-${fractionIndex}`,
|
|
129
|
+
isDigit: true,
|
|
130
|
+
position: -fractionIndex
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else if (part.type === "decimal") result.push({
|
|
134
|
+
char: part.value,
|
|
135
|
+
key: "decimal",
|
|
136
|
+
isDigit: false,
|
|
137
|
+
position: 0
|
|
138
|
+
});
|
|
139
|
+
else if (part.type === "group") {
|
|
140
|
+
groupCount++;
|
|
141
|
+
result.push({
|
|
142
|
+
char: part.value,
|
|
143
|
+
key: `group-${groupCount}`,
|
|
144
|
+
isDigit: false,
|
|
145
|
+
position: 0
|
|
146
|
+
});
|
|
147
|
+
} else if (part.type === "currency") result.push({
|
|
148
|
+
char: part.value,
|
|
149
|
+
key: `currency-${partIdx}`,
|
|
150
|
+
isDigit: false,
|
|
151
|
+
position: 0
|
|
152
|
+
});
|
|
153
|
+
else if (part.type === "percentSign") result.push({
|
|
154
|
+
char: part.value,
|
|
155
|
+
key: "percent",
|
|
156
|
+
isDigit: false,
|
|
157
|
+
position: 0
|
|
158
|
+
});
|
|
159
|
+
else result.push({
|
|
160
|
+
char: part.value,
|
|
161
|
+
key: `symbol-${partIdx}`,
|
|
162
|
+
isDigit: false,
|
|
163
|
+
position: 0
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
function Symbol({ char, isEntering, duration, easing }) {
|
|
169
|
+
const ref = useRef(null);
|
|
170
|
+
const hasAnimatedRef = useRef(false);
|
|
171
|
+
useLayoutEffect(() => {
|
|
172
|
+
if (!ref.current || !isEntering || hasAnimatedRef.current) return;
|
|
173
|
+
hasAnimatedRef.current = true;
|
|
174
|
+
ref.current.animate([{
|
|
175
|
+
opacity: 0,
|
|
176
|
+
transform: "scale(0.8)"
|
|
177
|
+
}, {
|
|
178
|
+
opacity: 1,
|
|
179
|
+
transform: "scale(1)"
|
|
180
|
+
}], {
|
|
181
|
+
duration: duration * .3,
|
|
182
|
+
easing,
|
|
183
|
+
fill: "forwards"
|
|
184
|
+
});
|
|
185
|
+
}, [
|
|
186
|
+
isEntering,
|
|
187
|
+
duration,
|
|
188
|
+
easing
|
|
189
|
+
]);
|
|
190
|
+
return /* @__PURE__ */ jsx("span", {
|
|
191
|
+
ref,
|
|
192
|
+
style: {
|
|
193
|
+
display: "inline-block",
|
|
194
|
+
whiteSpace: "pre",
|
|
195
|
+
opacity: isEntering ? 0 : 1
|
|
196
|
+
},
|
|
197
|
+
children: char
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function Digit({ digit, duration, digitHeight, delay, motionBlur, easing, isEntering, trend }) {
|
|
201
|
+
const columnRef = useRef(null);
|
|
202
|
+
const containerRef = useRef(null);
|
|
203
|
+
const animationRef = useRef(null);
|
|
204
|
+
const filterId = useRef(`blur-${Math.random().toString(36).slice(2, 9)}`).current;
|
|
205
|
+
const currentOffsetRef = useRef(null);
|
|
206
|
+
const targetDigitRef = useRef(digit);
|
|
207
|
+
const hasInitializedRef = useRef(false);
|
|
208
|
+
const sequence = useMemo(() => {
|
|
209
|
+
const seq = [];
|
|
210
|
+
for (let cycle = -1; cycle <= 1; cycle++) for (let d = 0; d <= 9; d++) seq.push(d);
|
|
211
|
+
return seq;
|
|
212
|
+
}, []);
|
|
213
|
+
useLayoutEffect(() => {
|
|
214
|
+
if (!hasInitializedRef.current) {
|
|
215
|
+
hasInitializedRef.current = true;
|
|
216
|
+
const offset = -(digit + 10) * digitHeight;
|
|
217
|
+
currentOffsetRef.current = offset;
|
|
218
|
+
targetDigitRef.current = digit;
|
|
219
|
+
if (columnRef.current) columnRef.current.style.transform = `translateY(${offset}px)`;
|
|
220
|
+
}
|
|
221
|
+
}, [digit, digitHeight]);
|
|
222
|
+
useLayoutEffect(() => {
|
|
223
|
+
if (!containerRef.current || !isEntering) return;
|
|
224
|
+
containerRef.current.animate([{
|
|
225
|
+
opacity: 0,
|
|
226
|
+
transform: "scale(0.5) translateY(-20px)"
|
|
227
|
+
}, {
|
|
228
|
+
opacity: 1,
|
|
229
|
+
transform: "scale(1) translateY(0)"
|
|
230
|
+
}], {
|
|
231
|
+
duration: duration * .4,
|
|
232
|
+
easing,
|
|
233
|
+
fill: "forwards"
|
|
234
|
+
});
|
|
235
|
+
}, [
|
|
236
|
+
isEntering,
|
|
237
|
+
duration,
|
|
238
|
+
easing
|
|
239
|
+
]);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
targetDigitRef.current = digit;
|
|
242
|
+
if (!hasInitializedRef.current) return;
|
|
243
|
+
if (digit === (currentOffsetRef.current !== null ? ((Math.round(-currentOffsetRef.current / digitHeight) - 10) % 10 + 10) % 10 : digit) && currentOffsetRef.current !== null) return;
|
|
244
|
+
animateToDigit(digit);
|
|
245
|
+
}, [digit, digitHeight]);
|
|
246
|
+
const animateToDigit = (target) => {
|
|
247
|
+
if (!columnRef.current || !containerRef.current) return;
|
|
248
|
+
if (animationRef.current) {
|
|
249
|
+
const computedStyle = getComputedStyle(columnRef.current);
|
|
250
|
+
currentOffsetRef.current = new DOMMatrix(computedStyle.transform).m42;
|
|
251
|
+
animationRef.current.cancel();
|
|
252
|
+
animationRef.current = null;
|
|
253
|
+
columnRef.current.style.transform = `translateY(${currentOffsetRef.current}px)`;
|
|
254
|
+
}
|
|
255
|
+
const rawIndex = currentOffsetRef.current !== null ? -currentOffsetRef.current / digitHeight : target + 10;
|
|
256
|
+
const from = ((Math.round(rawIndex) - 10) % 10 + 10) % 10;
|
|
257
|
+
if (target === from && currentOffsetRef.current !== null) {
|
|
258
|
+
const normalizedOffset = -(target + 10) * digitHeight;
|
|
259
|
+
if (columnRef.current) {
|
|
260
|
+
const currentAnim = animationRef.current;
|
|
261
|
+
if (currentAnim) currentAnim.cancel();
|
|
262
|
+
columnRef.current.style.transform = `translateY(${normalizedOffset}px)`;
|
|
263
|
+
}
|
|
264
|
+
currentOffsetRef.current = normalizedOffset;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let diff;
|
|
268
|
+
if (trend === 1) diff = target >= from ? target - from : 10 - from + target;
|
|
269
|
+
else if (trend === -1) diff = target <= from ? target - from : target - from - 10;
|
|
270
|
+
else {
|
|
271
|
+
diff = target - from;
|
|
272
|
+
if (diff > 5) diff -= 10;
|
|
273
|
+
else if (diff < -5) diff += 10;
|
|
274
|
+
}
|
|
275
|
+
const startOffset = currentOffsetRef.current ?? -(from + 10) * digitHeight;
|
|
276
|
+
const endOffset = startOffset - diff * digitHeight;
|
|
277
|
+
const blurEl = document.getElementById(filterId)?.querySelector("feGaussianBlur");
|
|
278
|
+
if (motionBlur && blurEl) {
|
|
279
|
+
const intensity = Math.min(Math.abs(diff) * 1.2, 6);
|
|
280
|
+
blurEl.setAttribute("stdDeviation", `0,${intensity}`);
|
|
281
|
+
containerRef.current.style.filter = `url(#${filterId})`;
|
|
282
|
+
}
|
|
283
|
+
const anim = columnRef.current.animate([{ transform: `translateY(${startOffset}px)` }, { transform: `translateY(${endOffset}px)` }], {
|
|
284
|
+
duration,
|
|
285
|
+
delay,
|
|
286
|
+
easing,
|
|
287
|
+
fill: "forwards"
|
|
288
|
+
});
|
|
289
|
+
animationRef.current = anim;
|
|
290
|
+
anim.onfinish = () => {
|
|
291
|
+
const normalizedOffset = -(target + 10) * digitHeight;
|
|
292
|
+
if (columnRef.current) {
|
|
293
|
+
anim.cancel();
|
|
294
|
+
columnRef.current.style.transform = `translateY(${normalizedOffset}px)`;
|
|
295
|
+
}
|
|
296
|
+
currentOffsetRef.current = normalizedOffset;
|
|
297
|
+
if (containerRef.current) containerRef.current.style.filter = "none";
|
|
298
|
+
if (blurEl) blurEl.setAttribute("stdDeviation", "0,0");
|
|
299
|
+
animationRef.current = null;
|
|
300
|
+
if (targetDigitRef.current !== target) requestAnimationFrame(() => {
|
|
301
|
+
animateToDigit(targetDigitRef.current);
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
anim.oncancel = () => {
|
|
305
|
+
animationRef.current = null;
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("svg", {
|
|
309
|
+
style: {
|
|
310
|
+
position: "absolute",
|
|
311
|
+
width: 0,
|
|
312
|
+
height: 0
|
|
313
|
+
},
|
|
314
|
+
"aria-hidden": "true",
|
|
315
|
+
children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("filter", {
|
|
316
|
+
id: filterId,
|
|
317
|
+
children: /* @__PURE__ */ jsx("feGaussianBlur", {
|
|
318
|
+
in: "SourceGraphic",
|
|
319
|
+
stdDeviation: "0,0"
|
|
320
|
+
})
|
|
321
|
+
}) })
|
|
322
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
323
|
+
ref: containerRef,
|
|
324
|
+
style: {
|
|
325
|
+
position: "relative",
|
|
326
|
+
height: `${digitHeight}px`,
|
|
327
|
+
overflow: "hidden",
|
|
328
|
+
width: "0.65em",
|
|
329
|
+
textAlign: "center",
|
|
330
|
+
opacity: isEntering ? 0 : 1
|
|
331
|
+
},
|
|
332
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
333
|
+
ref: columnRef,
|
|
334
|
+
style: {
|
|
335
|
+
position: "absolute",
|
|
336
|
+
left: 0,
|
|
337
|
+
right: 0,
|
|
338
|
+
willChange: "transform"
|
|
339
|
+
},
|
|
340
|
+
children: sequence.map((d, i) => /* @__PURE__ */ jsx("div", {
|
|
341
|
+
style: {
|
|
342
|
+
height: `${digitHeight}px`,
|
|
343
|
+
display: "flex",
|
|
344
|
+
alignItems: "center",
|
|
345
|
+
justifyContent: "center"
|
|
346
|
+
},
|
|
347
|
+
children: d
|
|
348
|
+
}, i))
|
|
349
|
+
})
|
|
350
|
+
})] });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
//#endregion
|
|
354
|
+
export { SlidingNumber };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StyleSlots } from '../../../../core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Style slots for SlidingText component
|
|
4
|
+
*/
|
|
5
|
+
export type SlidingTextSlot = 'root' | 'content' | 'token';
|
|
6
|
+
/**
|
|
7
|
+
* Base styles for SlidingText component
|
|
8
|
+
*/
|
|
9
|
+
export declare const slidingTextBaseStyles: StyleSlots<SlidingTextSlot>;
|
|
10
|
+
/**
|
|
11
|
+
* CSS classes for animation states
|
|
12
|
+
*/
|
|
13
|
+
export declare const slidingTextAnimationClasses: {
|
|
14
|
+
readonly enterFrom: "enter-from";
|
|
15
|
+
readonly enterTo: "enter-to";
|
|
16
|
+
readonly exitActive: "exit-active";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* CSS classes for direction variants
|
|
20
|
+
*/
|
|
21
|
+
export declare const slidingTextDirectionClasses: {
|
|
22
|
+
readonly vertical: "waapi-direction-vertical";
|
|
23
|
+
readonly horizontal: "waapi-direction-horizontal";
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=SlidingText.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlidingText.styles.d.ts","sourceRoot":"","sources":["../../../../../src/react-ui/primitives/waapi/SlidingText/SlidingText.styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,UAAU,CAAC,eAAe,CAI7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B;;;;CAI9B,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,2BAA2B;;;CAG9B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props for SlidingText component
|
|
3
|
+
*
|
|
4
|
+
* Character or word-by-word text animations with entrance/exit transitions and blur effects.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* // Character animation (default)
|
|
9
|
+
* <SlidingText text="Hello World" mode="character" />
|
|
10
|
+
*
|
|
11
|
+
* // Word animation
|
|
12
|
+
* <SlidingText text="React Components" mode="word" />
|
|
13
|
+
*
|
|
14
|
+
* // Horizontal direction
|
|
15
|
+
* <SlidingText
|
|
16
|
+
* text="Slide In"
|
|
17
|
+
* direction="horizontal"
|
|
18
|
+
* blur={false}
|
|
19
|
+
* />
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface ISlidingTextProps {
|
|
23
|
+
/** The text content to animate */
|
|
24
|
+
text: string;
|
|
25
|
+
/**
|
|
26
|
+
* Animation mode
|
|
27
|
+
* - 'character': Each character animates individually
|
|
28
|
+
* - 'word': The text animates as a unit
|
|
29
|
+
* - 'none': No animation
|
|
30
|
+
*/
|
|
31
|
+
mode?: 'word' | 'character' | 'none';
|
|
32
|
+
/** Animation direction */
|
|
33
|
+
direction?: 'vertical' | 'horizontal';
|
|
34
|
+
/** Delay between each character/word in milliseconds */
|
|
35
|
+
staggerDelay?: number;
|
|
36
|
+
/** Animation duration in milliseconds */
|
|
37
|
+
duration?: number;
|
|
38
|
+
/** CSS easing function */
|
|
39
|
+
easing?: string;
|
|
40
|
+
/** Enable blur effect during animation */
|
|
41
|
+
blur?: boolean;
|
|
42
|
+
/** Animate width changes during animation */
|
|
43
|
+
widthAnimation?: boolean;
|
|
44
|
+
/** Initial animation state */
|
|
45
|
+
initial?: 'initial' | false;
|
|
46
|
+
/** Trigger animation state */
|
|
47
|
+
animate?: 'animate';
|
|
48
|
+
/** Exit animation state */
|
|
49
|
+
exit?: 'exit';
|
|
50
|
+
/** Callback when animation completes */
|
|
51
|
+
onAnimationComplete?: () => void;
|
|
52
|
+
/** Additional CSS class name */
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Additional inline styles */
|
|
55
|
+
style?: React.CSSProperties;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=SlidingText.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlidingText.types.d.ts","sourceRoot":"","sources":["../../../../../src/react-ui/primitives/waapi/SlidingText/SlidingText.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAErC,0BAA0B;IAC1B,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IAEtC,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,6CAA6C;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAE5B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,wCAAwC;IACxC,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ISlidingTextProps } from './SlidingText.types';
|
|
3
|
+
/**
|
|
4
|
+
* SlidingText - Character or word-by-word text animations
|
|
5
|
+
*
|
|
6
|
+
* Animated text component with entrance/exit transitions and blur effects.
|
|
7
|
+
* Supports character-level or word-level animation modes.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* // Character animation (default)
|
|
12
|
+
* <SlidingText text="Hello World" mode="character" />
|
|
13
|
+
*
|
|
14
|
+
* // Word animation
|
|
15
|
+
* <SlidingText text="React Components" mode="word" />
|
|
16
|
+
*
|
|
17
|
+
* // Exit animation
|
|
18
|
+
* <SlidingText text="Goodbye" exit="exit" />
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const SlidingText: {
|
|
22
|
+
({ text, mode, direction, staggerDelay, duration, easing, blur, widthAnimation, initial, exit, className, style, }: ISlidingTextProps): React.ReactElement;
|
|
23
|
+
displayName: string;
|
|
24
|
+
};
|
|
25
|
+
export type { ISlidingTextProps };
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/react-ui/primitives/waapi/SlidingText/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,WAAW;wHAarB,iBAAiB,GAAG,KAAK,CAAC,YAAY;;CAkGxC,CAAC;AAIF,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/react-ui/primitives/waapi/SlidingText/index.tsx
|
|
8
|
+
/**
|
|
9
|
+
* SlidingText - Character or word-by-word text animations
|
|
10
|
+
*
|
|
11
|
+
* Animated text component with entrance/exit transitions and blur effects.
|
|
12
|
+
* Supports character-level or word-level animation modes.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // Character animation (default)
|
|
17
|
+
* <SlidingText text="Hello World" mode="character" />
|
|
18
|
+
*
|
|
19
|
+
* // Word animation
|
|
20
|
+
* <SlidingText text="React Components" mode="word" />
|
|
21
|
+
*
|
|
22
|
+
* // Exit animation
|
|
23
|
+
* <SlidingText text="Goodbye" exit="exit" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
const SlidingText = ({ text, mode = "word", direction = "vertical", staggerDelay = 15, duration = 200, easing = "cubic-bezier(0.33, 1, 0.68, 1)", blur = true, widthAnimation = false, initial = "initial", exit, className = "", style }) => {
|
|
27
|
+
const containerRef = useRef(null);
|
|
28
|
+
const contentRef = useRef(null);
|
|
29
|
+
const hasTriggeredEnterRef = useRef(false);
|
|
30
|
+
const [showAnimate, setShowAnimate] = useState(initial !== "initial");
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (initial === "initial" && !hasTriggeredEnterRef.current) {
|
|
33
|
+
hasTriggeredEnterRef.current = true;
|
|
34
|
+
requestAnimationFrame(() => {
|
|
35
|
+
setShowAnimate(true);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}, [initial]);
|
|
39
|
+
const visualState = exit === "exit" ? "exit" : showAnimate ? "animate" : "initial";
|
|
40
|
+
const elements = mode === "character" ? text.split("") : [text];
|
|
41
|
+
useLayoutEffect(() => {
|
|
42
|
+
if (!widthAnimation || !containerRef.current || !contentRef.current) return;
|
|
43
|
+
const container = containerRef.current;
|
|
44
|
+
const content = contentRef.current;
|
|
45
|
+
if (visualState === "initial") container.style.width = "0px";
|
|
46
|
+
else if (visualState === "animate") if (CSS.supports("interpolate-size", "allow-keywords")) {
|
|
47
|
+
container.style.width = "auto";
|
|
48
|
+
container.style.transition = `width ${duration}ms ${easing}`;
|
|
49
|
+
} else {
|
|
50
|
+
const targetWidth = content.scrollWidth;
|
|
51
|
+
container.style.width = `${targetWidth}px`;
|
|
52
|
+
container.style.transition = `width ${duration}ms ${easing}`;
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
container.style.width = "auto";
|
|
55
|
+
}, duration);
|
|
56
|
+
return () => clearTimeout(timer);
|
|
57
|
+
}
|
|
58
|
+
else if (visualState === "exit") {
|
|
59
|
+
const currentWidth = container.getBoundingClientRect().width;
|
|
60
|
+
container.style.width = `${currentWidth}px`;
|
|
61
|
+
container.getBoundingClientRect();
|
|
62
|
+
container.style.width = "0px";
|
|
63
|
+
container.style.transition = `width 180ms cubic-bezier(0.32, 0, 0.67, 0)`;
|
|
64
|
+
}
|
|
65
|
+
}, [
|
|
66
|
+
visualState,
|
|
67
|
+
widthAnimation,
|
|
68
|
+
duration,
|
|
69
|
+
easing,
|
|
70
|
+
text
|
|
71
|
+
]);
|
|
72
|
+
const getTransitionStyle = (index) => {
|
|
73
|
+
const delay = index * staggerDelay;
|
|
74
|
+
const isExit = visualState === "exit";
|
|
75
|
+
const currentDuration = isExit ? 180 : duration;
|
|
76
|
+
const currentEasing = isExit ? "cubic-bezier(0.32, 0, 0.67, 0)" : easing;
|
|
77
|
+
return {
|
|
78
|
+
transition: `
|
|
79
|
+
opacity ${currentDuration}ms ${currentEasing} ${delay}ms,
|
|
80
|
+
transform ${currentDuration}ms ${currentEasing} ${delay}ms,
|
|
81
|
+
filter ${currentDuration}ms ${currentEasing} ${delay}ms
|
|
82
|
+
`,
|
|
83
|
+
"--blur-amount": blur ? "4px" : "0px",
|
|
84
|
+
"--offset": direction === "vertical" ? "8px" : "16px"
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
return /* @__PURE__ */ jsx("div", {
|
|
88
|
+
ref: containerRef,
|
|
89
|
+
className: cn("waapi-sliding-text-container", className),
|
|
90
|
+
style,
|
|
91
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
92
|
+
ref: contentRef,
|
|
93
|
+
className: cn("waapi-sliding-text-content", `waapi-direction-${direction}`),
|
|
94
|
+
children: elements.map((char, index) => /* @__PURE__ */ jsx("span", {
|
|
95
|
+
className: cn("waapi-sliding-text-token", visualState === "initial" && "enter-from", visualState === "animate" && "enter-to", visualState === "exit" && "exit-active"),
|
|
96
|
+
style: getTransitionStyle(index),
|
|
97
|
+
children: char
|
|
98
|
+
}, index))
|
|
99
|
+
})
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
SlidingText.displayName = "SlidingText";
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { SlidingText };
|