@hunterchen/canvas 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -8
- package/dist/components/canvas/backgrounds.d.ts +4 -4
- package/dist/components/canvas/backgrounds.d.ts.map +1 -1
- package/dist/components/canvas/backgrounds.js +68 -39
- package/dist/components/canvas/backgrounds.js.map +1 -1
- package/dist/components/canvas/canvas.d.ts +3 -1
- package/dist/components/canvas/canvas.d.ts.map +1 -1
- package/dist/components/canvas/canvas.js +451 -387
- package/dist/components/canvas/canvas.js.map +1 -1
- package/dist/components/canvas/component.js +108 -174
- package/dist/components/canvas/component.js.map +1 -1
- package/dist/components/canvas/draggable.js +168 -151
- package/dist/components/canvas/draggable.js.map +1 -1
- package/dist/components/canvas/navbar/index.d.ts +4 -2
- package/dist/components/canvas/navbar/index.d.ts.map +1 -1
- package/dist/components/canvas/navbar/index.js +168 -101
- package/dist/components/canvas/navbar/index.js.map +1 -1
- package/dist/components/canvas/navbar/single-button.d.ts +10 -1
- package/dist/components/canvas/navbar/single-button.d.ts.map +1 -1
- package/dist/components/canvas/navbar/single-button.js +176 -69
- package/dist/components/canvas/navbar/single-button.js.map +1 -1
- package/dist/components/canvas/toolbar.js +121 -82
- package/dist/components/canvas/toolbar.js.map +1 -1
- package/dist/components/canvas/wrapper.js +127 -99
- package/dist/components/canvas/wrapper.js.map +1 -1
- package/dist/contexts/CanvasContext.js +25 -17
- package/dist/contexts/CanvasContext.js.map +1 -1
- package/dist/contexts/PerformanceContext.js +51 -50
- package/dist/contexts/PerformanceContext.js.map +1 -1
- package/dist/hooks/usePerformanceMode.js +36 -37
- package/dist/hooks/usePerformanceMode.js.map +1 -1
- package/dist/hooks/useWindowDimensions.js +22 -18
- package/dist/hooks/useWindowDimensions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -21
- package/dist/lib/canvas.js +65 -72
- package/dist/lib/canvas.js.map +1 -1
- package/dist/lib/constants.js +78 -92
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/utils.js +10 -5
- package/dist/lib/utils.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/index.d.ts +69 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/performance.js +18 -23
- package/dist/utils/performance.js.map +1 -1
- package/package.json +7 -21
- package/src/components/canvas/backgrounds.tsx +7 -7
- package/src/components/canvas/canvas.tsx +9 -2
- package/src/components/canvas/navbar/index.tsx +91 -15
- package/src/components/canvas/navbar/single-button.tsx +210 -56
- package/src/index.ts +5 -0
- package/src/styles.css +15 -13
- package/src/types/index.ts +91 -0
- package/dist/components/canvas/offest.js +0 -12
- package/dist/components/canvas/offest.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types/index.js +0 -6
- package/dist/types/index.js.map +0 -1
|
@@ -1,104 +1,171 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCanvasContext } from "../../../contexts/CanvasContext.js";
|
|
2
|
+
import { NAVBAR_DEBOUNCE_MS, RESPONSIVE_ZOOM_MAP } from "../../../lib/constants.js";
|
|
3
|
+
import { getScreenSizeEnum, getSectionPanCoordinates } from "../../../lib/canvas.js";
|
|
4
|
+
import useWindowDimensions_default from "../../../hooks/useWindowDimensions.js";
|
|
5
|
+
import { cn } from "../../../lib/utils.js";
|
|
6
|
+
import SingleButton from "./single-button.js";
|
|
7
|
+
import { usePerformanceMode } from "../../../hooks/usePerformanceMode.js";
|
|
2
8
|
import { motion, useMotionValueEvent } from "framer-motion";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
9
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
|
|
12
|
+
//#region src/components/canvas/navbar/index.tsx
|
|
13
|
+
const positionStyles = {
|
|
14
|
+
top: {
|
|
15
|
+
top: "1rem",
|
|
16
|
+
bottom: "auto",
|
|
17
|
+
left: "50%",
|
|
18
|
+
transform: "translateX(-50%)"
|
|
19
|
+
},
|
|
20
|
+
bottom: {
|
|
21
|
+
bottom: "1rem",
|
|
22
|
+
top: "auto",
|
|
23
|
+
left: "50%",
|
|
24
|
+
transform: "translateX(-50%)"
|
|
25
|
+
},
|
|
26
|
+
left: {
|
|
27
|
+
left: "1rem",
|
|
28
|
+
right: "auto",
|
|
29
|
+
top: "50%",
|
|
30
|
+
transform: "translateY(-50%)"
|
|
31
|
+
},
|
|
32
|
+
right: {
|
|
33
|
+
right: "1rem",
|
|
34
|
+
left: "auto",
|
|
35
|
+
top: "50%",
|
|
36
|
+
transform: "translateY(-50%)"
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const responsivePositionClasses = {
|
|
40
|
+
top: "top-12 md:top-4",
|
|
41
|
+
bottom: "bottom-12 md:bottom-4",
|
|
42
|
+
left: "left-4",
|
|
43
|
+
right: "right-4"
|
|
44
|
+
};
|
|
45
|
+
function Navbar({ panToOffset, onReset, items, config = {} }) {
|
|
46
|
+
const { x, y, scale, animationStage, setNextTargetSection } = useCanvasContext();
|
|
47
|
+
const [expandedButton, setExpandedButton] = useState(null);
|
|
48
|
+
const activePans = useRef(0);
|
|
49
|
+
const panTimeout = useRef(null);
|
|
50
|
+
const debounceBlocked = useRef(false);
|
|
51
|
+
const debounceCooldownTimeout = useRef(null);
|
|
52
|
+
const { height, width } = useWindowDimensions_default();
|
|
53
|
+
const { mode } = usePerformanceMode();
|
|
54
|
+
const defaultZoom = RESPONSIVE_ZOOM_MAP[getScreenSizeEnum(width)];
|
|
55
|
+
const debounceMs = NAVBAR_DEBOUNCE_MS[mode] ?? 0;
|
|
56
|
+
const { display = "icons", position = "bottom", className, style, buttonConfig, tooltipConfig, gap, padding } = config;
|
|
57
|
+
const isVertical = position === "left" || position === "right";
|
|
58
|
+
const homeItem = useMemo(() => items.find((item) => item.isHome), [items]);
|
|
59
|
+
const handleDebouncedClick = useCallback((callback) => {
|
|
60
|
+
if (debounceMs === 0) {
|
|
61
|
+
callback();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (debounceBlocked.current) return;
|
|
65
|
+
debounceBlocked.current = true;
|
|
66
|
+
callback();
|
|
67
|
+
if (debounceCooldownTimeout.current) clearTimeout(debounceCooldownTimeout.current);
|
|
68
|
+
debounceCooldownTimeout.current = setTimeout(() => {
|
|
69
|
+
debounceBlocked.current = false;
|
|
70
|
+
debounceCooldownTimeout.current = null;
|
|
71
|
+
}, debounceMs);
|
|
72
|
+
}, [debounceMs]);
|
|
73
|
+
const updateExpandedButton = () => {
|
|
74
|
+
if (panTimeout.current) clearTimeout(panTimeout.current);
|
|
75
|
+
panTimeout.current = setTimeout(() => {
|
|
76
|
+
activePans.current = 0;
|
|
77
|
+
}, 500);
|
|
78
|
+
if (activePans.current == 0) setExpandedButton(null);
|
|
79
|
+
};
|
|
80
|
+
useMotionValueEvent(x, "change", updateExpandedButton);
|
|
81
|
+
useMotionValueEvent(y, "change", updateExpandedButton);
|
|
82
|
+
useMotionValueEvent(scale, "change", updateExpandedButton);
|
|
83
|
+
const handlePan = useCallback(function handlePan(item) {
|
|
84
|
+
setExpandedButton(item.id);
|
|
85
|
+
activePans.current++;
|
|
86
|
+
setNextTargetSection(item.id);
|
|
87
|
+
if (item.isHome) {
|
|
88
|
+
onReset();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
panToOffset(getSectionPanCoordinates({
|
|
92
|
+
windowDimensions: {
|
|
93
|
+
width,
|
|
94
|
+
height
|
|
95
|
+
},
|
|
96
|
+
coords: {
|
|
97
|
+
x: item.x,
|
|
98
|
+
y: item.y,
|
|
99
|
+
width: item.width,
|
|
100
|
+
height: item.height
|
|
101
|
+
},
|
|
102
|
+
targetZoom: defaultZoom,
|
|
103
|
+
negative: true
|
|
104
|
+
}), () => {
|
|
105
|
+
activePans.current--;
|
|
106
|
+
}, defaultZoom);
|
|
107
|
+
}, [
|
|
108
|
+
panToOffset,
|
|
109
|
+
onReset,
|
|
110
|
+
width,
|
|
111
|
+
height,
|
|
112
|
+
defaultZoom,
|
|
113
|
+
setNextTargetSection
|
|
114
|
+
]);
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (animationStage < 2) return;
|
|
117
|
+
if (homeItem) handlePan(homeItem);
|
|
118
|
+
return () => {
|
|
119
|
+
if (panTimeout.current) clearTimeout(panTimeout.current);
|
|
120
|
+
if (debounceCooldownTimeout.current) clearTimeout(debounceCooldownTimeout.current);
|
|
121
|
+
};
|
|
122
|
+
}, [
|
|
123
|
+
handlePan,
|
|
124
|
+
animationStage,
|
|
125
|
+
homeItem
|
|
126
|
+
]);
|
|
127
|
+
const containerStyle = {
|
|
128
|
+
position: "fixed",
|
|
129
|
+
zIndex: 1e3,
|
|
130
|
+
pointerEvents: "auto",
|
|
131
|
+
display: "flex",
|
|
132
|
+
justifyContent: "center",
|
|
133
|
+
alignItems: "center",
|
|
134
|
+
...positionStyles[position]
|
|
135
|
+
};
|
|
136
|
+
const innerStyle = {
|
|
137
|
+
...gap !== void 0 && { gap: `${gap}px` },
|
|
138
|
+
...padding !== void 0 && { padding: `${padding}px` },
|
|
139
|
+
...isVertical && { flexDirection: "column" },
|
|
140
|
+
...style
|
|
141
|
+
};
|
|
142
|
+
return /* @__PURE__ */ jsx("div", {
|
|
143
|
+
className: responsivePositionClasses[position],
|
|
144
|
+
style: containerStyle,
|
|
145
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
146
|
+
className: isVertical ? "py-4 md:py-8" : "px-4 md:px-8",
|
|
147
|
+
children: /* @__PURE__ */ jsx(motion.div, {
|
|
148
|
+
className: cn("flex select-none items-center justify-center gap-1 rounded-[10px] border p-1 shadow-[0_6px_12px_rgba(0,0,0,0.08)]", !style?.backgroundColor && "bg-white", !style?.borderColor && "border-neutral-200", isVertical && "flex-col", className),
|
|
149
|
+
style: innerStyle,
|
|
150
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
151
|
+
className: cn("flex items-center gap-1", isVertical && "flex-col"),
|
|
152
|
+
children: items.map((item) => /* @__PURE__ */ jsx(SingleButton, {
|
|
153
|
+
label: item.label,
|
|
154
|
+
icon: item.icon,
|
|
155
|
+
onClick: () => handlePan(item),
|
|
156
|
+
isPushed: expandedButton === item.id,
|
|
157
|
+
onDebouncedClick: handleDebouncedClick,
|
|
158
|
+
displayMode: display,
|
|
159
|
+
buttonConfig,
|
|
160
|
+
tooltipConfig,
|
|
161
|
+
isVertical
|
|
162
|
+
}, item.id))
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
});
|
|
103
167
|
}
|
|
168
|
+
|
|
169
|
+
//#endregion
|
|
170
|
+
export { Navbar as default };
|
|
104
171
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/canvas/navbar/index.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["useWindowDimensions"],"sources":["../../../../src/components/canvas/navbar/index.tsx"],"sourcesContent":["import { motion, useMotionValueEvent } from \"framer-motion\";\nimport { useState, useRef, useEffect, useCallback, useMemo } from \"react\";\nimport SingleButton from \"./single-button\";\nimport type { NavItem, NavbarConfig, NavbarPosition } from \"../../../types\";\nimport { useCanvasContext } from \"../../../contexts/CanvasContext\";\nimport useWindowDimensions from \"../../../hooks/useWindowDimensions\";\nimport { usePerformanceMode } from \"../../../hooks/usePerformanceMode\";\nimport {\n getScreenSizeEnum,\n getSectionPanCoordinates,\n} from \"../../../lib/canvas\";\nimport {\n RESPONSIVE_ZOOM_MAP,\n NAVBAR_DEBOUNCE_MS,\n} from \"../../../lib/constants\";\nimport { cn } from \"../../../lib/utils\";\n\ninterface NavbarProps {\n panToOffset: (\n offset: { x: number; y: number },\n onComplete?: () => void,\n zoom?: number,\n ) => void;\n onReset: () => void;\n /** Array of navigation items defining sections, their icons, and coordinates */\n items: NavItem[];\n /** Navbar configuration options */\n config?: NavbarConfig;\n}\n\nconst positionStyles: Record<NavbarPosition, React.CSSProperties> = {\n top: {\n top: \"1rem\",\n bottom: \"auto\",\n left: \"50%\",\n transform: \"translateX(-50%)\",\n },\n bottom: {\n bottom: \"1rem\",\n top: \"auto\",\n left: \"50%\",\n transform: \"translateX(-50%)\",\n },\n left: {\n left: \"1rem\",\n right: \"auto\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n },\n right: {\n right: \"1rem\",\n left: \"auto\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n },\n};\n\n// Responsive position adjustments (mobile vs desktop)\nconst responsivePositionClasses: Record<NavbarPosition, string> = {\n top: \"top-12 md:top-4\",\n bottom: \"bottom-12 md:bottom-4\",\n left: \"left-4\",\n right: \"right-4\",\n};\n\nexport default function Navbar({\n panToOffset,\n onReset,\n items,\n config = {},\n}: NavbarProps) {\n const { x, y, scale, animationStage, setNextTargetSection } =\n useCanvasContext();\n const [expandedButton, setExpandedButton] = useState<string | null>(null);\n const activePans = useRef(0);\n const panTimeout = useRef<NodeJS.Timeout | null>(null);\n\n // Debounce state\n const debounceBlocked = useRef(false);\n const debounceCooldownTimeout = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n const { height, width } = useWindowDimensions();\n const { mode } = usePerformanceMode();\n\n const defaultZoom = RESPONSIVE_ZOOM_MAP[getScreenSizeEnum(width)];\n\n // Derive debounce duration from performance mode\n const debounceMs = NAVBAR_DEBOUNCE_MS[mode] ?? 0;\n\n // Extract config values\n const {\n display = \"icons\",\n position = \"bottom\",\n className,\n style,\n buttonConfig,\n tooltipConfig,\n gap,\n padding,\n } = config;\n\n const isVertical = position === \"left\" || position === \"right\";\n\n // Find the home section from items\n const homeItem = useMemo(() => items.find((item) => item.isHome), [items]);\n\n // Leading-edge debounce handler\n const handleDebouncedClick = useCallback(\n (callback: () => void) => {\n if (debounceMs === 0) {\n callback();\n return;\n }\n\n if (debounceBlocked.current) {\n // We're in the cooldown window; ignore this click\n return;\n }\n\n // Enter cooldown and perform the click immediately\n debounceBlocked.current = true;\n callback();\n\n if (debounceCooldownTimeout.current) {\n clearTimeout(debounceCooldownTimeout.current);\n }\n\n debounceCooldownTimeout.current = setTimeout(() => {\n debounceBlocked.current = false;\n debounceCooldownTimeout.current = null;\n }, debounceMs);\n },\n [debounceMs],\n );\n\n const updateExpandedButton = () => {\n // reset activePans if no movement has occurred recently\n if (panTimeout.current) clearTimeout(panTimeout.current);\n panTimeout.current = setTimeout(() => {\n activePans.current = 0;\n }, 500);\n\n if (activePans.current == 0) setExpandedButton(null);\n };\n\n useMotionValueEvent(x, \"change\", updateExpandedButton);\n useMotionValueEvent(y, \"change\", updateExpandedButton);\n useMotionValueEvent(scale, \"change\", updateExpandedButton);\n\n const handlePan = useCallback(\n function handlePan(item: NavItem) {\n setExpandedButton(item.id);\n activePans.current++;\n\n // Predictive pre-render hint: mark the target section so its CanvasComponent can\n // render even before it comes fully into view.\n setNextTargetSection(item.id);\n\n if (item.isHome) {\n onReset();\n return;\n }\n\n const panCoords = getSectionPanCoordinates({\n windowDimensions: { width, height },\n coords: { x: item.x, y: item.y, width: item.width, height: item.height },\n targetZoom: defaultZoom,\n negative: true,\n });\n\n panToOffset(\n panCoords,\n () => {\n activePans.current--;\n },\n defaultZoom,\n );\n },\n [panToOffset, onReset, width, height, defaultZoom, setNextTargetSection],\n );\n\n // Clean up timers on unmount and pan to home on animation complete\n useEffect(() => {\n if (animationStage < 2) return;\n if (homeItem) {\n handlePan(homeItem);\n }\n return () => {\n if (panTimeout.current) clearTimeout(panTimeout.current);\n if (debounceCooldownTimeout.current)\n clearTimeout(debounceCooldownTimeout.current);\n };\n }, [handlePan, animationStage, homeItem]);\n\n // Compute container styles (positioning only)\n const containerStyle: React.CSSProperties = {\n position: \"fixed\",\n zIndex: 1000,\n pointerEvents: \"auto\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n ...positionStyles[position],\n };\n\n // Compute inner container styles (visual styling)\n const innerStyle: React.CSSProperties = {\n ...(gap !== undefined && { gap: `${gap}px` }),\n ...(padding !== undefined && { padding: `${padding}px` }),\n ...(isVertical && { flexDirection: \"column\" }),\n ...style,\n };\n\n return (\n <div\n className={responsivePositionClasses[position]}\n style={containerStyle}\n >\n {/* padding to prevent edge bug */}\n <div className={isVertical ? \"py-4 md:py-8\" : \"px-4 md:px-8\"}>\n <motion.div\n className={cn(\n \"flex select-none items-center justify-center gap-1 rounded-[10px] border p-1 shadow-[0_6px_12px_rgba(0,0,0,0.08)]\",\n !style?.backgroundColor && \"bg-white\",\n !style?.borderColor && \"border-neutral-200\",\n isVertical && \"flex-col\",\n className,\n )}\n style={innerStyle}\n >\n <div className={cn(\"flex items-center gap-1\", isVertical && \"flex-col\")}>\n {items.map((item) => (\n <SingleButton\n key={item.id}\n label={item.label}\n icon={item.icon}\n onClick={() => handlePan(item)}\n isPushed={expandedButton === item.id}\n onDebouncedClick={handleDebouncedClick}\n displayMode={display}\n buttonConfig={buttonConfig}\n tooltipConfig={tooltipConfig}\n isVertical={isVertical}\n />\n ))}\n </div>\n </motion.div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NavbarDisplayMode, NavbarButtonConfig, NavbarTooltipConfig } from "../../../types";
|
|
1
2
|
interface SingleButtonProps {
|
|
2
3
|
label: string;
|
|
3
4
|
/** Lucide icon name or a custom icon component */
|
|
@@ -8,7 +9,15 @@ interface SingleButtonProps {
|
|
|
8
9
|
isPushed: boolean;
|
|
9
10
|
link?: string;
|
|
10
11
|
onDebouncedClick?: (callback: () => void) => void;
|
|
12
|
+
/** Display mode for the button */
|
|
13
|
+
displayMode?: NavbarDisplayMode;
|
|
14
|
+
/** Button styling configuration */
|
|
15
|
+
buttonConfig?: NavbarButtonConfig;
|
|
16
|
+
/** Tooltip configuration */
|
|
17
|
+
tooltipConfig?: NavbarTooltipConfig;
|
|
18
|
+
/** Whether the navbar is in vertical layout */
|
|
19
|
+
isVertical?: boolean;
|
|
11
20
|
}
|
|
12
|
-
export default function SingleButton({ label, icon, onClick, isPushed, link, onDebouncedClick, }: SingleButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default function SingleButton({ label, icon, onClick, isPushed, link, onDebouncedClick, displayMode, buttonConfig, tooltipConfig, isVertical, }: SingleButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
13
22
|
export {};
|
|
14
23
|
//# sourceMappingURL=single-button.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single-button.d.ts","sourceRoot":"","sources":["../../../../src/components/canvas/navbar/single-button.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"single-button.d.ts","sourceRoot":"","sources":["../../../../src/components/canvas/navbar/single-button.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAGxB,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAClD,kCAAkC;IAClC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,mCAAmC;IACnC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,4BAA4B;IAC5B,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,+CAA+C;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,gBAAgB,EAChB,WAAqB,EACrB,YAAiB,EACjB,aAAkB,EAClB,UAAkB,GACnB,EAAE,iBAAiB,2CA4QnB"}
|
|
@@ -1,72 +1,179 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
3
|
-
import * as LucideIcons from "lucide-react";
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
2
|
import { AnimatePresence, motion } from "framer-motion";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import * as LucideIcons from "lucide-react";
|
|
6
|
+
|
|
7
|
+
//#region src/components/canvas/navbar/single-button.tsx
|
|
8
|
+
function SingleButton({ label, icon, onClick, isPushed, link, onDebouncedClick, displayMode = "icons", buttonConfig = {}, tooltipConfig = {}, isVertical = false }) {
|
|
9
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
10
|
+
const [showTag, setShowTag] = useState(false);
|
|
11
|
+
const [copiedEmail, setCopiedEmail] = useState(false);
|
|
12
|
+
const IconComponent = typeof icon === "string" ? LucideIcons[icon] : icon;
|
|
13
|
+
const { className: buttonClassName, style: buttonStyle, activeClassName, activeStyle, hoverClassName, hoverStyle, iconClassName, iconSize = 20, labelClassName, labelStyle } = buttonConfig;
|
|
14
|
+
const { disabled: tooltipDisabled = false, className: tooltipClassName, style: tooltipStyle, delay: tooltipDelay = 100 } = tooltipConfig;
|
|
15
|
+
const showIcon = displayMode !== "labels";
|
|
16
|
+
const allowExpand = displayMode === "icons";
|
|
17
|
+
const showTooltip = (displayMode === "icons" || displayMode === "compact") && !tooltipDisabled;
|
|
18
|
+
if (showIcon && !IconComponent) throw new Error("A valid 'icon' prop is required (Lucide icon name or custom icon component).");
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
let timeoutId;
|
|
21
|
+
if (isHovered && showTooltip) timeoutId = setTimeout(() => {
|
|
22
|
+
setShowTag(true);
|
|
23
|
+
}, tooltipDelay);
|
|
24
|
+
else setShowTag(false);
|
|
25
|
+
return () => {
|
|
26
|
+
clearTimeout(timeoutId);
|
|
27
|
+
};
|
|
28
|
+
}, [
|
|
29
|
+
isHovered,
|
|
30
|
+
showTooltip,
|
|
31
|
+
tooltipDelay
|
|
32
|
+
]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setShowTag(false);
|
|
35
|
+
setIsHovered(false);
|
|
36
|
+
}, [isPushed]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (copiedEmail) {
|
|
39
|
+
const timeoutId = setTimeout(() => {
|
|
40
|
+
setCopiedEmail(false);
|
|
41
|
+
}, 2e3);
|
|
42
|
+
return () => clearTimeout(timeoutId);
|
|
43
|
+
}
|
|
44
|
+
}, [copiedEmail]);
|
|
45
|
+
const performClick = () => {
|
|
46
|
+
if (link) {
|
|
47
|
+
window.open(link, "_blank", "noopener,noreferrer");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
onClick?.();
|
|
51
|
+
};
|
|
52
|
+
const handleClick = () => {
|
|
53
|
+
if (onDebouncedClick) onDebouncedClick(performClick);
|
|
54
|
+
else performClick();
|
|
55
|
+
};
|
|
56
|
+
const displayLabel = copiedEmail ? "Email copied!" : label;
|
|
57
|
+
const baseButtonClass = "relative flex items-center rounded-md p-2 text-neutral-500 transition-colors duration-200 focus:outline-none";
|
|
58
|
+
const stateClass = isPushed ? activeClassName || !activeStyle && "bg-neutral-200" : isHovered ? hoverClassName || !hoverStyle && "bg-neutral-100" : "";
|
|
59
|
+
const computedButtonStyle = {
|
|
60
|
+
...buttonStyle,
|
|
61
|
+
...isPushed && activeStyle,
|
|
62
|
+
...isHovered && !isPushed && hoverStyle
|
|
63
|
+
};
|
|
64
|
+
const iconSizeStyle = {
|
|
65
|
+
width: iconSize,
|
|
66
|
+
height: iconSize
|
|
67
|
+
};
|
|
68
|
+
const baseIconClass = "flex-shrink-0";
|
|
69
|
+
const iconColorClass = buttonStyle?.color ? "" : isPushed ? "text-neutral-700" : "text-neutral-500";
|
|
70
|
+
const baseLabelClass = "whitespace-nowrap font-canvas-figtree text-sm font-medium text-neutral-700";
|
|
71
|
+
const tooltipPositionClass = isVertical ? "left-full top-1/2 -translate-y-1/2 ml-2" : "-top-10 left-1/2";
|
|
72
|
+
const tooltipTransform = isVertical ? {
|
|
73
|
+
x: 0,
|
|
74
|
+
y: "-50%"
|
|
75
|
+
} : { x: "-50%" };
|
|
76
|
+
const renderIcon = () => {
|
|
77
|
+
if (!showIcon || !IconComponent) return null;
|
|
78
|
+
return /* @__PURE__ */ jsx(IconComponent, {
|
|
79
|
+
className: cn(baseIconClass, iconColorClass, iconClassName),
|
|
80
|
+
style: iconSizeStyle
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const renderLabel = (animated = false) => {
|
|
84
|
+
if (animated) return /* @__PURE__ */ jsx(motion.span, {
|
|
85
|
+
initial: {
|
|
86
|
+
opacity: 0,
|
|
87
|
+
width: 0
|
|
88
|
+
},
|
|
89
|
+
animate: {
|
|
90
|
+
opacity: 1,
|
|
91
|
+
width: "auto"
|
|
92
|
+
},
|
|
93
|
+
exit: {
|
|
94
|
+
opacity: 0,
|
|
95
|
+
width: 0
|
|
96
|
+
},
|
|
97
|
+
transition: {
|
|
98
|
+
duration: .1,
|
|
99
|
+
ease: "easeInOut"
|
|
100
|
+
},
|
|
101
|
+
className: cn("overflow-hidden", baseLabelClass, labelClassName),
|
|
102
|
+
style: labelStyle,
|
|
103
|
+
children: displayLabel
|
|
104
|
+
});
|
|
105
|
+
return /* @__PURE__ */ jsx("span", {
|
|
106
|
+
className: cn(baseLabelClass, labelClassName),
|
|
107
|
+
style: labelStyle,
|
|
108
|
+
children: displayLabel
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const renderTooltip = () => {
|
|
112
|
+
if (!showTooltip || !showTag || isPushed) return null;
|
|
113
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(motion.div, {
|
|
114
|
+
initial: {
|
|
115
|
+
opacity: 0,
|
|
116
|
+
y: isVertical ? 0 : 5,
|
|
117
|
+
scale: .9,
|
|
118
|
+
...tooltipTransform
|
|
119
|
+
},
|
|
120
|
+
animate: {
|
|
121
|
+
opacity: 1,
|
|
122
|
+
y: 0,
|
|
123
|
+
scale: 1,
|
|
124
|
+
...tooltipTransform
|
|
125
|
+
},
|
|
126
|
+
exit: {
|
|
127
|
+
opacity: 0,
|
|
128
|
+
y: isVertical ? 0 : 5,
|
|
129
|
+
scale: .9,
|
|
130
|
+
...tooltipTransform
|
|
131
|
+
},
|
|
132
|
+
transition: {
|
|
133
|
+
duration: .05,
|
|
134
|
+
ease: "easeOut"
|
|
135
|
+
},
|
|
136
|
+
className: cn("pointer-events-none absolute z-50", tooltipPositionClass),
|
|
137
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
138
|
+
className: "rounded-sm bg-gradient-to-t from-black/10 to-transparent px-[1px] pb-[2.5px] pt-[1px]",
|
|
139
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
140
|
+
className: cn("whitespace-nowrap rounded-sm px-2 py-1 font-canvas-figtree text-sm", !tooltipStyle?.backgroundColor && "bg-neutral-50", !tooltipStyle?.color && "text-neutral-600", tooltipClassName),
|
|
141
|
+
style: tooltipStyle,
|
|
142
|
+
children: displayLabel
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
}) });
|
|
146
|
+
};
|
|
147
|
+
const renderContent = () => {
|
|
148
|
+
if (displayMode === "labels") return renderLabel();
|
|
149
|
+
if (displayMode === "icons-labels") return /* @__PURE__ */ jsxs("div", {
|
|
150
|
+
className: "flex items-center gap-2",
|
|
151
|
+
children: [renderIcon(), renderLabel()]
|
|
152
|
+
});
|
|
153
|
+
if (displayMode === "compact") return /* @__PURE__ */ jsxs(Fragment, { children: [renderIcon(), renderTooltip()] });
|
|
154
|
+
if (isPushed && allowExpand) return /* @__PURE__ */ jsxs("div", {
|
|
155
|
+
className: "flex items-center gap-2",
|
|
156
|
+
children: [/* @__PURE__ */ jsx("div", { children: renderIcon() }), renderLabel(true)]
|
|
157
|
+
});
|
|
158
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [renderIcon(), renderTooltip()] });
|
|
159
|
+
};
|
|
160
|
+
return /* @__PURE__ */ jsx(motion.button, {
|
|
161
|
+
"aria-label": label,
|
|
162
|
+
className: cn(baseButtonClass, stateClass, buttonClassName),
|
|
163
|
+
style: computedButtonStyle,
|
|
164
|
+
onClick: handleClick,
|
|
165
|
+
onMouseEnter: () => setIsHovered(true),
|
|
166
|
+
onMouseLeave: () => setIsHovered(false),
|
|
167
|
+
whileTap: { scale: .95 },
|
|
168
|
+
transition: {
|
|
169
|
+
type: "spring",
|
|
170
|
+
stiffness: 400,
|
|
171
|
+
damping: 25
|
|
172
|
+
},
|
|
173
|
+
children: renderContent()
|
|
174
|
+
});
|
|
71
175
|
}
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
export { SingleButton as default };
|
|
72
179
|
//# sourceMappingURL=single-button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single-button.js","sourceRoot":"","sources":["../../../../src/components/canvas/navbar/single-button.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"single-button.js","names":[],"sources":["../../../../src/components/canvas/navbar/single-button.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport * as LucideIcons from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport type {\n NavbarDisplayMode,\n NavbarButtonConfig,\n NavbarTooltipConfig,\n} from \"../../../types\";\nimport { cn } from \"../../../lib/utils\";\n\ninterface SingleButtonProps {\n label: string;\n /** Lucide icon name or a custom icon component */\n icon: string | React.ComponentType<{ className?: string }>;\n onClick?: () => void;\n isPushed: boolean;\n link?: string;\n onDebouncedClick?: (callback: () => void) => void;\n /** Display mode for the button */\n displayMode?: NavbarDisplayMode;\n /** Button styling configuration */\n buttonConfig?: NavbarButtonConfig;\n /** Tooltip configuration */\n tooltipConfig?: NavbarTooltipConfig;\n /** Whether the navbar is in vertical layout */\n isVertical?: boolean;\n}\n\nexport default function SingleButton({\n label,\n icon,\n onClick,\n isPushed,\n link,\n onDebouncedClick,\n displayMode = \"icons\",\n buttonConfig = {},\n tooltipConfig = {},\n isVertical = false,\n}: SingleButtonProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [showTag, setShowTag] = useState(false);\n const [copiedEmail, setCopiedEmail] = useState(false);\n\n const isLucideIconName = typeof icon === \"string\";\n const IconComponent = isLucideIconName\n ? (LucideIcons[icon as keyof typeof LucideIcons] as LucideIcons.LucideIcon | undefined)\n : icon;\n\n // Extract config values\n const {\n className: buttonClassName,\n style: buttonStyle,\n activeClassName,\n activeStyle,\n hoverClassName,\n hoverStyle,\n iconClassName,\n iconSize = 20,\n labelClassName,\n labelStyle,\n } = buttonConfig;\n\n const {\n disabled: tooltipDisabled = false,\n className: tooltipClassName,\n style: tooltipStyle,\n delay: tooltipDelay = 100,\n } = tooltipConfig;\n\n // Determine what to show based on display mode\n const showIcon = displayMode !== \"labels\";\n const allowExpand = displayMode === \"icons\"; // Only expand on active in icons mode\n const showTooltip = (displayMode === \"icons\" || displayMode === \"compact\") && !tooltipDisabled;\n\n // Validate icon component for modes that need it\n if (showIcon && !IconComponent) {\n throw new Error(\n \"A valid 'icon' prop is required (Lucide icon name or custom icon component).\",\n );\n }\n\n useEffect(() => {\n let timeoutId: NodeJS.Timeout;\n\n if (isHovered && showTooltip) {\n timeoutId = setTimeout(() => {\n setShowTag(true);\n }, tooltipDelay);\n } else {\n setShowTag(false);\n }\n\n return () => {\n clearTimeout(timeoutId);\n };\n }, [isHovered, showTooltip, tooltipDelay]);\n\n useEffect(() => {\n setShowTag(false);\n setIsHovered(false);\n }, [isPushed]);\n\n // Reset copied email state after 2 seconds\n useEffect(() => {\n if (copiedEmail) {\n const timeoutId = setTimeout(() => {\n setCopiedEmail(false);\n }, 2000);\n return () => clearTimeout(timeoutId);\n }\n }, [copiedEmail]);\n\n const performClick = () => {\n if (link) {\n window.open(link, \"_blank\", \"noopener,noreferrer\");\n return;\n }\n\n onClick?.();\n };\n\n const handleClick = () => {\n if (onDebouncedClick) {\n onDebouncedClick(performClick);\n } else {\n performClick();\n }\n };\n\n const displayLabel = copiedEmail ? \"Email copied!\" : label;\n\n // Compute button classes\n const baseButtonClass = \"relative flex items-center rounded-md p-2 text-neutral-500 transition-colors duration-200 focus:outline-none\";\n // Only apply default classes if no custom style is provided\n const stateClass = isPushed\n ? (activeClassName || (!activeStyle && \"bg-neutral-200\"))\n : isHovered\n ? (hoverClassName || (!hoverStyle && \"bg-neutral-100\"))\n : \"\";\n\n // Compute button styles\n const computedButtonStyle: React.CSSProperties = {\n ...buttonStyle,\n ...(isPushed && activeStyle),\n ...(isHovered && !isPushed && hoverStyle),\n };\n\n // Compute icon classes and styles\n const iconSizeStyle = { width: iconSize, height: iconSize };\n const baseIconClass = \"flex-shrink-0\";\n // Only apply default icon colors if no custom button color style is provided\n const hasCustomColor = buttonStyle?.color;\n const iconColorClass = hasCustomColor\n ? \"\"\n : isPushed\n ? \"text-neutral-700\"\n : \"text-neutral-500\";\n\n // Compute label classes\n const baseLabelClass = \"whitespace-nowrap font-canvas-figtree text-sm font-medium text-neutral-700\";\n\n // Tooltip position based on vertical layout\n const tooltipPositionClass = isVertical\n ? \"left-full top-1/2 -translate-y-1/2 ml-2\"\n : \"-top-10 left-1/2\";\n const tooltipTransform = isVertical\n ? { x: 0, y: \"-50%\" }\n : { x: \"-50%\" };\n\n // Render icon element\n const renderIcon = () => {\n if (!showIcon || !IconComponent) return null;\n return (\n <IconComponent\n className={cn(baseIconClass, iconColorClass, iconClassName)}\n style={iconSizeStyle}\n />\n );\n };\n\n // Render label element\n const renderLabel = (animated = false) => {\n if (animated) {\n return (\n <motion.span\n initial={{ opacity: 0, width: 0 }}\n animate={{ opacity: 1, width: \"auto\" }}\n exit={{ opacity: 0, width: 0 }}\n transition={{\n duration: 0.1,\n ease: \"easeInOut\",\n }}\n className={cn(\"overflow-hidden\", baseLabelClass, labelClassName)}\n style={labelStyle}\n >\n {displayLabel}\n </motion.span>\n );\n }\n return (\n <span\n className={cn(baseLabelClass, labelClassName)}\n style={labelStyle}\n >\n {displayLabel}\n </span>\n );\n };\n\n // Render tooltip\n const renderTooltip = () => {\n if (!showTooltip || !showTag || isPushed) return null;\n\n return (\n <AnimatePresence>\n <motion.div\n initial={{ opacity: 0, y: isVertical ? 0 : 5, scale: 0.9, ...tooltipTransform }}\n animate={{ opacity: 1, y: 0, scale: 1, ...tooltipTransform }}\n exit={{ opacity: 0, y: isVertical ? 0 : 5, scale: 0.9, ...tooltipTransform }}\n transition={{\n duration: 0.05,\n ease: \"easeOut\",\n }}\n className={cn(\"pointer-events-none absolute z-50\", tooltipPositionClass)}\n >\n <div className=\"rounded-sm bg-gradient-to-t from-black/10 to-transparent px-[1px] pb-[2.5px] pt-[1px]\">\n <div\n className={cn(\n \"whitespace-nowrap rounded-sm px-2 py-1 font-canvas-figtree text-sm\",\n !tooltipStyle?.backgroundColor && \"bg-neutral-50\",\n !tooltipStyle?.color && \"text-neutral-600\",\n tooltipClassName,\n )}\n style={tooltipStyle}\n >\n {displayLabel}\n </div>\n </div>\n </motion.div>\n </AnimatePresence>\n );\n };\n\n // Render based on display mode\n const renderContent = () => {\n // Labels only mode\n if (displayMode === \"labels\") {\n return renderLabel();\n }\n\n // Icons + labels always mode\n if (displayMode === \"icons-labels\") {\n return (\n <div className=\"flex items-center gap-2\">\n {renderIcon()}\n {renderLabel()}\n </div>\n );\n }\n\n // Compact mode - icons only, no expansion\n if (displayMode === \"compact\") {\n return (\n <>\n {renderIcon()}\n {renderTooltip()}\n </>\n );\n }\n\n // Icons mode (default) - expands on active\n if (isPushed && allowExpand) {\n return (\n <div className=\"flex items-center gap-2\">\n <div>{renderIcon()}</div>\n {renderLabel(true)}\n </div>\n );\n }\n\n return (\n <>\n {renderIcon()}\n {renderTooltip()}\n </>\n );\n };\n\n return (\n <motion.button\n aria-label={label}\n className={cn(baseButtonClass, stateClass, buttonClassName)}\n style={computedButtonStyle}\n onClick={handleClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n whileTap={{ scale: 0.95 }}\n transition={{\n type: \"spring\",\n stiffness: 400,\n damping: 25,\n }}\n >\n {renderContent()}\n </motion.button>\n );\n}\n"],"mappings":"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"}
|