@seedgrid/fe-components 0.2.9 → 2026.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/buttons/SgFloatActionButton.d.ts.map +1 -1
- package/dist/buttons/SgFloatActionButton.js +168 -38
- package/dist/commons/SgAvatar.d.ts +66 -0
- package/dist/commons/SgAvatar.d.ts.map +1 -0
- package/dist/commons/SgAvatar.js +136 -0
- package/dist/commons/SgSkeleton.d.ts +16 -0
- package/dist/commons/SgSkeleton.d.ts.map +1 -0
- package/dist/commons/SgSkeleton.js +58 -0
- package/dist/commons/SgToaster.d.ts +9 -0
- package/dist/commons/SgToaster.d.ts.map +1 -1
- package/dist/commons/SgToaster.js +86 -17
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts +39 -0
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts.map +1 -0
- package/dist/digits/discard-digit/SgDiscardDigit.js +303 -0
- package/dist/digits/discard-digit/index.d.ts +3 -0
- package/dist/digits/discard-digit/index.d.ts.map +1 -0
- package/dist/digits/discard-digit/index.js +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts +27 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts.map +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.js +85 -0
- package/dist/digits/fade-digit/index.d.ts +3 -0
- package/dist/digits/fade-digit/index.d.ts.map +1 -0
- package/dist/digits/fade-digit/index.js +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts +27 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts.map +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.js +70 -0
- package/dist/digits/flip-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts +32 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.js +86 -0
- package/dist/digits/matrix-digit/index.d.ts +3 -0
- package/dist/digits/matrix-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/index.js +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts +37 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts.map +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.js +59 -0
- package/dist/digits/neon-digit/index.d.ts +3 -0
- package/dist/digits/neon-digit/index.d.ts.map +1 -0
- package/dist/digits/neon-digit/index.js +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts +37 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.js +47 -0
- package/dist/digits/roller3d-digit/index.d.ts +3 -0
- package/dist/digits/roller3d-digit/index.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/index.js +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -1
- package/dist/environment/SgEnvironmentProvider.js +51 -12
- package/dist/gadgets/clock/SgClock.d.ts +3 -1
- package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
- package/dist/gadgets/clock/SgClock.js +111 -180
- package/dist/gadgets/clock/SgTimeProvider.d.ts +1 -0
- package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -1
- package/dist/gadgets/clock/SgTimeProvider.js +11 -4
- package/dist/gadgets/gauge/SgLinearGauge.d.ts +59 -0
- package/dist/gadgets/gauge/SgLinearGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgLinearGauge.js +258 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts +73 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgRadialGauge.js +311 -0
- package/dist/gadgets/gauge/index.d.ts +5 -0
- package/dist/gadgets/gauge/index.d.ts.map +1 -0
- package/dist/gadgets/gauge/index.js +2 -0
- package/dist/gadgets/qr-code/SgQRCode.d.ts +25 -0
- package/dist/gadgets/qr-code/SgQRCode.d.ts.map +1 -0
- package/dist/gadgets/qr-code/SgQRCode.js +75 -0
- package/dist/gadgets/qr-code/index.d.ts +3 -0
- package/dist/gadgets/qr-code/index.d.ts.map +1 -0
- package/dist/gadgets/qr-code/index.js +1 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts +91 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts.map +1 -0
- package/dist/gadgets/string-animator/SgStringAnimator.js +145 -0
- package/dist/gadgets/string-animator/index.d.ts +3 -0
- package/dist/gadgets/string-animator/index.d.ts.map +1 -0
- package/dist/gadgets/string-animator/index.js +1 -0
- package/dist/i18n/en-US.json +9 -1
- package/dist/i18n/es.json +55 -47
- package/dist/i18n/index.d.ts +32 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/pt-BR.json +9 -1
- package/dist/i18n/pt-PT.json +9 -1
- package/dist/index.d.ts +53 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -1
- package/dist/inputs/SgAutocomplete.js +21 -5
- package/dist/inputs/SgCombobox.d.ts +26 -0
- package/dist/inputs/SgCombobox.d.ts.map +1 -0
- package/dist/inputs/SgCombobox.js +354 -0
- package/dist/inputs/SgInputOTP.d.ts.map +1 -1
- package/dist/inputs/SgInputOTP.js +9 -2
- package/dist/inputs/SgRadioGroup.d.ts +37 -0
- package/dist/inputs/SgRadioGroup.d.ts.map +1 -0
- package/dist/inputs/SgRadioGroup.js +139 -0
- package/dist/inputs/SgRating.d.ts +55 -0
- package/dist/inputs/SgRating.d.ts.map +1 -0
- package/dist/inputs/SgRating.js +135 -0
- package/dist/inputs/SgSlider.d.ts +20 -0
- package/dist/inputs/SgSlider.d.ts.map +1 -0
- package/dist/inputs/SgSlider.js +40 -0
- package/dist/inputs/SgStepperInput.d.ts +22 -0
- package/dist/inputs/SgStepperInput.d.ts.map +1 -0
- package/dist/inputs/SgStepperInput.js +51 -0
- package/dist/inputs/SgTextEditor.d.ts +1 -0
- package/dist/inputs/SgTextEditor.d.ts.map +1 -1
- package/dist/inputs/SgTextEditor.js +19 -3
- package/dist/inputs/SgToggleSwitch.d.ts +36 -0
- package/dist/inputs/SgToggleSwitch.d.ts.map +1 -0
- package/dist/inputs/SgToggleSwitch.js +174 -0
- package/dist/layout/SgAccordion.d.ts +39 -0
- package/dist/layout/SgAccordion.d.ts.map +1 -0
- package/dist/layout/SgAccordion.js +116 -0
- package/dist/layout/SgBreadcrumb.d.ts +33 -0
- package/dist/layout/SgBreadcrumb.d.ts.map +1 -0
- package/dist/layout/SgBreadcrumb.js +121 -0
- package/dist/layout/SgCarousel.d.ts +43 -0
- package/dist/layout/SgCarousel.d.ts.map +1 -0
- package/dist/layout/SgCarousel.js +166 -0
- package/dist/layout/SgDockLayout.d.ts +14 -0
- package/dist/layout/SgDockLayout.d.ts.map +1 -1
- package/dist/layout/SgDockLayout.js +145 -13
- package/dist/layout/SgDockScreen.d.ts +15 -0
- package/dist/layout/SgDockScreen.d.ts.map +1 -0
- package/dist/layout/SgDockScreen.js +13 -0
- package/dist/layout/SgDockZone.d.ts.map +1 -1
- package/dist/layout/SgDockZone.js +36 -2
- package/dist/layout/SgExpandablePanel.d.ts +50 -0
- package/dist/layout/SgExpandablePanel.d.ts.map +1 -0
- package/dist/layout/SgExpandablePanel.js +302 -0
- package/dist/layout/SgMainPanel.d.ts.map +1 -1
- package/dist/layout/SgMainPanel.js +36 -14
- package/dist/layout/SgMenu.d.ts +91 -0
- package/dist/layout/SgMenu.d.ts.map +1 -0
- package/dist/layout/SgMenu.js +939 -0
- package/dist/layout/SgPageControl.d.ts +49 -0
- package/dist/layout/SgPageControl.d.ts.map +1 -0
- package/dist/layout/SgPageControl.js +152 -0
- package/dist/layout/SgPanel.d.ts.map +1 -1
- package/dist/layout/SgPanel.js +10 -1
- package/dist/layout/SgScreen.d.ts +2 -0
- package/dist/layout/SgScreen.d.ts.map +1 -1
- package/dist/layout/SgScreen.js +4 -2
- package/dist/layout/SgToolBar.d.ts +9 -3
- package/dist/layout/SgToolBar.d.ts.map +1 -1
- package/dist/layout/SgToolBar.js +461 -55
- package/dist/menus/SgDockMenu.d.ts +62 -0
- package/dist/menus/SgDockMenu.d.ts.map +1 -0
- package/dist/menus/SgDockMenu.js +480 -0
- package/dist/others/SgPlayground.js +73 -73
- package/package.json +72 -57
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +0 -23
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +0 -1
- package/dist/gadgets/flip-digit/SgFlipDigit.js +0 -118
- package/dist/gadgets/flip-digit/index.d.ts.map +0 -1
- /package/dist/{gadgets → digits}/flip-digit/index.d.ts +0 -0
- /package/dist/{gadgets → digits}/flip-digit/index.js +0 -0
|
@@ -13,24 +13,86 @@ const POSITION_CLASS = {
|
|
|
13
13
|
"bottom-left": "bottom-4 left-4 items-start",
|
|
14
14
|
"bottom-center": "bottom-4 left-1/2 -translate-x-1/2 items-center"
|
|
15
15
|
};
|
|
16
|
-
const
|
|
17
|
-
default:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const RICH_TYPE_COLORS = {
|
|
17
|
+
default: {
|
|
18
|
+
border: "hsl(var(--border))",
|
|
19
|
+
bg: "hsl(var(--background))",
|
|
20
|
+
fg: "hsl(var(--foreground))"
|
|
21
|
+
},
|
|
22
|
+
success: {
|
|
23
|
+
border: "#22c55e",
|
|
24
|
+
bg: "#16a34a",
|
|
25
|
+
fg: "#ffffff"
|
|
26
|
+
},
|
|
27
|
+
info: {
|
|
28
|
+
border: "#0ea5e9",
|
|
29
|
+
bg: "#0284c7",
|
|
30
|
+
fg: "#ffffff"
|
|
31
|
+
},
|
|
32
|
+
warning: {
|
|
33
|
+
border: "#f59e0b",
|
|
34
|
+
bg: "#f59e0b",
|
|
35
|
+
fg: "#000000"
|
|
36
|
+
},
|
|
37
|
+
error: {
|
|
38
|
+
border: "#ef4444",
|
|
39
|
+
bg: "#dc2626",
|
|
40
|
+
fg: "#ffffff"
|
|
41
|
+
},
|
|
42
|
+
loading: {
|
|
43
|
+
border: "#3b82f6",
|
|
44
|
+
bg: "#2563eb",
|
|
45
|
+
fg: "#ffffff"
|
|
46
|
+
}
|
|
23
47
|
};
|
|
24
|
-
const
|
|
25
|
-
default:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
const SOFT_TYPE_COLORS = {
|
|
49
|
+
default: {
|
|
50
|
+
border: "hsl(var(--border))",
|
|
51
|
+
bg: "hsl(var(--background))",
|
|
52
|
+
fg: "hsl(var(--foreground))"
|
|
53
|
+
},
|
|
54
|
+
success: {
|
|
55
|
+
border: "#86efac",
|
|
56
|
+
bg: "#f0fdf4",
|
|
57
|
+
fg: "#14532d"
|
|
58
|
+
},
|
|
59
|
+
info: {
|
|
60
|
+
border: "#7dd3fc",
|
|
61
|
+
bg: "#f0f9ff",
|
|
62
|
+
fg: "#0c4a6e"
|
|
63
|
+
},
|
|
64
|
+
warning: {
|
|
65
|
+
border: "#fcd34d",
|
|
66
|
+
bg: "#fffbeb",
|
|
67
|
+
fg: "#78350f"
|
|
68
|
+
},
|
|
69
|
+
error: {
|
|
70
|
+
border: "#fca5a5",
|
|
71
|
+
bg: "#fef2f2",
|
|
72
|
+
fg: "#7f1d1d"
|
|
73
|
+
},
|
|
74
|
+
loading: {
|
|
75
|
+
border: "#93c5fd",
|
|
76
|
+
bg: "#eff6ff",
|
|
77
|
+
fg: "#1e3a8a"
|
|
78
|
+
}
|
|
31
79
|
};
|
|
80
|
+
function clampTransparency(value) {
|
|
81
|
+
if (value === undefined || Number.isNaN(value))
|
|
82
|
+
return 0;
|
|
83
|
+
return Math.max(0, Math.min(100, value));
|
|
84
|
+
}
|
|
85
|
+
function resolveToastColors(type, richColors, customColors) {
|
|
86
|
+
const base = richColors ? RICH_TYPE_COLORS[type] : SOFT_TYPE_COLORS[type];
|
|
87
|
+
const custom = customColors?.[type];
|
|
88
|
+
return {
|
|
89
|
+
border: custom?.border ?? base.border,
|
|
90
|
+
bg: custom?.bg ?? base.bg,
|
|
91
|
+
fg: custom?.fg ?? base.fg
|
|
92
|
+
};
|
|
93
|
+
}
|
|
32
94
|
export function SgToaster(props) {
|
|
33
|
-
const { position = "top-right", duration = 4000, visibleToasts = 6, closeButton = true, richColors = true, className, style, ...rest } = props;
|
|
95
|
+
const { position = "top-right", duration = 4000, visibleToasts = 6, closeButton = true, richColors = true, transparency = 0, customColors, className, style, ...rest } = props;
|
|
34
96
|
const [toasts, setToasts] = React.useState([]);
|
|
35
97
|
const timersRef = React.useRef({});
|
|
36
98
|
React.useEffect(() => subscribeSgToasts(setToasts), []);
|
|
@@ -74,10 +136,17 @@ export function SgToaster(props) {
|
|
|
74
136
|
timersRef.current = {};
|
|
75
137
|
};
|
|
76
138
|
}, []);
|
|
139
|
+
const toastOpacity = 1 - clampTransparency(transparency) / 100;
|
|
77
140
|
return (_jsx("div", { className: cn("pointer-events-none fixed z-[1100] flex max-h-screen w-full flex-col gap-2 p-4 sm:w-auto", POSITION_CLASS[position], className), style: style, ...rest, children: visible.map((toast) => {
|
|
78
|
-
const
|
|
141
|
+
const typeColors = resolveToastColors(toast.type, richColors, customColors);
|
|
79
142
|
const canClose = toast.closeButton ?? closeButton;
|
|
80
|
-
return (_jsxs("div", { className: cn("pointer-events-auto flex min-w-[260px] max-w-[420px] items-start gap-3 rounded-md border px-3 py-2 shadow-lg",
|
|
143
|
+
return (_jsxs("div", { className: cn("pointer-events-auto flex min-w-[260px] max-w-[420px] items-start gap-3 rounded-md border px-3 py-2 shadow-lg", toast.className), style: {
|
|
144
|
+
borderColor: typeColors.border,
|
|
145
|
+
backgroundColor: typeColors.bg,
|
|
146
|
+
color: typeColors.fg,
|
|
147
|
+
opacity: toastOpacity,
|
|
148
|
+
...toast.style
|
|
149
|
+
}, role: "status", "aria-live": "polite", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [toast.title ? _jsx("div", { className: "text-sm font-semibold", children: toast.title }) : null, toast.description ? _jsx("div", { className: "mt-0.5 text-xs opacity-90", children: toast.description }) : null] }), toast.action ? (_jsx("button", { type: "button", className: "rounded border border-current/30 px-2 py-1 text-xs font-medium opacity-95 hover:opacity-100", onClick: () => {
|
|
81
150
|
toast.action?.onClick?.();
|
|
82
151
|
dismissSgToast(toast.id);
|
|
83
152
|
}, children: toast.action.label })) : null, canClose ? (_jsx("button", { type: "button", className: "rounded px-1.5 py-0.5 text-xs opacity-80 hover:opacity-100", onClick: () => dismissSgToast(toast.id), "aria-label": "Close toast", children: "x" })) : null] }, toast.id));
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type SgDiscardDigitProps = {
|
|
3
|
+
/** Texto/valor exibido na folha do topo. */
|
|
4
|
+
value: string;
|
|
5
|
+
/** Cor do texto principal. */
|
|
6
|
+
color?: string;
|
|
7
|
+
/** Fonte (font-family) usada no texto. */
|
|
8
|
+
font?: string;
|
|
9
|
+
/** Cor de fundo das folhas. */
|
|
10
|
+
backgroundColor?: string;
|
|
11
|
+
/** Tamanho da fonte em px (escala geral do bloco). */
|
|
12
|
+
fontSize?: number;
|
|
13
|
+
/** Peso da fonte. */
|
|
14
|
+
fontWeight?: number | string;
|
|
15
|
+
/** Ativa animacao de descarte quando o valor muda. */
|
|
16
|
+
animateOnChange?: boolean;
|
|
17
|
+
/** Duracao total da animacao em ms. */
|
|
18
|
+
transitionMs?: number;
|
|
19
|
+
/** Quantidade de folhas visiveis na pilha (min 2, max 30). Ignorado se totalNumberPages for definido. */
|
|
20
|
+
stackDepth?: number;
|
|
21
|
+
/** Total de paginas do monte. Quando definido, a pilha visual encolhe a cada increasePage(). */
|
|
22
|
+
totalNumberPages?: number;
|
|
23
|
+
/** Estrategia de animacao para mudancas por prop `value`. */
|
|
24
|
+
changeAnimationMode?: "discard" | "incoming";
|
|
25
|
+
/** Classes CSS adicionais. */
|
|
26
|
+
className?: string;
|
|
27
|
+
/** Estilo inline adicional. */
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
};
|
|
30
|
+
export type SgDiscardDigitHandle = {
|
|
31
|
+
/** Decrementa a pagina atual (minimo 1). */
|
|
32
|
+
decreasePage(): void;
|
|
33
|
+
/** Incrementa a pagina atual (maximo totalNumberPages). */
|
|
34
|
+
increasePage(): void;
|
|
35
|
+
/** Retorna a pagina atual (1-indexed). */
|
|
36
|
+
page(): number;
|
|
37
|
+
};
|
|
38
|
+
export declare const SgDiscardDigit: React.ForwardRefExoticComponent<SgDiscardDigitProps & React.RefAttributes<SgDiscardDigitHandle>>;
|
|
39
|
+
//# sourceMappingURL=SgDiscardDigit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgDiscardDigit.d.ts","sourceRoot":"","sources":["../../../src/digits/discard-digit/SgDiscardDigit.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA4B/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,sDAAsD;IACtD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,mBAAmB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAC7C,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,4CAA4C;IAC5C,YAAY,IAAI,IAAI,CAAC;IACrB,2DAA2D;IAC3D,YAAY,IAAI,IAAI,CAAC;IACrB,0CAA0C;IAC1C,IAAI,IAAI,MAAM,CAAC;CAChB,CAAC;AAmBF,eAAO,MAAM,cAAc,kGAkYzB,CAAC"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function cn(...parts) {
|
|
4
|
+
return parts.filter(Boolean).join(" ");
|
|
5
|
+
}
|
|
6
|
+
function createRandomMotion(fontSize) {
|
|
7
|
+
const side = Math.random() < 0.5 ? -1 : 1;
|
|
8
|
+
const base = Math.max(26, Math.round(fontSize * 0.55));
|
|
9
|
+
return {
|
|
10
|
+
tx: side * (base + Math.round(Math.random() * base * 0.55)),
|
|
11
|
+
ty: Math.max(56, Math.round(fontSize * 1.45 + Math.random() * fontSize * 0.55)),
|
|
12
|
+
rotateZ: side * (8 + Math.round(Math.random() * 12)),
|
|
13
|
+
rotateX: -(10 + Math.round(Math.random() * 16)),
|
|
14
|
+
scale: 0.68 + Math.random() * 0.14,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** px that each sheet peeks below the one above — visible "spine" line */
|
|
18
|
+
const PER_LAYER_Y = 3;
|
|
19
|
+
/** horizontal drift per layer — subtle perspective illusion */
|
|
20
|
+
const PER_LAYER_X = 0;
|
|
21
|
+
export const SgDiscardDigit = React.forwardRef(function SgDiscardDigit({ value, color = "#0f172a", font, backgroundColor = "#f8fafc", fontSize = 64, fontWeight = 700, animateOnChange = true, transitionMs = 640, stackDepth = 20, totalNumberPages, changeAnimationMode = "discard", className, style, }, ref) {
|
|
22
|
+
const [currentPage, setCurrentPage] = React.useState(1);
|
|
23
|
+
const directionRef = React.useRef("next");
|
|
24
|
+
React.useImperativeHandle(ref, () => ({
|
|
25
|
+
decreasePage() {
|
|
26
|
+
directionRef.current = "prev";
|
|
27
|
+
setCurrentPage((p) => Math.max(1, p - 1));
|
|
28
|
+
},
|
|
29
|
+
increasePage() {
|
|
30
|
+
directionRef.current = "next";
|
|
31
|
+
setCurrentPage((p) => (totalNumberPages != null ? Math.min(totalNumberPages, p + 1) : p + 1));
|
|
32
|
+
},
|
|
33
|
+
page() {
|
|
34
|
+
return currentPage;
|
|
35
|
+
},
|
|
36
|
+
}), [currentPage, totalNumberPages]);
|
|
37
|
+
const depth = totalNumberPages != null
|
|
38
|
+
? Math.max(1, Math.min(30, totalNumberPages - currentPage + 1))
|
|
39
|
+
: Math.max(2, Math.min(30, stackDepth));
|
|
40
|
+
// ── Dimensions ─────────────────────────────────────────────────────────────
|
|
41
|
+
const cardW = Math.max(74, Math.round(fontSize * 1.14));
|
|
42
|
+
const cardH = Math.max(96, Math.round(fontSize * 1.52));
|
|
43
|
+
const radius = Math.max(8, Math.round(fontSize * 0.15));
|
|
44
|
+
const textSize = Math.max(18, Math.round(fontSize * 0.86));
|
|
45
|
+
// depth-1 backing layers so total visible sheets = depth
|
|
46
|
+
const numStack = depth - 1;
|
|
47
|
+
const PAD = 10;
|
|
48
|
+
const containerW = cardW + PAD * 2 + Math.ceil(numStack * PER_LAYER_X);
|
|
49
|
+
const containerH = cardH + numStack * PER_LAYER_Y + PAD * 2;
|
|
50
|
+
// Left edge where cards start (horizontally centered)
|
|
51
|
+
const cardLeft = Math.floor((containerW - cardW) / 2);
|
|
52
|
+
// ── Paper aesthetics ────────────────────────────────────────────────────────
|
|
53
|
+
const paperEdgeSoft = "rgba(15, 23, 42, 0.16)";
|
|
54
|
+
const paperEdgeMid = "rgba(15, 23, 42, 0.26)";
|
|
55
|
+
const paperTexture = "repeating-linear-gradient(0deg, rgba(15,23,42,0.025) 0px, rgba(15,23,42,0.025) 1px, transparent 1px, transparent 4px)";
|
|
56
|
+
// ── State ───────────────────────────────────────────────────────────────────
|
|
57
|
+
const [displayValue, setDisplayValue] = React.useState(value);
|
|
58
|
+
const [activeDiscard, setActiveDiscard] = React.useState(null);
|
|
59
|
+
const [activeIncoming, setActiveIncoming] = React.useState(null);
|
|
60
|
+
const discardIdRef = React.useRef(0);
|
|
61
|
+
const latestValueRef = React.useRef(value);
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
latestValueRef.current = value;
|
|
64
|
+
}, [value]);
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (!animateOnChange) {
|
|
67
|
+
setActiveDiscard(null);
|
|
68
|
+
setActiveIncoming(null);
|
|
69
|
+
setDisplayValue(value);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (value === displayValue && activeDiscard === null && activeIncoming === null)
|
|
73
|
+
return;
|
|
74
|
+
const shouldUseIncoming = directionRef.current === "prev" || changeAnimationMode === "incoming";
|
|
75
|
+
if (shouldUseIncoming) {
|
|
76
|
+
// Incoming: nova folha sobe por baixo revelando o valor anterior
|
|
77
|
+
if (activeIncoming === null && value !== displayValue) {
|
|
78
|
+
const nextId = ++discardIdRef.current;
|
|
79
|
+
setActiveIncoming({
|
|
80
|
+
id: nextId,
|
|
81
|
+
value,
|
|
82
|
+
motion: createRandomMotion(fontSize),
|
|
83
|
+
started: false,
|
|
84
|
+
});
|
|
85
|
+
// displayValue permanece no valor antigo; atualiza só ao fim da animacao
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Discard: folha atual voa para fora, revelando o novo valor
|
|
90
|
+
if (activeIncoming === null) {
|
|
91
|
+
if (activeDiscard === null) {
|
|
92
|
+
const nextId = ++discardIdRef.current;
|
|
93
|
+
setActiveDiscard({
|
|
94
|
+
id: nextId,
|
|
95
|
+
value: displayValue,
|
|
96
|
+
motion: createRandomMotion(fontSize),
|
|
97
|
+
started: false,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
setDisplayValue(value);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
directionRef.current = "next";
|
|
104
|
+
}, [activeDiscard, activeIncoming, animateOnChange, changeAnimationMode, displayValue, fontSize, value]);
|
|
105
|
+
// ── RAF: discard ─────────────────────────────────────────────────────────
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
if (!activeDiscard || activeDiscard.started)
|
|
108
|
+
return;
|
|
109
|
+
let raf = 0;
|
|
110
|
+
raf = window.requestAnimationFrame(() => {
|
|
111
|
+
setActiveDiscard((prev) => prev && prev.id === activeDiscard.id ? { ...prev, started: true } : prev);
|
|
112
|
+
});
|
|
113
|
+
return () => window.cancelAnimationFrame(raf);
|
|
114
|
+
}, [activeDiscard]);
|
|
115
|
+
// ── RAF: incoming ────────────────────────────────────────────────────────
|
|
116
|
+
React.useEffect(() => {
|
|
117
|
+
if (!activeIncoming || activeIncoming.started)
|
|
118
|
+
return;
|
|
119
|
+
let raf = 0;
|
|
120
|
+
raf = window.requestAnimationFrame(() => {
|
|
121
|
+
setActiveIncoming((prev) => prev && prev.id === activeIncoming.id ? { ...prev, started: true } : prev);
|
|
122
|
+
});
|
|
123
|
+
return () => window.cancelAnimationFrame(raf);
|
|
124
|
+
}, [activeIncoming]);
|
|
125
|
+
// ── Cleanup: discard ─────────────────────────────────────────────────────
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
if (!activeDiscard || !activeDiscard.started)
|
|
128
|
+
return;
|
|
129
|
+
const currentId = activeDiscard.id;
|
|
130
|
+
const timer = window.setTimeout(() => {
|
|
131
|
+
setActiveDiscard((prev) => (prev && prev.id === currentId ? null : prev));
|
|
132
|
+
const latest = latestValueRef.current;
|
|
133
|
+
setDisplayValue((prev) => (prev === latest ? prev : latest));
|
|
134
|
+
}, Math.max(120, transitionMs));
|
|
135
|
+
return () => window.clearTimeout(timer);
|
|
136
|
+
}, [activeDiscard, transitionMs]);
|
|
137
|
+
// ── Cleanup: incoming ────────────────────────────────────────────────────
|
|
138
|
+
React.useEffect(() => {
|
|
139
|
+
if (!activeIncoming || !activeIncoming.started)
|
|
140
|
+
return;
|
|
141
|
+
const currentId = activeIncoming.id;
|
|
142
|
+
const timer = window.setTimeout(() => {
|
|
143
|
+
setActiveIncoming((prev) => (prev && prev.id === currentId ? null : prev));
|
|
144
|
+
setDisplayValue(latestValueRef.current);
|
|
145
|
+
}, Math.max(120, transitionMs));
|
|
146
|
+
return () => window.clearTimeout(timer);
|
|
147
|
+
}, [activeIncoming, transitionMs]);
|
|
148
|
+
// ── Stack layers ────────────────────────────────────────────────────────────
|
|
149
|
+
// i=0 → BOTTOM of stack (most displaced, lowest z-index = 1)
|
|
150
|
+
// i=numStack-1 → just below main card (least displaced, highest z-index = numStack)
|
|
151
|
+
// Each layer shows exactly PER_LAYER_Y px of its bottom edge.
|
|
152
|
+
const stackLayers = React.useMemo(() => {
|
|
153
|
+
return Array.from({ length: numStack }, (_, i) => {
|
|
154
|
+
const distFromTop = numStack - i; // 1 = closest to main, numStack = bottom
|
|
155
|
+
return {
|
|
156
|
+
top: PAD + distFromTop * PER_LAYER_Y,
|
|
157
|
+
left: cardLeft + distFromTop * PER_LAYER_X,
|
|
158
|
+
// Subtle darkening toward the bottom of the stack
|
|
159
|
+
opacity: Math.max(0.80, 1 - (distFromTop - 1) * 0.007),
|
|
160
|
+
zIndex: i + 1, // bottom=1 (behind), top-of-stack=numStack (in front)
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
}, [numStack, cardLeft]);
|
|
164
|
+
const shadowTone = "rgba(2, 8, 23, 0.28)";
|
|
165
|
+
const subtleTone = "rgba(2, 8, 23, 0.12)";
|
|
166
|
+
return (_jsxs("div", { role: "img", "aria-label": value, className: cn("inline-block", className), style: {
|
|
167
|
+
position: "relative",
|
|
168
|
+
width: containerW,
|
|
169
|
+
height: containerH,
|
|
170
|
+
perspective: "1000px",
|
|
171
|
+
...style,
|
|
172
|
+
}, children: [_jsx("div", { "aria-hidden": "true", style: {
|
|
173
|
+
position: "absolute",
|
|
174
|
+
width: Math.round(cardW * 0.88),
|
|
175
|
+
height: Math.max(14, Math.round(cardH * 0.12)),
|
|
176
|
+
left: "50%",
|
|
177
|
+
top: PAD + cardH + numStack * PER_LAYER_Y + 2,
|
|
178
|
+
transform: "translateX(-50%)",
|
|
179
|
+
filter: "blur(8px)",
|
|
180
|
+
background: "radial-gradient(ellipse at center, rgba(2,8,23,0.30) 0%, rgba(2,8,23,0.05) 70%, transparent 100%)",
|
|
181
|
+
} }), stackLayers.map((layer, i) => (_jsx("div", { "aria-hidden": "true", style: {
|
|
182
|
+
position: "absolute",
|
|
183
|
+
top: layer.top,
|
|
184
|
+
left: layer.left,
|
|
185
|
+
width: cardW,
|
|
186
|
+
height: cardH,
|
|
187
|
+
borderRadius: radius,
|
|
188
|
+
backgroundColor,
|
|
189
|
+
// Visible top + side borders; bottom border slightly stronger so each
|
|
190
|
+
// 3-px strip is legible as a distinct sheet edge.
|
|
191
|
+
border: `1px solid ${paperEdgeSoft}`,
|
|
192
|
+
borderBottom: `1px solid ${paperEdgeMid}`,
|
|
193
|
+
boxShadow: `inset 0 1px 0 rgba(255,255,255,0.70), inset 0 -1px 0 rgba(15,23,42,0.06)`,
|
|
194
|
+
opacity: layer.opacity,
|
|
195
|
+
zIndex: layer.zIndex,
|
|
196
|
+
} }, `sheet-${i}`))), _jsxs("div", { style: {
|
|
197
|
+
position: "absolute",
|
|
198
|
+
top: PAD,
|
|
199
|
+
left: cardLeft,
|
|
200
|
+
width: cardW,
|
|
201
|
+
height: cardH,
|
|
202
|
+
borderRadius: radius,
|
|
203
|
+
backgroundColor,
|
|
204
|
+
border: `1px solid ${paperEdgeSoft}`,
|
|
205
|
+
display: "flex",
|
|
206
|
+
alignItems: "center",
|
|
207
|
+
justifyContent: "center",
|
|
208
|
+
boxShadow: `0 8px 16px ${subtleTone}, 0 0 0 1px rgba(255,255,255,0.22), inset 0 1px 0 rgba(255,255,255,0.80), inset 0 -1px 0 rgba(15,23,42,0.07)`,
|
|
209
|
+
transition: `opacity ${Math.round(transitionMs * 0.52)}ms ease`,
|
|
210
|
+
zIndex: numStack + 1,
|
|
211
|
+
overflow: "hidden",
|
|
212
|
+
}, children: [_jsx("div", { "aria-hidden": "true", style: {
|
|
213
|
+
position: "absolute",
|
|
214
|
+
inset: 0,
|
|
215
|
+
background: `linear-gradient(160deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0.12) 28%, rgba(255,255,255,0) 55%), linear-gradient(180deg, rgba(0,0,0,0.02) 0%, rgba(0,0,0,0.09) 100%), ${paperTexture}`,
|
|
216
|
+
pointerEvents: "none",
|
|
217
|
+
} }), _jsx("span", { style: {
|
|
218
|
+
position: "relative",
|
|
219
|
+
zIndex: 1,
|
|
220
|
+
color,
|
|
221
|
+
fontFamily: font,
|
|
222
|
+
fontSize: textSize,
|
|
223
|
+
fontWeight,
|
|
224
|
+
lineHeight: 1,
|
|
225
|
+
userSelect: "none",
|
|
226
|
+
textShadow: `0 1px 0 rgba(255,255,255,0.45), 0 10px 16px rgba(2,8,23,0.10)`,
|
|
227
|
+
}, children: displayValue })] }), activeIncoming ? (_jsxs("div", { style: {
|
|
228
|
+
position: "absolute",
|
|
229
|
+
top: PAD,
|
|
230
|
+
left: cardLeft,
|
|
231
|
+
width: cardW,
|
|
232
|
+
height: cardH,
|
|
233
|
+
borderRadius: radius,
|
|
234
|
+
backgroundColor,
|
|
235
|
+
border: `1px solid ${paperEdgeMid}`,
|
|
236
|
+
display: "flex",
|
|
237
|
+
alignItems: "center",
|
|
238
|
+
justifyContent: "center",
|
|
239
|
+
transform: activeIncoming.started
|
|
240
|
+
? "translate3d(0, 0, 80px) rotateX(0deg) rotateZ(0deg) scale(1)"
|
|
241
|
+
: `translate3d(${activeIncoming.motion.tx}px, ${activeIncoming.motion.ty}px, 74px) rotateX(${activeIncoming.motion.rotateX}deg) rotateZ(${activeIncoming.motion.rotateZ}deg) scale(${activeIncoming.motion.scale})`,
|
|
242
|
+
transformOrigin: "50% 80%",
|
|
243
|
+
opacity: activeIncoming.started ? 1 : 0,
|
|
244
|
+
boxShadow: `0 16px 22px ${shadowTone}, 0 30px 30px rgba(2,8,23,0.18), 0 0 0 1px rgba(255,255,255,0.26), inset 0 1px 0 rgba(255,255,255,0.82), inset 0 -1px 0 rgba(15,23,42,0.10)`,
|
|
245
|
+
transition: `transform ${transitionMs}ms cubic-bezier(0.18, 0.78, 0.18, 1), opacity ${Math.round(transitionMs * 0.6)}ms ease`,
|
|
246
|
+
zIndex: numStack + 20,
|
|
247
|
+
overflow: "hidden",
|
|
248
|
+
}, children: [_jsx("div", { "aria-hidden": "true", style: {
|
|
249
|
+
position: "absolute",
|
|
250
|
+
inset: 0,
|
|
251
|
+
background: `linear-gradient(160deg, rgba(255,255,255,0.62) 0%, rgba(255,255,255,0.16) 30%, rgba(255,255,255,0) 58%), linear-gradient(180deg, rgba(0,0,0,0.02) 0%, rgba(0,0,0,0.12) 100%), ${paperTexture}`,
|
|
252
|
+
pointerEvents: "none",
|
|
253
|
+
} }), _jsx("span", { style: {
|
|
254
|
+
position: "relative",
|
|
255
|
+
zIndex: 1,
|
|
256
|
+
color,
|
|
257
|
+
fontFamily: font,
|
|
258
|
+
fontSize: textSize,
|
|
259
|
+
fontWeight,
|
|
260
|
+
lineHeight: 1,
|
|
261
|
+
userSelect: "none",
|
|
262
|
+
textShadow: `0 1px 0 rgba(255,255,255,0.45), 0 12px 18px rgba(2,8,23,0.13)`,
|
|
263
|
+
}, children: activeIncoming.value })] })) : null, activeDiscard ? (_jsxs("div", { style: {
|
|
264
|
+
position: "absolute",
|
|
265
|
+
top: PAD,
|
|
266
|
+
left: cardLeft,
|
|
267
|
+
width: cardW,
|
|
268
|
+
height: cardH,
|
|
269
|
+
borderRadius: radius,
|
|
270
|
+
backgroundColor,
|
|
271
|
+
border: `1px solid ${paperEdgeMid}`,
|
|
272
|
+
display: "flex",
|
|
273
|
+
alignItems: "center",
|
|
274
|
+
justifyContent: "center",
|
|
275
|
+
transform: activeDiscard.started
|
|
276
|
+
? `translate3d(${activeDiscard.motion.tx}px, ${activeDiscard.motion.ty}px, 74px) rotateX(${activeDiscard.motion.rotateX}deg) rotateZ(${activeDiscard.motion.rotateZ}deg) scale(${activeDiscard.motion.scale})`
|
|
277
|
+
: "translate3d(0, 0, 80px) rotateX(0deg) rotateZ(0deg) scale(1)",
|
|
278
|
+
transformOrigin: "50% 20%",
|
|
279
|
+
opacity: activeDiscard.started ? 0 : 1,
|
|
280
|
+
boxShadow: activeDiscard.started
|
|
281
|
+
? `0 7px 15px ${shadowTone}, 0 22px 26px rgba(2,8,23,0.14), inset 0 1px 0 rgba(255,255,255,0.72)`
|
|
282
|
+
: `0 16px 22px ${shadowTone}, 0 30px 30px rgba(2,8,23,0.18), 0 0 0 1px rgba(255,255,255,0.26), inset 0 1px 0 rgba(255,255,255,0.82), inset 0 -1px 0 rgba(15,23,42,0.10)`,
|
|
283
|
+
transition: `transform ${transitionMs}ms cubic-bezier(0.2, 0.82, 0.2, 1), opacity ${transitionMs}ms ease, box-shadow ${Math.round(transitionMs * 0.65)}ms ease`,
|
|
284
|
+
zIndex: numStack + 20,
|
|
285
|
+
overflow: "hidden",
|
|
286
|
+
}, children: [_jsx("div", { "aria-hidden": "true", style: {
|
|
287
|
+
position: "absolute",
|
|
288
|
+
inset: 0,
|
|
289
|
+
background: `linear-gradient(160deg, rgba(255,255,255,0.62) 0%, rgba(255,255,255,0.16) 30%, rgba(255,255,255,0) 58%), linear-gradient(180deg, rgba(0,0,0,0.02) 0%, rgba(0,0,0,0.12) 100%), ${paperTexture}`,
|
|
290
|
+
pointerEvents: "none",
|
|
291
|
+
} }), _jsx("span", { style: {
|
|
292
|
+
position: "relative",
|
|
293
|
+
zIndex: 1,
|
|
294
|
+
color,
|
|
295
|
+
fontFamily: font,
|
|
296
|
+
fontSize: textSize,
|
|
297
|
+
fontWeight,
|
|
298
|
+
lineHeight: 1,
|
|
299
|
+
userSelect: "none",
|
|
300
|
+
textShadow: `0 1px 0 rgba(255,255,255,0.45), 0 12px 18px rgba(2,8,23,0.13)`,
|
|
301
|
+
}, children: activeDiscard.value })] })) : null] }));
|
|
302
|
+
});
|
|
303
|
+
SgDiscardDigit.displayName = "SgDiscardDigit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/digits/discard-digit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SgDiscardDigit } from "./SgDiscardDigit";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type SgFadeDigitProps = {
|
|
2
|
+
/** Character to display. When changed, the current digit fades out and the new one fades in. */
|
|
3
|
+
value: string;
|
|
4
|
+
/** Text color of the digit. @default "#edebeb" */
|
|
5
|
+
color?: string;
|
|
6
|
+
/** Card background color. @default "#333232" */
|
|
7
|
+
backgroundColor?: string;
|
|
8
|
+
/**
|
|
9
|
+
* CSS font-family for the digit.
|
|
10
|
+
* @default undefined (browser default)
|
|
11
|
+
*/
|
|
12
|
+
font?: string;
|
|
13
|
+
/** Font size in pixels — controls overall card scale. @default 70 */
|
|
14
|
+
fontSize?: number;
|
|
15
|
+
/** Additional CSS classes on the outer card wrapper. */
|
|
16
|
+
className?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* SgFadeDigit — an animated card that "turns off" its current digit (fade to
|
|
20
|
+
* transparent) and "turns on" the new one (fade in), simulating a bulb or
|
|
21
|
+
* display switching effect.
|
|
22
|
+
*
|
|
23
|
+
* The card style matches SgFlipDigit (same proportions, divider line and
|
|
24
|
+
* shadow) so both components can be mixed in clock-style layouts.
|
|
25
|
+
*/
|
|
26
|
+
export declare function SgFadeDigit({ value, color, backgroundColor, font, fontSize, className, }: SgFadeDigitProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=SgFadeDigit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgFadeDigit.d.ts","sourceRoot":"","sources":["../../../src/digits/fade-digit/SgFadeDigit.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gGAAgG;IAChG,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,KAAiB,EACjB,eAA2B,EAC3B,IAAI,EACJ,QAAa,EACb,SAAS,GACV,EAAE,gBAAgB,2CAyGlB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
/** Duration of each fade phase (out and in) in ms */
|
|
5
|
+
const FADE_MS = 200;
|
|
6
|
+
/**
|
|
7
|
+
* SgFadeDigit — an animated card that "turns off" its current digit (fade to
|
|
8
|
+
* transparent) and "turns on" the new one (fade in), simulating a bulb or
|
|
9
|
+
* display switching effect.
|
|
10
|
+
*
|
|
11
|
+
* The card style matches SgFlipDigit (same proportions, divider line and
|
|
12
|
+
* shadow) so both components can be mixed in clock-style layouts.
|
|
13
|
+
*/
|
|
14
|
+
export function SgFadeDigit({ value, color = "#edebeb", backgroundColor = "#333232", font, fontSize = 70, className, }) {
|
|
15
|
+
/**
|
|
16
|
+
* What is rendered in the card. Lags behind `value` during animation:
|
|
17
|
+
* we first fade out the old value, swap here, then fade in the new one.
|
|
18
|
+
*/
|
|
19
|
+
const [displayValue, setDisplayValue] = React.useState(value);
|
|
20
|
+
/** Drives the CSS opacity transition (1 = fully visible, 0 = invisible). */
|
|
21
|
+
const [opacity, setOpacity] = React.useState(1);
|
|
22
|
+
React.useEffect(() => {
|
|
23
|
+
// Nothing to do — already showing the right value
|
|
24
|
+
if (value === displayValue)
|
|
25
|
+
return;
|
|
26
|
+
// Phase 1: fade out
|
|
27
|
+
setOpacity(0);
|
|
28
|
+
// Phase 2: after fade-out completes, swap text and fade in
|
|
29
|
+
const t = window.setTimeout(() => {
|
|
30
|
+
setDisplayValue(value); // swap while invisible
|
|
31
|
+
setOpacity(1); // fade in with new value
|
|
32
|
+
}, FADE_MS);
|
|
33
|
+
return () => window.clearTimeout(t);
|
|
34
|
+
// displayValue intentionally in deps: when setDisplayValue fires it causes
|
|
35
|
+
// a re-run, which returns early (value === displayValue) — no loop.
|
|
36
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
+
}, [value, displayValue]);
|
|
38
|
+
// ── Card dimensions (proportional to fontSize, matching SgFlipDigit) ──────
|
|
39
|
+
const cardW = Math.round(fontSize * 0.88);
|
|
40
|
+
const cardH = Math.round(fontSize * 1.38);
|
|
41
|
+
const cardRadius = Math.max(2, Math.round(fontSize * 0.04));
|
|
42
|
+
return (_jsxs("div", { "aria-label": displayValue, className: className, style: {
|
|
43
|
+
display: "inline-block",
|
|
44
|
+
position: "relative",
|
|
45
|
+
width: cardW,
|
|
46
|
+
height: cardH,
|
|
47
|
+
borderRadius: cardRadius,
|
|
48
|
+
backgroundColor,
|
|
49
|
+
boxShadow: "0 .125em .3125em rgba(0,0,0,.25), 0 .02125em .06125em rgba(0,0,0,.25)",
|
|
50
|
+
overflow: "hidden",
|
|
51
|
+
flexShrink: 0,
|
|
52
|
+
}, children: [_jsx("div", { "aria-hidden": "true", style: {
|
|
53
|
+
position: "absolute",
|
|
54
|
+
left: 0,
|
|
55
|
+
right: 0,
|
|
56
|
+
top: Math.round(cardH / 2),
|
|
57
|
+
height: 1,
|
|
58
|
+
backgroundColor: "rgba(0,0,0,0.35)",
|
|
59
|
+
zIndex: 1,
|
|
60
|
+
pointerEvents: "none",
|
|
61
|
+
} }), _jsx("div", { "aria-hidden": "true", style: {
|
|
62
|
+
position: "absolute",
|
|
63
|
+
inset: 0,
|
|
64
|
+
background: "linear-gradient(to bottom, rgba(255,255,255,0.045) 0%, transparent 52%)",
|
|
65
|
+
zIndex: 2,
|
|
66
|
+
pointerEvents: "none",
|
|
67
|
+
} }), _jsx("div", { style: {
|
|
68
|
+
position: "absolute",
|
|
69
|
+
inset: 0,
|
|
70
|
+
display: "flex",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
justifyContent: "center",
|
|
73
|
+
fontSize,
|
|
74
|
+
color,
|
|
75
|
+
fontFamily: font,
|
|
76
|
+
fontWeight: "bold",
|
|
77
|
+
lineHeight: 1,
|
|
78
|
+
userSelect: "none",
|
|
79
|
+
opacity,
|
|
80
|
+
// Transition is CONSTANT — direction never changes, so the browser
|
|
81
|
+
// always animates when opacity goes 1→0 or 0→1.
|
|
82
|
+
transition: `opacity ${FADE_MS}ms ease`,
|
|
83
|
+
zIndex: 3,
|
|
84
|
+
}, children: displayValue })] }));
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/digits/fade-digit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SgFadeDigit } from "./SgFadeDigit";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type SgFlipDigitProps = {
|
|
2
|
+
/** The character to display (single char) */
|
|
3
|
+
value: string;
|
|
4
|
+
/** Font size in pixels — controls overall scale */
|
|
5
|
+
fontSize?: number;
|
|
6
|
+
/** Additional CSS classes applied to the outer wrapper */
|
|
7
|
+
className?: string;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Use fontSize to control size. width/height are ignored when using @pqina/flip.
|
|
10
|
+
*/
|
|
11
|
+
width?: number;
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Use fontSize to control size. width/height are ignored when using @pqina/flip.
|
|
14
|
+
*/
|
|
15
|
+
height?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* SgFlipDigit - Animated flip card powered by @pqina/flip (MIT)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const [digit, setDigit] = useState("0");
|
|
23
|
+
* <SgFlipDigit value={digit} fontSize={70} />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function SgFlipDigit({ value, fontSize, className, }: SgFlipDigitProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=SgFlipDigit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgFlipDigit.d.ts","sourceRoot":"","sources":["../../../src/digits/flip-digit/SgFlipDigit.tsx"],"names":[],"mappings":"AAuBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAa,EACb,SAAS,GACV,EAAE,gBAAgB,2CAwDlB"}
|