@steez-ui/ui 0.1.5 → 0.1.7
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/README.md +3 -3
- package/dist/components/LoadingOverlayCrystalline.d.ts +10 -0
- package/dist/components/LoadingOverlayCrystalline.d.ts.map +1 -0
- package/dist/components/LoadingOverlayCrystalline.js +7 -0
- package/dist/components/LoadingOverlayCrystalline.js.map +1 -0
- package/dist/components/LoadingOverlayCrystalline.module.css +39 -0
- package/dist/components/LoadingScreen.d.ts +17 -0
- package/dist/components/LoadingScreen.d.ts.map +1 -0
- package/dist/components/LoadingScreen.js +246 -0
- package/dist/components/LoadingScreen.js.map +1 -0
- package/dist/components/LoadingScreen.module.css +108 -0
- package/dist/components/OverlayButton.d.ts +7 -0
- package/dist/components/OverlayButton.d.ts.map +1 -0
- package/dist/components/OverlayButton.js +8 -0
- package/dist/components/OverlayButton.js.map +1 -0
- package/dist/components/OverlayButton.module.css +32 -0
- package/dist/components/PixelTooltip.d.ts +10 -0
- package/dist/components/PixelTooltip.d.ts.map +1 -0
- package/dist/components/PixelTooltip.js +41 -0
- package/dist/components/PixelTooltip.js.map +1 -0
- package/dist/components/PixelTooltip.module.css +81 -0
- package/dist/components/QuickInfoCard.d.ts +20 -0
- package/dist/components/QuickInfoCard.d.ts.map +1 -0
- package/dist/components/QuickInfoCard.js +38 -0
- package/dist/components/QuickInfoCard.js.map +1 -0
- package/dist/components/QuickInfoCard.module.css +138 -0
- package/dist/components/Section.d.ts +9 -0
- package/dist/components/Section.d.ts.map +1 -0
- package/dist/components/Section.js +7 -0
- package/dist/components/Section.js.map +1 -0
- package/dist/components/Section.module.css +20 -0
- package/dist/components/SectionHeader.d.ts +10 -0
- package/dist/components/SectionHeader.d.ts.map +1 -0
- package/dist/components/SectionHeader.js +7 -0
- package/dist/components/SectionHeader.js.map +1 -0
- package/dist/components/SectionHeader.module.css +54 -0
- package/dist/components/StatCard.d.ts +11 -0
- package/dist/components/StatCard.d.ts.map +1 -0
- package/dist/components/StatCard.js +14 -0
- package/dist/components/StatCard.js.map +1 -0
- package/dist/components/StatCard.module.css +41 -0
- package/dist/components/WidgetCard.d.ts +12 -0
- package/dist/components/WidgetCard.d.ts.map +1 -0
- package/dist/components/WidgetCard.js +17 -0
- package/dist/components/WidgetCard.js.map +1 -0
- package/dist/components/WidgetCard.module.css +75 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -101,9 +101,9 @@ GitHub Actions mirror the same CI, npm publish, and Cloudflare Pages deploy path
|
|
|
101
101
|
|
|
102
102
|
## Current Packages
|
|
103
103
|
|
|
104
|
-
- `@steez-ui/theme@0.1.
|
|
105
|
-
- `@steez-ui/icons@0.1.
|
|
106
|
-
- `@steez-ui/ui@0.1.
|
|
104
|
+
- `@steez-ui/theme@0.1.7`
|
|
105
|
+
- `@steez-ui/icons@0.1.7`
|
|
106
|
+
- `@steez-ui/ui@0.1.7`
|
|
107
107
|
|
|
108
108
|
## External Setup
|
|
109
109
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface LoadingOverlayCrystallineProps {
|
|
3
|
+
message: string;
|
|
4
|
+
subtext?: string;
|
|
5
|
+
icon?: React.ReactNode;
|
|
6
|
+
showEffect?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function LoadingOverlayCrystalline({ message, subtext, icon, showEffect, }: LoadingOverlayCrystallineProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default LoadingOverlayCrystalline;
|
|
10
|
+
//# sourceMappingURL=LoadingOverlayCrystalline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingOverlayCrystalline.d.ts","sourceRoot":"","sources":["../../src/components/LoadingOverlayCrystalline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,IAAI,EACJ,UAAkB,GACnB,EAAE,8BAA8B,2CAYhC;AAED,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styles from "./LoadingOverlayCrystalline.module.css";
|
|
3
|
+
export function LoadingOverlayCrystalline({ message, subtext, icon, showEffect = false, }) {
|
|
4
|
+
return (_jsx("div", { className: `${styles.root} ${showEffect ? "" : styles.noEffect}`.trim(), children: _jsxs("div", { className: styles.card, children: [icon ? _jsx("span", { className: styles.icon, children: icon }) : null, _jsxs("div", { children: [_jsx("div", { children: message }), subtext ? _jsx("div", { className: styles.subtext, children: subtext }) : null] })] }) }));
|
|
5
|
+
}
|
|
6
|
+
export default LoadingOverlayCrystalline;
|
|
7
|
+
//# sourceMappingURL=LoadingOverlayCrystalline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingOverlayCrystalline.js","sourceRoot":"","sources":["../../src/components/LoadingOverlayCrystalline.tsx"],"names":[],"mappings":";AAEA,OAAO,MAAM,MAAM,wCAAwC,CAAC;AAS5D,MAAM,UAAU,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,IAAI,EACJ,UAAU,GAAG,KAAK,GACa;IAC/B,OAAO,CACL,cAAK,SAAS,EAAE,GAAG,MAAM,CAAC,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAC1E,eAAK,SAAS,EAAE,MAAM,CAAC,IAAI,aACxB,IAAI,CAAC,CAAC,CAAC,eAAM,SAAS,EAAE,MAAM,CAAC,IAAI,YAAG,IAAI,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC1D,0BACE,wBAAM,OAAO,GAAO,EACnB,OAAO,CAAC,CAAC,CAAC,cAAK,SAAS,EAAE,MAAM,CAAC,OAAO,YAAG,OAAO,GAAO,CAAC,CAAC,CAAC,IAAI,IAC7D,IACF,GACF,CACP,CAAC;AACJ,CAAC;AAED,eAAe,yBAAyB,CAAC","sourcesContent":["import React from \"react\";\n\nimport styles from \"./LoadingOverlayCrystalline.module.css\";\n\nexport interface LoadingOverlayCrystallineProps {\n message: string;\n subtext?: string;\n icon?: React.ReactNode;\n showEffect?: boolean;\n}\n\nexport function LoadingOverlayCrystalline({\n message,\n subtext,\n icon,\n showEffect = false,\n}: LoadingOverlayCrystallineProps) {\n return (\n <div className={`${styles.root} ${showEffect ? \"\" : styles.noEffect}`.trim()}>\n <div className={styles.card}>\n {icon ? <span className={styles.icon}>{icon}</span> : null}\n <div>\n <div>{message}</div>\n {subtext ? <div className={styles.subtext}>{subtext}</div> : null}\n </div>\n </div>\n </div>\n );\n}\n\nexport default LoadingOverlayCrystalline;\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
position: absolute;
|
|
3
|
+
inset: 0;
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
z-index: var(--z-viewer-quick-tools);
|
|
8
|
+
background: transparent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.noEffect {
|
|
12
|
+
background: transparent;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.card {
|
|
16
|
+
position: relative;
|
|
17
|
+
z-index: 20;
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 10px;
|
|
21
|
+
padding: 14px 16px;
|
|
22
|
+
background: color-mix(in srgb, var(--bg-primary) 85%, transparent);
|
|
23
|
+
border: 1px solid color-mix(in srgb, var(--text-primary) 14%, transparent);
|
|
24
|
+
border-radius: 10px;
|
|
25
|
+
color: var(--text-primary);
|
|
26
|
+
font-size: 14px;
|
|
27
|
+
font-weight: 600;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.icon {
|
|
31
|
+
opacity: 0.9;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.subtext {
|
|
35
|
+
margin-top: 2px;
|
|
36
|
+
color: color-mix(in srgb, var(--text-secondary) 92%, transparent);
|
|
37
|
+
font-size: 12px;
|
|
38
|
+
font-weight: 500;
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface LoadingScreenProps {
|
|
3
|
+
progress: number;
|
|
4
|
+
message?: string;
|
|
5
|
+
logo?: React.ReactNode;
|
|
6
|
+
title?: React.ReactNode;
|
|
7
|
+
audioFeedback?: boolean;
|
|
8
|
+
fullscreen?: boolean;
|
|
9
|
+
themeMode?: "auto" | "light" | "dark";
|
|
10
|
+
}
|
|
11
|
+
export declare function LoadingScreen({ progress, message, logo, title, audioFeedback, fullscreen, themeMode, }: LoadingScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export type LoadingStage = "init" | "auth" | "agent" | "config" | "ready";
|
|
13
|
+
export declare function useLoadingProgress(isLoading: boolean, _phase?: string, stage?: LoadingStage): {
|
|
14
|
+
progress: number;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=LoadingScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingScreen.d.ts","sourceRoot":"","sources":["../../src/components/LoadingScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CACvC;AAmCD,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,KAAiB,EACjB,aAAqB,EACrB,UAAiB,EACjB,SAAkB,GACnB,EAAE,kBAAkB,2CAmPpB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAkB1E,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,OAAO,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,GAAE,YAAqB;;;EAgD7B"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { LightCrossIcon } from "@steez-ui/icons";
|
|
4
|
+
import { LoadingProgressBar, LOADING_PROGRESS_SEGMENT_COUNT, } from "./LoadingProgressBar.js";
|
|
5
|
+
import styles from "./LoadingScreen.module.css";
|
|
6
|
+
function randomFloat() {
|
|
7
|
+
return Math.random();
|
|
8
|
+
}
|
|
9
|
+
function resolveIsLightMode(themeMode) {
|
|
10
|
+
if (themeMode === "light") {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (themeMode === "dark") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (typeof window === "undefined") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return (document.documentElement.getAttribute("data-theme") === "light" ||
|
|
20
|
+
window.localStorage.getItem("theme") === "light");
|
|
21
|
+
}
|
|
22
|
+
export function LoadingScreen({ progress, message, logo, title = "LOADING", audioFeedback = false, fullscreen = true, themeMode = "auto", }) {
|
|
23
|
+
const [displayProgress, setDisplayProgress] = React.useState(0);
|
|
24
|
+
const [ellipsisCount, setEllipsisCount] = React.useState(0);
|
|
25
|
+
const [crosses, setCrosses] = React.useState([]);
|
|
26
|
+
const [bounds, setBounds] = React.useState({ width: 0, height: 0 });
|
|
27
|
+
const prevFilledBars = React.useRef(0);
|
|
28
|
+
const crossIdRef = React.useRef(0);
|
|
29
|
+
const isLoadingRef = React.useRef(true);
|
|
30
|
+
const audioCtxRef = React.useRef(null);
|
|
31
|
+
const audioEnabledRef = React.useRef(false);
|
|
32
|
+
const containerRef = React.useRef(null);
|
|
33
|
+
const updateBounds = React.useCallback(() => {
|
|
34
|
+
const nextWidth = containerRef.current?.clientWidth || window.innerWidth || 0;
|
|
35
|
+
const nextHeight = containerRef.current?.clientHeight || window.innerHeight || 0;
|
|
36
|
+
setBounds((current) => {
|
|
37
|
+
if (current.width === nextWidth && current.height === nextHeight) {
|
|
38
|
+
return current;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
width: nextWidth,
|
|
42
|
+
height: nextHeight,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}, []);
|
|
46
|
+
const spawnCross = React.useCallback(() => {
|
|
47
|
+
if (!isLoadingRef.current || typeof window === "undefined") {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const tilt = (randomFloat() - 0.5) * 6;
|
|
51
|
+
const availableWidth = Math.max(bounds.width || window.innerWidth, 160);
|
|
52
|
+
const nextCross = {
|
|
53
|
+
id: crossIdRef.current += 1,
|
|
54
|
+
x: randomFloat() * Math.max(availableWidth - 120, 24),
|
|
55
|
+
rotation: tilt,
|
|
56
|
+
scale: 0.4 + randomFloat() * 0.4,
|
|
57
|
+
groundOffset: randomFloat() * 30,
|
|
58
|
+
delay: 0,
|
|
59
|
+
opacity: 0.4 + randomFloat() * 0.3,
|
|
60
|
+
};
|
|
61
|
+
setCrosses((current) => [...current.slice(-17), nextCross]);
|
|
62
|
+
}, [bounds.width]);
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
if (typeof window === "undefined") {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
updateBounds();
|
|
68
|
+
window.addEventListener("resize", updateBounds);
|
|
69
|
+
const observer = typeof ResizeObserver === "undefined" || !containerRef.current
|
|
70
|
+
? null
|
|
71
|
+
: new ResizeObserver(() => {
|
|
72
|
+
updateBounds();
|
|
73
|
+
});
|
|
74
|
+
if (observer && containerRef.current) {
|
|
75
|
+
observer.observe(containerRef.current);
|
|
76
|
+
}
|
|
77
|
+
return () => {
|
|
78
|
+
window.removeEventListener("resize", updateBounds);
|
|
79
|
+
observer?.disconnect();
|
|
80
|
+
};
|
|
81
|
+
}, [updateBounds]);
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
isLoadingRef.current = true;
|
|
84
|
+
const scheduleNextCross = () => {
|
|
85
|
+
const delay = 400 + randomFloat() * 300;
|
|
86
|
+
return setTimeout(() => {
|
|
87
|
+
spawnCross();
|
|
88
|
+
if (isLoadingRef.current) {
|
|
89
|
+
scheduleNextCross();
|
|
90
|
+
}
|
|
91
|
+
}, delay);
|
|
92
|
+
};
|
|
93
|
+
const timeoutId = scheduleNextCross();
|
|
94
|
+
return () => {
|
|
95
|
+
clearTimeout(timeoutId);
|
|
96
|
+
isLoadingRef.current = false;
|
|
97
|
+
};
|
|
98
|
+
}, [spawnCross]);
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (!audioFeedback) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const enableAudio = async () => {
|
|
104
|
+
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
105
|
+
if (!AudioContextClass) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const ctx = audioCtxRef.current ?? new AudioContextClass();
|
|
110
|
+
audioCtxRef.current = ctx;
|
|
111
|
+
if (ctx.state === "suspended") {
|
|
112
|
+
await ctx.resume();
|
|
113
|
+
}
|
|
114
|
+
audioEnabledRef.current = ctx.state === "running";
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
audioEnabledRef.current = false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
window.addEventListener("pointerdown", enableAudio, {
|
|
121
|
+
once: true,
|
|
122
|
+
passive: true,
|
|
123
|
+
});
|
|
124
|
+
window.addEventListener("keydown", enableAudio, { once: true });
|
|
125
|
+
return () => {
|
|
126
|
+
window.removeEventListener("pointerdown", enableAudio);
|
|
127
|
+
window.removeEventListener("keydown", enableAudio);
|
|
128
|
+
if (audioCtxRef.current) {
|
|
129
|
+
void audioCtxRef.current.close().catch(() => undefined);
|
|
130
|
+
audioCtxRef.current = null;
|
|
131
|
+
}
|
|
132
|
+
audioEnabledRef.current = false;
|
|
133
|
+
};
|
|
134
|
+
}, [audioFeedback]);
|
|
135
|
+
React.useEffect(() => {
|
|
136
|
+
if (!audioFeedback) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const playBeep = () => {
|
|
140
|
+
const audioCtx = audioCtxRef.current;
|
|
141
|
+
if (!audioCtx || !audioEnabledRef.current) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const osc = audioCtx.createOscillator();
|
|
145
|
+
const gain = audioCtx.createGain();
|
|
146
|
+
osc.type = "triangle";
|
|
147
|
+
osc.frequency.value = 523.25;
|
|
148
|
+
gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
|
|
149
|
+
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.08);
|
|
150
|
+
osc.connect(gain);
|
|
151
|
+
gain.connect(audioCtx.destination);
|
|
152
|
+
osc.start();
|
|
153
|
+
osc.stop(audioCtx.currentTime + 0.08);
|
|
154
|
+
};
|
|
155
|
+
const filledBars = Math.round((displayProgress / 100) * LOADING_PROGRESS_SEGMENT_COUNT);
|
|
156
|
+
if (filledBars > prevFilledBars.current) {
|
|
157
|
+
playBeep();
|
|
158
|
+
prevFilledBars.current = filledBars;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}, [audioFeedback, displayProgress]);
|
|
162
|
+
React.useEffect(() => {
|
|
163
|
+
const ellipsisInterval = setInterval(() => {
|
|
164
|
+
setEllipsisCount((count) => (count + 1) % 4);
|
|
165
|
+
}, 400);
|
|
166
|
+
return () => clearInterval(ellipsisInterval);
|
|
167
|
+
}, []);
|
|
168
|
+
React.useEffect(() => {
|
|
169
|
+
const timeout = setTimeout(() => {
|
|
170
|
+
setDisplayProgress(progress);
|
|
171
|
+
}, 50);
|
|
172
|
+
return () => clearTimeout(timeout);
|
|
173
|
+
}, [progress]);
|
|
174
|
+
const ellipsis = ".".repeat(ellipsisCount);
|
|
175
|
+
const cleanMessage = message ? message.replace(/\.+$/, "") : "";
|
|
176
|
+
const isLightMode = resolveIsLightMode(themeMode);
|
|
177
|
+
const screenWidth = bounds.width || (typeof window === "undefined" ? 0 : window.innerWidth);
|
|
178
|
+
const screenHeight = bounds.height || (typeof window === "undefined" ? 0 : window.innerHeight);
|
|
179
|
+
return (_jsxs("div", { ref: containerRef, className: `${styles.screen} ${fullscreen ? styles.fullscreen : styles.contained} ${isLightMode ? styles.lightTheme : styles.darkTheme}`.trim(), style: {
|
|
180
|
+
"--screen-height": `${screenHeight}px`,
|
|
181
|
+
"--screen-width": `${screenWidth}px`,
|
|
182
|
+
}, children: [crosses.map((cross) => (_jsx("div", { className: styles.crossContainer, style: {
|
|
183
|
+
"--cross-left": `${cross.x}px`,
|
|
184
|
+
"--cross-delay": `${cross.delay}ms`,
|
|
185
|
+
"--rotation": `${cross.rotation}deg`,
|
|
186
|
+
"--ground": `${cross.groundOffset}%`,
|
|
187
|
+
"--cross-scale": String(cross.scale),
|
|
188
|
+
"--cross-opacity": String(cross.opacity),
|
|
189
|
+
}, onAnimationEnd: (event) => {
|
|
190
|
+
if (event.animationName === "crossFall") {
|
|
191
|
+
event.currentTarget.classList.add(styles.crossLanded);
|
|
192
|
+
}
|
|
193
|
+
}, children: _jsx("div", { className: styles.crossSvg, children: _jsx(LightCrossIcon, { className: styles.crossShape }) }) }, cross.id))), _jsxs("div", { className: styles.centerStack, children: [logo ? _jsx("div", { className: styles.logoWrap, children: logo }) : null, _jsx("div", { className: styles.title, children: title })] }), _jsx(LoadingProgressBar, { progress: displayProgress, className: styles.progressRow }), _jsxs("div", { className: styles.footerMessage, children: [cleanMessage, ellipsis] })] }));
|
|
194
|
+
}
|
|
195
|
+
const STAGE_PROGRESS = {
|
|
196
|
+
init: 0,
|
|
197
|
+
auth: 25,
|
|
198
|
+
agent: 50,
|
|
199
|
+
config: 75,
|
|
200
|
+
ready: 100,
|
|
201
|
+
};
|
|
202
|
+
const STAGE_MESSAGES = {
|
|
203
|
+
init: "Initializing...",
|
|
204
|
+
auth: "Authenticating...",
|
|
205
|
+
agent: "Loading agent...",
|
|
206
|
+
config: "Loading configuration...",
|
|
207
|
+
ready: "Preparing interface...",
|
|
208
|
+
};
|
|
209
|
+
export function useLoadingProgress(isLoading, _phase, stage = "init") {
|
|
210
|
+
const [progress, setProgress] = React.useState(0);
|
|
211
|
+
const [currentMessage, setCurrentMessage] = React.useState("Initializing...");
|
|
212
|
+
const progressRef = React.useRef(0);
|
|
213
|
+
React.useEffect(() => {
|
|
214
|
+
progressRef.current = progress;
|
|
215
|
+
}, [progress]);
|
|
216
|
+
React.useEffect(() => {
|
|
217
|
+
if (!isLoading) {
|
|
218
|
+
setProgress(100);
|
|
219
|
+
progressRef.current = 100;
|
|
220
|
+
setCurrentMessage("Complete!");
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const targetProgress = STAGE_PROGRESS[stage];
|
|
224
|
+
const targetMessage = STAGE_MESSAGES[stage];
|
|
225
|
+
setCurrentMessage(targetMessage);
|
|
226
|
+
const currentProgressSnapshot = progressRef.current;
|
|
227
|
+
const diff = targetProgress - currentProgressSnapshot;
|
|
228
|
+
const steps = 20;
|
|
229
|
+
const stepDuration = 100;
|
|
230
|
+
let step = 0;
|
|
231
|
+
const interval = setInterval(() => {
|
|
232
|
+
step += 1;
|
|
233
|
+
const easedStep = step / steps;
|
|
234
|
+
const easedProgress = diff * easedStep;
|
|
235
|
+
const nextProgress = Math.min(currentProgressSnapshot + easedProgress, targetProgress);
|
|
236
|
+
progressRef.current = nextProgress;
|
|
237
|
+
setProgress(nextProgress);
|
|
238
|
+
if (step >= steps) {
|
|
239
|
+
clearInterval(interval);
|
|
240
|
+
}
|
|
241
|
+
}, stepDuration);
|
|
242
|
+
return () => clearInterval(interval);
|
|
243
|
+
}, [isLoading, stage]);
|
|
244
|
+
return { progress, message: currentMessage };
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=LoadingScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingScreen.js","sourceRoot":"","sources":["../../src/components/LoadingScreen.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from \"react\";\n\nimport { LightCrossIcon } from \"@steez-ui/icons\";\n\nimport {\n LoadingProgressBar,\n LOADING_PROGRESS_SEGMENT_COUNT,\n} from \"./LoadingProgressBar.js\";\nimport styles from \"./LoadingScreen.module.css\";\n\nexport interface LoadingScreenProps {\n progress: number;\n message?: string;\n logo?: React.ReactNode;\n title?: React.ReactNode;\n audioFeedback?: boolean;\n fullscreen?: boolean;\n themeMode?: \"auto\" | \"light\" | \"dark\";\n}\n\ninterface Cross {\n id: number;\n x: number;\n rotation: number;\n scale: number;\n groundOffset: number;\n delay: number;\n opacity: number;\n}\n\nfunction randomFloat() {\n return Math.random();\n}\n\nfunction resolveIsLightMode(themeMode: LoadingScreenProps[\"themeMode\"]) {\n if (themeMode === \"light\") {\n return true;\n }\n\n if (themeMode === \"dark\") {\n return false;\n }\n\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return (\n document.documentElement.getAttribute(\"data-theme\") === \"light\" ||\n window.localStorage.getItem(\"theme\") === \"light\"\n );\n}\n\nexport function LoadingScreen({\n progress,\n message,\n logo,\n title = \"LOADING\",\n audioFeedback = false,\n fullscreen = true,\n themeMode = \"auto\",\n}: LoadingScreenProps) {\n const [displayProgress, setDisplayProgress] = React.useState(0);\n const [ellipsisCount, setEllipsisCount] = React.useState(0);\n const [crosses, setCrosses] = React.useState<Cross[]>([]);\n const [bounds, setBounds] = React.useState({ width: 0, height: 0 });\n const prevFilledBars = React.useRef(0);\n const crossIdRef = React.useRef(0);\n const isLoadingRef = React.useRef(true);\n const audioCtxRef = React.useRef<AudioContext | null>(null);\n const audioEnabledRef = React.useRef(false);\n const containerRef = React.useRef<HTMLDivElement>(null);\n\n const updateBounds = React.useCallback(() => {\n const nextWidth =\n containerRef.current?.clientWidth || window.innerWidth || 0;\n const nextHeight =\n containerRef.current?.clientHeight || window.innerHeight || 0;\n\n setBounds((current) => {\n if (current.width === nextWidth && current.height === nextHeight) {\n return current;\n }\n\n return {\n width: nextWidth,\n height: nextHeight,\n };\n });\n }, []);\n\n const spawnCross = React.useCallback(() => {\n if (!isLoadingRef.current || typeof window === \"undefined\") {\n return;\n }\n\n const tilt = (randomFloat() - 0.5) * 6;\n const availableWidth = Math.max(bounds.width || window.innerWidth, 160);\n const nextCross: Cross = {\n id: crossIdRef.current += 1,\n x: randomFloat() * Math.max(availableWidth - 120, 24),\n rotation: tilt,\n scale: 0.4 + randomFloat() * 0.4,\n groundOffset: randomFloat() * 30,\n delay: 0,\n opacity: 0.4 + randomFloat() * 0.3,\n };\n setCrosses((current) => [...current.slice(-17), nextCross]);\n }, [bounds.width]);\n\n React.useEffect(() => {\n if (typeof window === \"undefined\") {\n return undefined;\n }\n\n updateBounds();\n window.addEventListener(\"resize\", updateBounds);\n\n const observer =\n typeof ResizeObserver === \"undefined\" || !containerRef.current\n ? null\n : new ResizeObserver(() => {\n updateBounds();\n });\n\n if (observer && containerRef.current) {\n observer.observe(containerRef.current);\n }\n\n return () => {\n window.removeEventListener(\"resize\", updateBounds);\n observer?.disconnect();\n };\n }, [updateBounds]);\n\n React.useEffect(() => {\n isLoadingRef.current = true;\n\n const scheduleNextCross = () => {\n const delay = 400 + randomFloat() * 300;\n return setTimeout(() => {\n spawnCross();\n if (isLoadingRef.current) {\n scheduleNextCross();\n }\n }, delay);\n };\n\n const timeoutId = scheduleNextCross();\n return () => {\n clearTimeout(timeoutId);\n isLoadingRef.current = false;\n };\n }, [spawnCross]);\n\n React.useEffect(() => {\n if (!audioFeedback) {\n return undefined;\n }\n\n const enableAudio = async () => {\n const AudioContextClass =\n window.AudioContext || (window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AudioContextClass) {\n return;\n }\n\n try {\n const ctx = audioCtxRef.current ?? new AudioContextClass();\n audioCtxRef.current = ctx;\n if (ctx.state === \"suspended\") {\n await ctx.resume();\n }\n audioEnabledRef.current = ctx.state === \"running\";\n } catch {\n audioEnabledRef.current = false;\n }\n };\n\n window.addEventListener(\"pointerdown\", enableAudio, {\n once: true,\n passive: true,\n });\n window.addEventListener(\"keydown\", enableAudio, { once: true });\n\n return () => {\n window.removeEventListener(\"pointerdown\", enableAudio);\n window.removeEventListener(\"keydown\", enableAudio);\n if (audioCtxRef.current) {\n void audioCtxRef.current.close().catch(() => undefined);\n audioCtxRef.current = null;\n }\n audioEnabledRef.current = false;\n };\n }, [audioFeedback]);\n\n React.useEffect(() => {\n if (!audioFeedback) {\n return undefined;\n }\n\n const playBeep = () => {\n const audioCtx = audioCtxRef.current;\n if (!audioCtx || !audioEnabledRef.current) {\n return;\n }\n\n const osc = audioCtx.createOscillator();\n const gain = audioCtx.createGain();\n osc.type = \"triangle\";\n osc.frequency.value = 523.25;\n gain.gain.setValueAtTime(0.15, audioCtx.currentTime);\n gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.08);\n osc.connect(gain);\n gain.connect(audioCtx.destination);\n osc.start();\n osc.stop(audioCtx.currentTime + 0.08);\n };\n\n const filledBars = Math.round(\n (displayProgress / 100) * LOADING_PROGRESS_SEGMENT_COUNT,\n );\n if (filledBars > prevFilledBars.current) {\n playBeep();\n prevFilledBars.current = filledBars;\n }\n\n return undefined;\n }, [audioFeedback, displayProgress]);\n\n React.useEffect(() => {\n const ellipsisInterval = setInterval(() => {\n setEllipsisCount((count) => (count + 1) % 4);\n }, 400);\n return () => clearInterval(ellipsisInterval);\n }, []);\n\n React.useEffect(() => {\n const timeout = setTimeout(() => {\n setDisplayProgress(progress);\n }, 50);\n return () => clearTimeout(timeout);\n }, [progress]);\n\n const ellipsis = \".\".repeat(ellipsisCount);\n const cleanMessage = message ? message.replace(/\\.+$/, \"\") : \"\";\n const isLightMode = resolveIsLightMode(themeMode);\n const screenWidth =\n bounds.width || (typeof window === \"undefined\" ? 0 : window.innerWidth);\n const screenHeight =\n bounds.height || (typeof window === \"undefined\" ? 0 : window.innerHeight);\n\n return (\n <div\n ref={containerRef}\n className={`${styles.screen} ${fullscreen ? styles.fullscreen : styles.contained} ${\n isLightMode ? styles.lightTheme : styles.darkTheme\n }`.trim()}\n style={\n {\n \"--screen-height\": `${screenHeight}px`,\n \"--screen-width\": `${screenWidth}px`,\n } as React.CSSProperties\n }\n >\n {crosses.map((cross) => (\n <div\n key={cross.id}\n className={styles.crossContainer}\n style={\n {\n \"--cross-left\": `${cross.x}px`,\n \"--cross-delay\": `${cross.delay}ms`,\n \"--rotation\": `${cross.rotation}deg`,\n \"--ground\": `${cross.groundOffset}%`,\n \"--cross-scale\": String(cross.scale),\n \"--cross-opacity\": String(cross.opacity),\n } as React.CSSProperties\n }\n onAnimationEnd={(event) => {\n if (event.animationName === \"crossFall\") {\n event.currentTarget.classList.add(styles.crossLanded);\n }\n }}\n >\n <div className={styles.crossSvg}>\n <LightCrossIcon className={styles.crossShape} />\n </div>\n </div>\n ))}\n\n <div className={styles.centerStack}>\n {logo ? <div className={styles.logoWrap}>{logo}</div> : null}\n <div className={styles.title}>{title}</div>\n </div>\n\n <LoadingProgressBar progress={displayProgress} className={styles.progressRow} />\n\n <div className={styles.footerMessage}>\n {cleanMessage}\n {ellipsis}\n </div>\n </div>\n );\n}\n\nexport type LoadingStage = \"init\" | \"auth\" | \"agent\" | \"config\" | \"ready\";\n\nconst STAGE_PROGRESS: Record<LoadingStage, number> = {\n init: 0,\n auth: 25,\n agent: 50,\n config: 75,\n ready: 100,\n};\n\nconst STAGE_MESSAGES: Record<LoadingStage, string> = {\n init: \"Initializing...\",\n auth: \"Authenticating...\",\n agent: \"Loading agent...\",\n config: \"Loading configuration...\",\n ready: \"Preparing interface...\",\n};\n\nexport function useLoadingProgress(\n isLoading: boolean,\n _phase?: string,\n stage: LoadingStage = \"init\",\n) {\n const [progress, setProgress] = React.useState(0);\n const [currentMessage, setCurrentMessage] = React.useState(\"Initializing...\");\n const progressRef = React.useRef(0);\n\n React.useEffect(() => {\n progressRef.current = progress;\n }, [progress]);\n\n React.useEffect(() => {\n if (!isLoading) {\n setProgress(100);\n progressRef.current = 100;\n setCurrentMessage(\"Complete!\");\n return undefined;\n }\n\n const targetProgress = STAGE_PROGRESS[stage];\n const targetMessage = STAGE_MESSAGES[stage];\n setCurrentMessage(targetMessage);\n\n const currentProgressSnapshot = progressRef.current;\n const diff = targetProgress - currentProgressSnapshot;\n const steps = 20;\n const stepDuration = 100;\n let step = 0;\n\n const interval = setInterval(() => {\n step += 1;\n const easedStep = step / steps;\n const easedProgress = diff * easedStep;\n const nextProgress = Math.min(\n currentProgressSnapshot + easedProgress,\n targetProgress,\n );\n progressRef.current = nextProgress;\n setProgress(nextProgress);\n\n if (step >= steps) {\n clearInterval(interval);\n }\n }, stepDuration);\n\n return () => clearInterval(interval);\n }, [isLoading, stage]);\n\n return { progress, message: currentMessage };\n}\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
.screen {
|
|
2
|
+
position: fixed;
|
|
3
|
+
inset: 0;
|
|
4
|
+
z-index: 9999;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
padding: 20px;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
font-family: var(--font-primary);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.fullscreen {
|
|
15
|
+
position: fixed;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.contained {
|
|
19
|
+
position: absolute;
|
|
20
|
+
border: 1px solid var(--border-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.lightTheme {
|
|
24
|
+
background: #fafbfc;
|
|
25
|
+
color: #1a1a1a;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.darkTheme {
|
|
29
|
+
background: #0a0a0a;
|
|
30
|
+
color: #cbcbcc;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@keyframes crossFall {
|
|
34
|
+
0% {
|
|
35
|
+
transform: translateY(-450px) rotate(var(--rotation));
|
|
36
|
+
opacity: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
10% {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
100% {
|
|
44
|
+
transform: translateY(calc(var(--screen-height, 100vh) - var(--ground) - 50px))
|
|
45
|
+
rotate(var(--rotation));
|
|
46
|
+
opacity: 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.crossContainer {
|
|
51
|
+
position: absolute;
|
|
52
|
+
top: 0;
|
|
53
|
+
left: var(--cross-left, 0px);
|
|
54
|
+
z-index: 1;
|
|
55
|
+
will-change: transform;
|
|
56
|
+
animation: crossFall 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
|
57
|
+
animation-delay: var(--cross-delay, 0ms);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.crossSvg {
|
|
61
|
+
transform: scale(var(--cross-scale, 1));
|
|
62
|
+
transform-origin: center top;
|
|
63
|
+
opacity: var(--cross-opacity, 0.6);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.crossLanded .crossSvg {
|
|
67
|
+
opacity: 1 !important;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.crossShape {
|
|
71
|
+
color: currentColor;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.centerStack {
|
|
75
|
+
position: relative;
|
|
76
|
+
z-index: 10;
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 24px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.logoWrap {
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.title {
|
|
90
|
+
font-size: clamp(24px, 8vw, 48px);
|
|
91
|
+
font-weight: 700;
|
|
92
|
+
letter-spacing: 8px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.progressRow {
|
|
96
|
+
position: relative;
|
|
97
|
+
z-index: 10;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.footerMessage {
|
|
101
|
+
position: absolute;
|
|
102
|
+
right: 40px;
|
|
103
|
+
bottom: 40px;
|
|
104
|
+
z-index: 10;
|
|
105
|
+
opacity: 0.5;
|
|
106
|
+
font-family: var(--font-mono);
|
|
107
|
+
font-size: 14px;
|
|
108
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface OverlayButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
3
|
+
active?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function OverlayButton({ active, className, ...props }: OverlayButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default OverlayButton;
|
|
7
|
+
//# sourceMappingURL=OverlayButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverlayButton.d.ts","sourceRoot":"","sources":["../../src/components/OverlayButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,kBACf,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,SAAc,EACd,GAAG,KAAK,EACT,EAAE,kBAAkB,2CAKpB;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import styles from "./OverlayButton.module.css";
|
|
3
|
+
export function OverlayButton({ active, className = "", ...props }) {
|
|
4
|
+
const classes = `${styles.overlayBtn} ${active ? styles.overlayBtnActive : ""} ${className}`.trim();
|
|
5
|
+
return _jsx("button", { type: "button", className: classes, ...props });
|
|
6
|
+
}
|
|
7
|
+
export default OverlayButton;
|
|
8
|
+
//# sourceMappingURL=OverlayButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverlayButton.js","sourceRoot":"","sources":["../../src/components/OverlayButton.tsx"],"names":[],"mappings":";AAEA,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAOhD,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,SAAS,GAAG,EAAE,EACd,GAAG,KAAK,EACW;IACnB,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,UAAU,IAClC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,EACrC,IAAI,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC;IACvB,OAAO,iBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,OAAO,KAAM,KAAK,GAAI,CAAC;AACjE,CAAC;AAED,eAAe,aAAa,CAAC","sourcesContent":["import React from \"react\";\n\nimport styles from \"./OverlayButton.module.css\";\n\nexport interface OverlayButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean;\n}\n\nexport function OverlayButton({\n active,\n className = \"\",\n ...props\n}: OverlayButtonProps) {\n const classes = `${styles.overlayBtn} ${\n active ? styles.overlayBtnActive : \"\"\n } ${className}`.trim();\n return <button type=\"button\" className={classes} {...props} />;\n}\n\nexport default OverlayButton;\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.overlayBtn {
|
|
2
|
+
width: var(--overlay-btn-size, 2rem);
|
|
3
|
+
height: var(--overlay-btn-size, 2rem);
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
border: 1px solid var(--border-color);
|
|
8
|
+
border-radius: var(--overlay-btn-radius, 0.5rem);
|
|
9
|
+
background: color-mix(in srgb, var(--bg-primary) 72%, transparent);
|
|
10
|
+
backdrop-filter: blur(2px);
|
|
11
|
+
color: var(--text-secondary);
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
opacity: 0.8;
|
|
14
|
+
transition:
|
|
15
|
+
opacity 150ms ease,
|
|
16
|
+
background-color 150ms ease,
|
|
17
|
+
color 150ms ease,
|
|
18
|
+
border-color 150ms ease;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.overlayBtn:hover {
|
|
22
|
+
background: color-mix(in srgb, var(--bg-primary) 92%, transparent);
|
|
23
|
+
color: var(--text-primary);
|
|
24
|
+
opacity: 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.overlayBtnActive,
|
|
28
|
+
.overlayBtnActive:hover {
|
|
29
|
+
background: var(--text-primary);
|
|
30
|
+
border-color: var(--text-primary);
|
|
31
|
+
color: var(--bg-primary);
|
|
32
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface PixelTooltipProps {
|
|
3
|
+
content: string;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
6
|
+
delay?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function PixelTooltip({ content, children, position, delay, }: PixelTooltipProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default PixelTooltip;
|
|
10
|
+
//# sourceMappingURL=PixelTooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PixelTooltip.d.ts","sourceRoot":"","sources":["../../src/components/PixelTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,QAAQ,EACR,QAAgB,EAChB,KAAW,GACZ,EAAE,iBAAiB,2CAgEnB;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import styles from "./PixelTooltip.module.css";
|
|
4
|
+
export function PixelTooltip({ content, children, position = "top", delay = 200, }) {
|
|
5
|
+
const [isVisible, setIsVisible] = React.useState(false);
|
|
6
|
+
const [coords, setCoords] = React.useState({ x: 0, y: 0, width: 0 });
|
|
7
|
+
const timeoutRef = React.useRef(null);
|
|
8
|
+
const triggerRef = React.useRef(null);
|
|
9
|
+
const handleMouseEnter = React.useCallback(() => {
|
|
10
|
+
timeoutRef.current = setTimeout(() => {
|
|
11
|
+
if (triggerRef.current) {
|
|
12
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
13
|
+
setCoords({
|
|
14
|
+
x: rect.left,
|
|
15
|
+
y: rect.bottom,
|
|
16
|
+
width: rect.width,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
setIsVisible(true);
|
|
20
|
+
}, delay);
|
|
21
|
+
}, [delay]);
|
|
22
|
+
const handleMouseLeave = React.useCallback(() => {
|
|
23
|
+
if (timeoutRef.current) {
|
|
24
|
+
clearTimeout(timeoutRef.current);
|
|
25
|
+
timeoutRef.current = null;
|
|
26
|
+
}
|
|
27
|
+
setIsVisible(false);
|
|
28
|
+
}, []);
|
|
29
|
+
React.useEffect(() => () => {
|
|
30
|
+
if (timeoutRef.current) {
|
|
31
|
+
clearTimeout(timeoutRef.current);
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, className: styles.trigger, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: children }), isVisible ? (_jsx("div", { className: `${styles.tooltip} ${styles[position]} ${styles.show}`.trim(), style: {
|
|
35
|
+
left: `${coords.x + 5}px`,
|
|
36
|
+
top: `${coords.y + 8}px`,
|
|
37
|
+
width: `${Math.max(coords.width - 20, 60)}px`,
|
|
38
|
+
}, children: _jsx("div", { className: styles.pixelContainer, children: _jsx("div", { className: styles.content, children: content }) }) })) : null] }));
|
|
39
|
+
}
|
|
40
|
+
export default PixelTooltip;
|
|
41
|
+
//# sourceMappingURL=PixelTooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PixelTooltip.js","sourceRoot":"","sources":["../../src/components/PixelTooltip.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAS/C,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,EACP,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,GAAG,GACO;IAClB,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAuC,IAAI,CAAC,CAAC;IAC5E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAiB,IAAI,CAAC,CAAC;IAEtD,MAAM,gBAAgB,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC9C,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC;gBACxD,SAAS,CAAC;oBACR,CAAC,EAAE,IAAI,CAAC,IAAI;oBACZ,CAAC,EAAE,IAAI,CAAC,MAAM;oBACd,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;YACD,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,gBAAgB,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC9C,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACjC,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC;QAC5B,CAAC;QACD,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,KAAK,CAAC,SAAS,CACb,GAAG,EAAE,CAAC,GAAG,EAAE;QACT,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,OAAO,CACL,8BACE,cACE,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,CAAC,OAAO,EACzB,YAAY,EAAE,gBAAgB,EAC9B,YAAY,EAAE,gBAAgB,YAE7B,QAAQ,GACL,EAEL,SAAS,CAAC,CAAC,CAAC,CACX,cACE,SAAS,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EACxE,KAAK,EAAE;oBACL,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI;oBACzB,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI;oBACxB,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,IAAI;iBAC9C,YAED,cAAK,SAAS,EAAE,MAAM,CAAC,cAAc,YACnC,cAAK,SAAS,EAAE,MAAM,CAAC,OAAO,YAAG,OAAO,GAAO,GAC3C,GACF,CACP,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAC;AACJ,CAAC;AAED,eAAe,YAAY,CAAC","sourcesContent":["import React from \"react\";\n\nimport styles from \"./PixelTooltip.module.css\";\n\nexport interface PixelTooltipProps {\n content: string;\n children: React.ReactNode;\n position?: \"top\" | \"bottom\" | \"left\" | \"right\";\n delay?: number;\n}\n\nexport function PixelTooltip({\n content,\n children,\n position = \"top\",\n delay = 200,\n}: PixelTooltipProps) {\n const [isVisible, setIsVisible] = React.useState(false);\n const [coords, setCoords] = React.useState({ x: 0, y: 0, width: 0 });\n const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const triggerRef = React.useRef<HTMLDivElement>(null);\n\n const handleMouseEnter = React.useCallback(() => {\n timeoutRef.current = setTimeout(() => {\n if (triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect();\n setCoords({\n x: rect.left,\n y: rect.bottom,\n width: rect.width,\n });\n }\n setIsVisible(true);\n }, delay);\n }, [delay]);\n\n const handleMouseLeave = React.useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n setIsVisible(false);\n }, []);\n\n React.useEffect(\n () => () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n },\n [],\n );\n\n return (\n <>\n <div\n ref={triggerRef}\n className={styles.trigger}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {children}\n </div>\n\n {isVisible ? (\n <div\n className={`${styles.tooltip} ${styles[position]} ${styles.show}`.trim()}\n style={{\n left: `${coords.x + 5}px`,\n top: `${coords.y + 8}px`,\n width: `${Math.max(coords.width - 20, 60)}px`,\n }}\n >\n <div className={styles.pixelContainer}>\n <div className={styles.content}>{content}</div>\n </div>\n </div>\n ) : null}\n </>\n );\n}\n\nexport default PixelTooltip;\n"]}
|