@hanzo/ui 8.27.9 → 8.27.10
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/backends/gui/3d-card.cjs +100 -0
- package/dist/backends/gui/3d-card.d.ts +59 -0
- package/dist/backends/gui/3d-card.d.ts.map +1 -0
- package/dist/backends/gui/3d-card.js +92 -0
- package/dist/backends/gui/3d-card.js.map +1 -0
- package/dist/backends/gui/3d-marquee.cjs +225 -0
- package/dist/backends/gui/3d-marquee.d.ts +114 -0
- package/dist/backends/gui/3d-marquee.d.ts.map +1 -0
- package/dist/backends/gui/3d-marquee.js +190 -0
- package/dist/backends/gui/3d-marquee.js.map +1 -0
- package/dist/backends/gui/3d-pin.cjs +51 -0
- package/dist/backends/gui/3d-pin.d.ts +14 -0
- package/dist/backends/gui/3d-pin.d.ts.map +1 -0
- package/dist/backends/gui/3d-pin.js +49 -0
- package/dist/backends/gui/3d-pin.js.map +1 -0
- package/dist/backends/gui/advanced-chart.cjs +61 -0
- package/dist/backends/gui/advanced-chart.d.ts +38 -0
- package/dist/backends/gui/advanced-chart.d.ts.map +1 -0
- package/dist/backends/gui/advanced-chart.js +59 -0
- package/dist/backends/gui/advanced-chart.js.map +1 -0
- package/dist/backends/gui/alert.cjs +106 -0
- package/dist/backends/gui/alert.d.ts +39 -0
- package/dist/backends/gui/alert.d.ts.map +1 -0
- package/dist/backends/gui/alert.js +102 -0
- package/dist/backends/gui/alert.js.map +1 -0
- package/dist/backends/gui/android.cjs +52 -0
- package/dist/backends/gui/android.d.ts +16 -0
- package/dist/backends/gui/android.d.ts.map +1 -0
- package/dist/backends/gui/android.js +50 -0
- package/dist/backends/gui/android.js.map +1 -0
- package/dist/backends/gui/animated-beam.cjs +97 -0
- package/dist/backends/gui/animated-beam.d.ts +57 -0
- package/dist/backends/gui/animated-beam.d.ts.map +1 -0
- package/dist/backends/gui/animated-beam.js +62 -0
- package/dist/backends/gui/animated-beam.js.map +1 -0
- package/dist/backends/gui/animated-cursor.cjs +183 -0
- package/dist/backends/gui/animated-cursor.d.ts +42 -0
- package/dist/backends/gui/animated-cursor.d.ts.map +1 -0
- package/dist/backends/gui/animated-cursor.js +181 -0
- package/dist/backends/gui/animated-cursor.js.map +1 -0
- package/dist/backends/gui/animated-testimonials.cjs +109 -0
- package/dist/backends/gui/animated-testimonials.d.ts +23 -0
- package/dist/backends/gui/animated-testimonials.d.ts.map +1 -0
- package/dist/backends/gui/animated-testimonials.js +74 -0
- package/dist/backends/gui/animated-testimonials.js.map +1 -0
- package/dist/backends/gui/animated-tooltip.cjs +100 -0
- package/dist/backends/gui/animated-tooltip.d.ts +15 -0
- package/dist/backends/gui/animated-tooltip.d.ts.map +1 -0
- package/dist/backends/gui/animated-tooltip.js +65 -0
- package/dist/backends/gui/animated-tooltip.js.map +1 -0
- package/dist/backends/gui/announcement.cjs +54 -0
- package/dist/backends/gui/announcement.d.ts +26 -0
- package/dist/backends/gui/announcement.d.ts.map +1 -0
- package/dist/backends/gui/announcement.js +52 -0
- package/dist/backends/gui/announcement.js.map +1 -0
- package/dist/backends/gui/apple-cards-carousel.cjs +198 -0
- package/dist/backends/gui/apple-cards-carousel.d.ts +45 -0
- package/dist/backends/gui/apple-cards-carousel.d.ts.map +1 -0
- package/dist/backends/gui/apple-cards-carousel.js +162 -0
- package/dist/backends/gui/apple-cards-carousel.js.map +1 -0
- package/dist/backends/gui/apple-hello-effect.cjs +38 -0
- package/dist/backends/gui/apple-hello-effect.d.ts +9 -0
- package/dist/backends/gui/apple-hello-effect.d.ts.map +1 -0
- package/dist/backends/gui/apple-hello-effect.js +36 -0
- package/dist/backends/gui/apple-hello-effect.js.map +1 -0
- package/dist/backends/gui/banner.cjs +108 -0
- package/dist/backends/gui/banner.d.ts +33 -0
- package/dist/backends/gui/banner.d.ts.map +1 -0
- package/dist/backends/gui/banner.js +106 -0
- package/dist/backends/gui/banner.js.map +1 -0
- package/dist/backends/gui/calendar.cjs +152 -0
- package/dist/backends/gui/calendar.d.ts +41 -0
- package/dist/backends/gui/calendar.d.ts.map +1 -0
- package/dist/backends/gui/calendar.js +151 -0
- package/dist/backends/gui/calendar.js.map +1 -0
- package/dist/backends/gui/choicebox.cjs +50 -0
- package/dist/backends/gui/choicebox.d.ts +33 -0
- package/dist/backends/gui/choicebox.d.ts.map +1 -0
- package/dist/backends/gui/choicebox.js +49 -0
- package/dist/backends/gui/choicebox.js.map +1 -0
- package/dist/backends/gui/code-block.cjs +120 -0
- package/dist/backends/gui/code-block.d.ts +22 -0
- package/dist/backends/gui/code-block.d.ts.map +1 -0
- package/dist/backends/gui/code-block.js +118 -0
- package/dist/backends/gui/code-block.js.map +1 -0
- package/dist/backends/gui/code-editor.cjs +195 -0
- package/dist/backends/gui/code-editor.d.ts +43 -0
- package/dist/backends/gui/code-editor.d.ts.map +1 -0
- package/dist/backends/gui/code-editor.js +160 -0
- package/dist/backends/gui/code-editor.js.map +1 -0
- package/dist/backends/gui/code-tabs.cjs +21 -0
- package/dist/backends/gui/code-tabs.d.ts +22 -0
- package/dist/backends/gui/code-tabs.d.ts.map +1 -0
- package/dist/backends/gui/code-tabs.js +19 -0
- package/dist/backends/gui/code-tabs.js.map +1 -0
- package/dist/backends/gui/color-picker.cjs +141 -0
- package/dist/backends/gui/color-picker.d.ts +25 -0
- package/dist/backends/gui/color-picker.d.ts.map +1 -0
- package/dist/backends/gui/color-picker.js +139 -0
- package/dist/backends/gui/color-picker.js.map +1 -0
- package/dist/backends/gui/combobox.cjs +42 -0
- package/dist/backends/gui/combobox.d.ts +39 -0
- package/dist/backends/gui/combobox.d.ts.map +1 -0
- package/dist/backends/gui/combobox.js +40 -0
- package/dist/backends/gui/combobox.js.map +1 -0
- package/dist/backends/gui/company-profile.cjs +43 -0
- package/dist/backends/gui/company-profile.d.ts +33 -0
- package/dist/backends/gui/company-profile.d.ts.map +1 -0
- package/dist/backends/gui/company-profile.js +41 -0
- package/dist/backends/gui/company-profile.js.map +1 -0
- package/dist/backends/gui/comparison.cjs +43 -0
- package/dist/backends/gui/comparison.d.ts +16 -0
- package/dist/backends/gui/comparison.d.ts.map +1 -0
- package/dist/backends/gui/comparison.js +41 -0
- package/dist/backends/gui/comparison.js.map +1 -0
- package/dist/backends/gui/credit-card.cjs +80 -0
- package/dist/backends/gui/credit-card.d.ts +21 -0
- package/dist/backends/gui/credit-card.d.ts.map +1 -0
- package/dist/backends/gui/credit-card.js +78 -0
- package/dist/backends/gui/credit-card.js.map +1 -0
- package/dist/backends/gui/crypto-market.cjs +128 -0
- package/dist/backends/gui/crypto-market.d.ts +52 -0
- package/dist/backends/gui/crypto-market.d.ts.map +1 -0
- package/dist/backends/gui/crypto-market.js +126 -0
- package/dist/backends/gui/crypto-market.js.map +1 -0
- package/dist/backends/gui/crypto-screener.cjs +55 -0
- package/dist/backends/gui/crypto-screener.d.ts +42 -0
- package/dist/backends/gui/crypto-screener.d.ts.map +1 -0
- package/dist/backends/gui/crypto-screener.js +53 -0
- package/dist/backends/gui/crypto-screener.js.map +1 -0
- package/dist/backends/gui/cursor.cjs +40 -0
- package/dist/backends/gui/cursor.d.ts +16 -0
- package/dist/backends/gui/cursor.d.ts.map +1 -0
- package/dist/backends/gui/cursor.js +38 -0
- package/dist/backends/gui/cursor.js.map +1 -0
- package/dist/backends/gui/data-table.cjs +211 -0
- package/dist/backends/gui/data-table.d.ts +71 -0
- package/dist/backends/gui/data-table.d.ts.map +1 -0
- package/dist/backends/gui/data-table.js +175 -0
- package/dist/backends/gui/data-table.js.map +1 -0
- package/dist/backends/gui/date-picker.cjs +66 -0
- package/dist/backends/gui/date-picker.d.ts +29 -0
- package/dist/backends/gui/date-picker.d.ts.map +1 -0
- package/dist/backends/gui/date-picker.js +64 -0
- package/dist/backends/gui/date-picker.js.map +1 -0
- package/dist/backends/gui/desktop-spotlight.cjs +46 -0
- package/dist/backends/gui/desktop-spotlight.d.ts +34 -0
- package/dist/backends/gui/desktop-spotlight.d.ts.map +1 -0
- package/dist/backends/gui/desktop-spotlight.js +44 -0
- package/dist/backends/gui/desktop-spotlight.js.map +1 -0
- package/dist/backends/gui/desktop-window.cjs +180 -0
- package/dist/backends/gui/desktop-window.d.ts +51 -0
- package/dist/backends/gui/desktop-window.d.ts.map +1 -0
- package/dist/backends/gui/desktop-window.js +177 -0
- package/dist/backends/gui/desktop-window.js.map +1 -0
- package/dist/backends/gui/desktop.cjs +238 -0
- package/dist/backends/gui/desktop.d.ts +109 -0
- package/dist/backends/gui/desktop.d.ts.map +1 -0
- package/dist/backends/gui/desktop.js +199 -0
- package/dist/backends/gui/desktop.js.map +1 -0
- package/dist/backends/gui/dialog-stack.cjs +26 -0
- package/dist/backends/gui/dialog-stack.d.ts +23 -0
- package/dist/backends/gui/dialog-stack.d.ts.map +1 -0
- package/dist/backends/gui/dialog-stack.js +24 -0
- package/dist/backends/gui/dialog-stack.js.map +1 -0
- package/dist/backends/gui/dock.cjs +99 -0
- package/dist/backends/gui/dock.d.ts +41 -0
- package/dist/backends/gui/dock.d.ts.map +1 -0
- package/dist/backends/gui/dock.js +96 -0
- package/dist/backends/gui/dock.js.map +1 -0
- package/dist/backends/gui/dropzone.cjs +142 -0
- package/dist/backends/gui/dropzone.d.ts +12 -0
- package/dist/backends/gui/dropzone.d.ts.map +1 -0
- package/dist/backends/gui/dropzone.js +140 -0
- package/dist/backends/gui/dropzone.js.map +1 -0
- package/dist/backends/gui/editor.cjs +80 -0
- package/dist/backends/gui/editor.d.ts +23 -0
- package/dist/backends/gui/editor.d.ts.map +1 -0
- package/dist/backends/gui/editor.js +45 -0
- package/dist/backends/gui/editor.js.map +1 -0
- package/dist/backends/gui/empty.cjs +71 -0
- package/dist/backends/gui/empty.d.ts +43 -0
- package/dist/backends/gui/empty.d.ts.map +1 -0
- package/dist/backends/gui/empty.js +63 -0
- package/dist/backends/gui/empty.js.map +1 -0
- package/dist/backends/gui/financials.cjs +45 -0
- package/dist/backends/gui/financials.d.ts +36 -0
- package/dist/backends/gui/financials.d.ts.map +1 -0
- package/dist/backends/gui/financials.js +43 -0
- package/dist/backends/gui/financials.js.map +1 -0
- package/dist/backends/gui/forex-market.cjs +21 -0
- package/dist/backends/gui/forex-market.d.ts +17 -0
- package/dist/backends/gui/forex-market.d.ts.map +1 -0
- package/dist/backends/gui/forex-market.js +19 -0
- package/dist/backends/gui/forex-market.js.map +1 -0
- package/dist/backends/gui/forex-screener.cjs +57 -0
- package/dist/backends/gui/forex-screener.d.ts +45 -0
- package/dist/backends/gui/forex-screener.d.ts.map +1 -0
- package/dist/backends/gui/forex-screener.js +55 -0
- package/dist/backends/gui/forex-screener.js.map +1 -0
- package/dist/backends/gui/gantt.cjs +50 -0
- package/dist/backends/gui/gantt.d.ts +19 -0
- package/dist/backends/gui/gantt.d.ts.map +1 -0
- package/dist/backends/gui/gantt.js +49 -0
- package/dist/backends/gui/gantt.js.map +1 -0
- package/dist/backends/gui/index.cjs +108 -1
- package/dist/backends/gui/index.d.ts +41 -0
- package/dist/backends/gui/index.d.ts.map +1 -1
- package/dist/backends/gui/index.js +41 -0
- package/dist/backends/gui/index.js.map +1 -1
- package/dist/gallery.html +1 -1
- package/dist/index.cjs +70 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/index.node.cjs +70 -1
- package/dist/index.node.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AnimatedCursor = AnimatedCursor;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* AnimatedCursor — a custom pointer that follows the mouse, grows on hover,
|
|
8
|
+
* shrinks on click, and can trail a fading string of dots behind it.
|
|
9
|
+
*
|
|
10
|
+
* It reads the document rather than any one element: a `mousemove` positions
|
|
11
|
+
* the dot, `mouseover` walks `event.target` up through `closest()` against four
|
|
12
|
+
* selector groups (text / interactive / grab / grabbing) to pick a shape, and
|
|
13
|
+
* `mousedown`/`mouseup` drive the click scale. While it is live one stylesheet
|
|
14
|
+
* rule hides the native cursor everywhere — the body alone is not enough, since
|
|
15
|
+
* a button's own `cursor: pointer` and a field's I-beam would still show beside
|
|
16
|
+
* the dot — and the rule goes with the component. `color` and `trailColor` take
|
|
17
|
+
* a theme token (`$color`, the default) or any CSS colour, and the trail, unless
|
|
18
|
+
* given its own, is the fill at a third of its strength. The frame carries
|
|
19
|
+
* `zIndex`, so the dot stays above overlays. All of that is DOM-only, so it
|
|
20
|
+
* renders nothing on native, on a touch device (`hideOnTouch`, the default)
|
|
21
|
+
* or while `isVisible` is off, and does nothing before mount, keeping the
|
|
22
|
+
* server render and the first client render identical.
|
|
23
|
+
*/
|
|
24
|
+
const gui_1 = require("@hanzo/gui");
|
|
25
|
+
const react_1 = require("react");
|
|
26
|
+
const sx_1 = require("../../sx.cjs");
|
|
27
|
+
const slot_1 = require("./slot.cjs");
|
|
28
|
+
const INTERACTIVE_SELECTORS = [
|
|
29
|
+
'a',
|
|
30
|
+
'button',
|
|
31
|
+
'input',
|
|
32
|
+
'textarea',
|
|
33
|
+
'select',
|
|
34
|
+
'[role="button"]',
|
|
35
|
+
'[role="link"]',
|
|
36
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
37
|
+
'[data-cursor="pointer"]',
|
|
38
|
+
].join(', ');
|
|
39
|
+
const TEXT_SELECTORS = [
|
|
40
|
+
'input[type="text"]',
|
|
41
|
+
'input[type="email"]',
|
|
42
|
+
'input[type="password"]',
|
|
43
|
+
'input[type="search"]',
|
|
44
|
+
'textarea',
|
|
45
|
+
'[contenteditable="true"]',
|
|
46
|
+
'[data-cursor="text"]',
|
|
47
|
+
].join(', ');
|
|
48
|
+
const GRAB_SELECTORS = '[data-cursor="grab"]';
|
|
49
|
+
const GRABBING_SELECTORS = '[data-cursor="grabbing"]';
|
|
50
|
+
const HIDE_NATIVE = '* { cursor: none !important }';
|
|
51
|
+
const touchCapable = () => gui_1.isWeb &&
|
|
52
|
+
typeof window !== 'undefined' &&
|
|
53
|
+
('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
54
|
+
/** A theme token (`$color`) as the CSS variable behind it; any other colour as written. */
|
|
55
|
+
const paint = (theme, c) => c.startsWith('$') ? String(theme[c.slice(1)]?.get('web') ?? c) : c;
|
|
56
|
+
/**
|
|
57
|
+
* The shape `closest()` finds for a mouseover target, or `default` for none.
|
|
58
|
+
* Text fields are checked before the general interactive group, because a
|
|
59
|
+
* text `<input>` and a `<textarea>` also match that group's bare `input` /
|
|
60
|
+
* `textarea` entries and would otherwise never read as text.
|
|
61
|
+
*/
|
|
62
|
+
const shapeFor = (target) => {
|
|
63
|
+
const el = target;
|
|
64
|
+
if (!el?.closest)
|
|
65
|
+
return 'default';
|
|
66
|
+
if (el.closest(TEXT_SELECTORS))
|
|
67
|
+
return 'text';
|
|
68
|
+
if (el.closest(INTERACTIVE_SELECTORS))
|
|
69
|
+
return 'pointer';
|
|
70
|
+
if (el.closest(GRAB_SELECTORS))
|
|
71
|
+
return 'grab';
|
|
72
|
+
if (el.closest(GRABBING_SELECTORS))
|
|
73
|
+
return 'grabbing';
|
|
74
|
+
return 'default';
|
|
75
|
+
};
|
|
76
|
+
function AnimatedCursor({ isVisible = true, size = 20, color = '$color', trailColor, animationDuration = 200, showTrail = true, trailLength = 8, blendMode = 'normal', hoverScale = 1.5, hideOnTouch = true, className, clickAnimationDuration = 100, zIndex = 9999, }) {
|
|
77
|
+
const theme = (0, gui_1.useTheme)();
|
|
78
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
79
|
+
const [isTouch, setIsTouch] = (0, react_1.useState)(false);
|
|
80
|
+
const [state, setState] = (0, react_1.useState)({
|
|
81
|
+
position: { x: 0, y: 0 },
|
|
82
|
+
isHovering: false,
|
|
83
|
+
isClicking: false,
|
|
84
|
+
cursorType: 'default',
|
|
85
|
+
});
|
|
86
|
+
const [trail, setTrail] = (0, react_1.useState)([]);
|
|
87
|
+
const trailRef = (0, react_1.useRef)([]);
|
|
88
|
+
(0, react_1.useEffect)(() => {
|
|
89
|
+
setMounted(true);
|
|
90
|
+
setIsTouch(touchCapable());
|
|
91
|
+
}, []);
|
|
92
|
+
const live = mounted && gui_1.isWeb && isVisible && !(hideOnTouch && isTouch);
|
|
93
|
+
(0, react_1.useEffect)(() => {
|
|
94
|
+
if (!live)
|
|
95
|
+
return;
|
|
96
|
+
const onMove = (e) => {
|
|
97
|
+
const position = { x: e.clientX, y: e.clientY };
|
|
98
|
+
setState((s) => ({ ...s, position }));
|
|
99
|
+
const next = [...trailRef.current, position];
|
|
100
|
+
trailRef.current = next.slice(Math.max(0, next.length - trailLength));
|
|
101
|
+
setTrail(trailRef.current);
|
|
102
|
+
};
|
|
103
|
+
const onOver = (e) => {
|
|
104
|
+
const cursorType = shapeFor(e.target);
|
|
105
|
+
setState((s) => ({ ...s, isHovering: cursorType !== 'default', cursorType }));
|
|
106
|
+
};
|
|
107
|
+
const onDown = () => setState((s) => ({ ...s, isClicking: true }));
|
|
108
|
+
const onUp = () => setState((s) => ({ ...s, isClicking: false }));
|
|
109
|
+
const onLeave = () => setState((s) => ({ ...s, isHovering: false, cursorType: 'default' }));
|
|
110
|
+
document.addEventListener('mousemove', onMove);
|
|
111
|
+
document.addEventListener('mouseover', onOver);
|
|
112
|
+
document.addEventListener('mousedown', onDown);
|
|
113
|
+
document.addEventListener('mouseup', onUp);
|
|
114
|
+
document.addEventListener('mouseleave', onLeave);
|
|
115
|
+
const hide = document.createElement('style');
|
|
116
|
+
hide.textContent = HIDE_NATIVE;
|
|
117
|
+
document.head.appendChild(hide);
|
|
118
|
+
return () => {
|
|
119
|
+
document.removeEventListener('mousemove', onMove);
|
|
120
|
+
document.removeEventListener('mouseover', onOver);
|
|
121
|
+
document.removeEventListener('mousedown', onDown);
|
|
122
|
+
document.removeEventListener('mouseup', onUp);
|
|
123
|
+
document.removeEventListener('mouseleave', onLeave);
|
|
124
|
+
hide.remove();
|
|
125
|
+
};
|
|
126
|
+
}, [live, trailLength]);
|
|
127
|
+
if (!live)
|
|
128
|
+
return null;
|
|
129
|
+
const fill = paint(theme, color);
|
|
130
|
+
const trailFill = trailColor ? paint(theme, trailColor) : `color-mix(in srgb, ${fill} 31%, transparent)`;
|
|
131
|
+
const hoverRingSize = size * hoverScale;
|
|
132
|
+
const dotScale = (state.isHovering ? hoverScale : 1) * (state.isClicking ? 0.8 : 1);
|
|
133
|
+
const isText = state.cursorType === 'text';
|
|
134
|
+
const isGrab = state.cursorType === 'grab' || state.cursorType === 'grabbing';
|
|
135
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('animated-cursor'), position: "fixed", inset: 0, style: { zIndex }, pointerEvents: "none", ...(0, sx_1.sx)(className), children: [showTrail &&
|
|
136
|
+
trail.map((point, i) => {
|
|
137
|
+
const weight = (i + 1) / trail.length;
|
|
138
|
+
const style = {
|
|
139
|
+
position: 'absolute',
|
|
140
|
+
left: point.x - size / 2,
|
|
141
|
+
top: point.y - size / 2,
|
|
142
|
+
width: size,
|
|
143
|
+
height: size,
|
|
144
|
+
borderRadius: '50%',
|
|
145
|
+
backgroundColor: trailFill,
|
|
146
|
+
opacity: weight * 0.5,
|
|
147
|
+
transform: `scale(${weight * 0.8})`,
|
|
148
|
+
zIndex: zIndex - i - 1,
|
|
149
|
+
mixBlendMode: blendMode,
|
|
150
|
+
};
|
|
151
|
+
return (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('animated-cursor-trail'), style: style }, i);
|
|
152
|
+
}), (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('animated-cursor-dot'), "data-shape": state.cursorType, style: {
|
|
153
|
+
position: 'absolute',
|
|
154
|
+
left: state.position.x - (isText ? 1 : size / 2),
|
|
155
|
+
top: state.position.y - (isText ? 12 : size / 2),
|
|
156
|
+
width: isText ? 2 : size,
|
|
157
|
+
height: isText ? 24 : size,
|
|
158
|
+
backgroundColor: fill,
|
|
159
|
+
borderRadius: isText ? 0 : isGrab ? '6px' : '50%',
|
|
160
|
+
transform: `scale(${dotScale})`,
|
|
161
|
+
transitionProperty: 'transform',
|
|
162
|
+
transitionDuration: `${state.isClicking ? clickAnimationDuration : animationDuration}ms`,
|
|
163
|
+
transitionTimingFunction: 'ease-out',
|
|
164
|
+
zIndex,
|
|
165
|
+
mixBlendMode: blendMode,
|
|
166
|
+
} }), state.isHovering && !isText && ((0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('animated-cursor-ring'), style: {
|
|
167
|
+
position: 'absolute',
|
|
168
|
+
left: state.position.x - hoverRingSize / 2,
|
|
169
|
+
top: state.position.y - hoverRingSize / 2,
|
|
170
|
+
width: hoverRingSize,
|
|
171
|
+
height: hoverRingSize,
|
|
172
|
+
borderRadius: '50%',
|
|
173
|
+
borderWidth: 2,
|
|
174
|
+
borderStyle: 'solid',
|
|
175
|
+
borderColor: fill,
|
|
176
|
+
opacity: 0.3,
|
|
177
|
+
transitionProperty: 'all',
|
|
178
|
+
transitionDuration: `${animationDuration}ms`,
|
|
179
|
+
transitionTimingFunction: 'ease-out',
|
|
180
|
+
zIndex: zIndex - 1,
|
|
181
|
+
mixBlendMode: blendMode,
|
|
182
|
+
} }))] }));
|
|
183
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type AnimatedCursorBlendMode = 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'hard-light' | 'soft-light' | 'difference' | 'exclusion';
|
|
2
|
+
export type AnimatedCursorShape = 'default' | 'pointer' | 'text' | 'grab' | 'grabbing';
|
|
3
|
+
export type CursorPosition = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
};
|
|
7
|
+
export type CursorState = {
|
|
8
|
+
position: CursorPosition;
|
|
9
|
+
isHovering: boolean;
|
|
10
|
+
isClicking: boolean;
|
|
11
|
+
cursorType: AnimatedCursorShape;
|
|
12
|
+
};
|
|
13
|
+
export type AnimatedCursorProps = {
|
|
14
|
+
/** Whether the cursor renders at all; off, the native cursor is untouched. */
|
|
15
|
+
isVisible?: boolean;
|
|
16
|
+
/** Diameter of the dot, in px. */
|
|
17
|
+
size?: number;
|
|
18
|
+
/** Fill of the dot: a theme token such as `$color`, or a CSS colour. */
|
|
19
|
+
color?: string;
|
|
20
|
+
/** Fill of each trailing dot; by default the fill at a third of its strength. */
|
|
21
|
+
trailColor?: string;
|
|
22
|
+
/** How long the dot and the ring take to settle, in ms. */
|
|
23
|
+
animationDuration?: number;
|
|
24
|
+
/** Whether a fading trail of past positions renders behind the dot. */
|
|
25
|
+
showTrail?: boolean;
|
|
26
|
+
/** How many past positions the trail keeps. */
|
|
27
|
+
trailLength?: number;
|
|
28
|
+
/** `mix-blend-mode` for the dot, the trail and the hover ring. */
|
|
29
|
+
blendMode?: AnimatedCursorBlendMode;
|
|
30
|
+
/** Scale the dot reaches while hovering an interactive element. */
|
|
31
|
+
hoverScale?: number;
|
|
32
|
+
/** Render nothing on a device that reports touch support. */
|
|
33
|
+
hideOnTouch?: boolean;
|
|
34
|
+
/** Class notation for the fixed frame. */
|
|
35
|
+
className?: string;
|
|
36
|
+
/** How long the click scale takes to release, in ms. */
|
|
37
|
+
clickAnimationDuration?: number;
|
|
38
|
+
/** Stacking context of the frame, and of the dot within it; the trail and ring sit just under. */
|
|
39
|
+
zIndex?: number;
|
|
40
|
+
};
|
|
41
|
+
export declare function AnimatedCursor({ isVisible, size, color, trailColor, animationDuration, showTrail, trailLength, blendMode, hoverScale, hideOnTouch, className, clickAnimationDuration, zIndex, }: AnimatedCursorProps): import("react").JSX.Element | null;
|
|
42
|
+
//# sourceMappingURL=animated-cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-cursor.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/animated-cursor.tsx"],"names":[],"mappings":"AAyBA,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,WAAW,CAAA;AAEf,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAA;AAEtF,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAErD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,mBAAmB,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,SAAS,CAAC,EAAE,uBAAuB,CAAA;IACnC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAsDD,wBAAgB,cAAc,CAAC,EAC7B,SAAgB,EAChB,IAAS,EACT,KAAgB,EAChB,UAAU,EACV,iBAAuB,EACvB,SAAgB,EAChB,WAAe,EACf,SAAoB,EACpB,UAAgB,EAChB,WAAkB,EAClB,SAAS,EACT,sBAA4B,EAC5B,MAAa,GACd,EAAE,mBAAmB,sCA2IrB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AnimatedCursor — a custom pointer that follows the mouse, grows on hover,
|
|
5
|
+
* shrinks on click, and can trail a fading string of dots behind it.
|
|
6
|
+
*
|
|
7
|
+
* It reads the document rather than any one element: a `mousemove` positions
|
|
8
|
+
* the dot, `mouseover` walks `event.target` up through `closest()` against four
|
|
9
|
+
* selector groups (text / interactive / grab / grabbing) to pick a shape, and
|
|
10
|
+
* `mousedown`/`mouseup` drive the click scale. While it is live one stylesheet
|
|
11
|
+
* rule hides the native cursor everywhere — the body alone is not enough, since
|
|
12
|
+
* a button's own `cursor: pointer` and a field's I-beam would still show beside
|
|
13
|
+
* the dot — and the rule goes with the component. `color` and `trailColor` take
|
|
14
|
+
* a theme token (`$color`, the default) or any CSS colour, and the trail, unless
|
|
15
|
+
* given its own, is the fill at a third of its strength. The frame carries
|
|
16
|
+
* `zIndex`, so the dot stays above overlays. All of that is DOM-only, so it
|
|
17
|
+
* renders nothing on native, on a touch device (`hideOnTouch`, the default)
|
|
18
|
+
* or while `isVisible` is off, and does nothing before mount, keeping the
|
|
19
|
+
* server render and the first client render identical.
|
|
20
|
+
*/
|
|
21
|
+
import { YStack, isWeb, useTheme } from '@hanzo/gui';
|
|
22
|
+
import { useEffect, useRef, useState } from 'react';
|
|
23
|
+
import { sx } from '../../sx.js';
|
|
24
|
+
import { slot } from './slot.js';
|
|
25
|
+
const INTERACTIVE_SELECTORS = [
|
|
26
|
+
'a',
|
|
27
|
+
'button',
|
|
28
|
+
'input',
|
|
29
|
+
'textarea',
|
|
30
|
+
'select',
|
|
31
|
+
'[role="button"]',
|
|
32
|
+
'[role="link"]',
|
|
33
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
34
|
+
'[data-cursor="pointer"]',
|
|
35
|
+
].join(', ');
|
|
36
|
+
const TEXT_SELECTORS = [
|
|
37
|
+
'input[type="text"]',
|
|
38
|
+
'input[type="email"]',
|
|
39
|
+
'input[type="password"]',
|
|
40
|
+
'input[type="search"]',
|
|
41
|
+
'textarea',
|
|
42
|
+
'[contenteditable="true"]',
|
|
43
|
+
'[data-cursor="text"]',
|
|
44
|
+
].join(', ');
|
|
45
|
+
const GRAB_SELECTORS = '[data-cursor="grab"]';
|
|
46
|
+
const GRABBING_SELECTORS = '[data-cursor="grabbing"]';
|
|
47
|
+
const HIDE_NATIVE = '* { cursor: none !important }';
|
|
48
|
+
const touchCapable = () => isWeb &&
|
|
49
|
+
typeof window !== 'undefined' &&
|
|
50
|
+
('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
51
|
+
/** A theme token (`$color`) as the CSS variable behind it; any other colour as written. */
|
|
52
|
+
const paint = (theme, c) => c.startsWith('$') ? String(theme[c.slice(1)]?.get('web') ?? c) : c;
|
|
53
|
+
/**
|
|
54
|
+
* The shape `closest()` finds for a mouseover target, or `default` for none.
|
|
55
|
+
* Text fields are checked before the general interactive group, because a
|
|
56
|
+
* text `<input>` and a `<textarea>` also match that group's bare `input` /
|
|
57
|
+
* `textarea` entries and would otherwise never read as text.
|
|
58
|
+
*/
|
|
59
|
+
const shapeFor = (target) => {
|
|
60
|
+
const el = target;
|
|
61
|
+
if (!el?.closest)
|
|
62
|
+
return 'default';
|
|
63
|
+
if (el.closest(TEXT_SELECTORS))
|
|
64
|
+
return 'text';
|
|
65
|
+
if (el.closest(INTERACTIVE_SELECTORS))
|
|
66
|
+
return 'pointer';
|
|
67
|
+
if (el.closest(GRAB_SELECTORS))
|
|
68
|
+
return 'grab';
|
|
69
|
+
if (el.closest(GRABBING_SELECTORS))
|
|
70
|
+
return 'grabbing';
|
|
71
|
+
return 'default';
|
|
72
|
+
};
|
|
73
|
+
export function AnimatedCursor({ isVisible = true, size = 20, color = '$color', trailColor, animationDuration = 200, showTrail = true, trailLength = 8, blendMode = 'normal', hoverScale = 1.5, hideOnTouch = true, className, clickAnimationDuration = 100, zIndex = 9999, }) {
|
|
74
|
+
const theme = useTheme();
|
|
75
|
+
const [mounted, setMounted] = useState(false);
|
|
76
|
+
const [isTouch, setIsTouch] = useState(false);
|
|
77
|
+
const [state, setState] = useState({
|
|
78
|
+
position: { x: 0, y: 0 },
|
|
79
|
+
isHovering: false,
|
|
80
|
+
isClicking: false,
|
|
81
|
+
cursorType: 'default',
|
|
82
|
+
});
|
|
83
|
+
const [trail, setTrail] = useState([]);
|
|
84
|
+
const trailRef = useRef([]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setMounted(true);
|
|
87
|
+
setIsTouch(touchCapable());
|
|
88
|
+
}, []);
|
|
89
|
+
const live = mounted && isWeb && isVisible && !(hideOnTouch && isTouch);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!live)
|
|
92
|
+
return;
|
|
93
|
+
const onMove = (e) => {
|
|
94
|
+
const position = { x: e.clientX, y: e.clientY };
|
|
95
|
+
setState((s) => ({ ...s, position }));
|
|
96
|
+
const next = [...trailRef.current, position];
|
|
97
|
+
trailRef.current = next.slice(Math.max(0, next.length - trailLength));
|
|
98
|
+
setTrail(trailRef.current);
|
|
99
|
+
};
|
|
100
|
+
const onOver = (e) => {
|
|
101
|
+
const cursorType = shapeFor(e.target);
|
|
102
|
+
setState((s) => ({ ...s, isHovering: cursorType !== 'default', cursorType }));
|
|
103
|
+
};
|
|
104
|
+
const onDown = () => setState((s) => ({ ...s, isClicking: true }));
|
|
105
|
+
const onUp = () => setState((s) => ({ ...s, isClicking: false }));
|
|
106
|
+
const onLeave = () => setState((s) => ({ ...s, isHovering: false, cursorType: 'default' }));
|
|
107
|
+
document.addEventListener('mousemove', onMove);
|
|
108
|
+
document.addEventListener('mouseover', onOver);
|
|
109
|
+
document.addEventListener('mousedown', onDown);
|
|
110
|
+
document.addEventListener('mouseup', onUp);
|
|
111
|
+
document.addEventListener('mouseleave', onLeave);
|
|
112
|
+
const hide = document.createElement('style');
|
|
113
|
+
hide.textContent = HIDE_NATIVE;
|
|
114
|
+
document.head.appendChild(hide);
|
|
115
|
+
return () => {
|
|
116
|
+
document.removeEventListener('mousemove', onMove);
|
|
117
|
+
document.removeEventListener('mouseover', onOver);
|
|
118
|
+
document.removeEventListener('mousedown', onDown);
|
|
119
|
+
document.removeEventListener('mouseup', onUp);
|
|
120
|
+
document.removeEventListener('mouseleave', onLeave);
|
|
121
|
+
hide.remove();
|
|
122
|
+
};
|
|
123
|
+
}, [live, trailLength]);
|
|
124
|
+
if (!live)
|
|
125
|
+
return null;
|
|
126
|
+
const fill = paint(theme, color);
|
|
127
|
+
const trailFill = trailColor ? paint(theme, trailColor) : `color-mix(in srgb, ${fill} 31%, transparent)`;
|
|
128
|
+
const hoverRingSize = size * hoverScale;
|
|
129
|
+
const dotScale = (state.isHovering ? hoverScale : 1) * (state.isClicking ? 0.8 : 1);
|
|
130
|
+
const isText = state.cursorType === 'text';
|
|
131
|
+
const isGrab = state.cursorType === 'grab' || state.cursorType === 'grabbing';
|
|
132
|
+
return (_jsxs(YStack, { ...slot('animated-cursor'), position: "fixed", inset: 0, style: { zIndex }, pointerEvents: "none", ...sx(className), children: [showTrail &&
|
|
133
|
+
trail.map((point, i) => {
|
|
134
|
+
const weight = (i + 1) / trail.length;
|
|
135
|
+
const style = {
|
|
136
|
+
position: 'absolute',
|
|
137
|
+
left: point.x - size / 2,
|
|
138
|
+
top: point.y - size / 2,
|
|
139
|
+
width: size,
|
|
140
|
+
height: size,
|
|
141
|
+
borderRadius: '50%',
|
|
142
|
+
backgroundColor: trailFill,
|
|
143
|
+
opacity: weight * 0.5,
|
|
144
|
+
transform: `scale(${weight * 0.8})`,
|
|
145
|
+
zIndex: zIndex - i - 1,
|
|
146
|
+
mixBlendMode: blendMode,
|
|
147
|
+
};
|
|
148
|
+
return _jsx(YStack, { ...slot('animated-cursor-trail'), style: style }, i);
|
|
149
|
+
}), _jsx(YStack, { ...slot('animated-cursor-dot'), "data-shape": state.cursorType, style: {
|
|
150
|
+
position: 'absolute',
|
|
151
|
+
left: state.position.x - (isText ? 1 : size / 2),
|
|
152
|
+
top: state.position.y - (isText ? 12 : size / 2),
|
|
153
|
+
width: isText ? 2 : size,
|
|
154
|
+
height: isText ? 24 : size,
|
|
155
|
+
backgroundColor: fill,
|
|
156
|
+
borderRadius: isText ? 0 : isGrab ? '6px' : '50%',
|
|
157
|
+
transform: `scale(${dotScale})`,
|
|
158
|
+
transitionProperty: 'transform',
|
|
159
|
+
transitionDuration: `${state.isClicking ? clickAnimationDuration : animationDuration}ms`,
|
|
160
|
+
transitionTimingFunction: 'ease-out',
|
|
161
|
+
zIndex,
|
|
162
|
+
mixBlendMode: blendMode,
|
|
163
|
+
} }), state.isHovering && !isText && (_jsx(YStack, { ...slot('animated-cursor-ring'), style: {
|
|
164
|
+
position: 'absolute',
|
|
165
|
+
left: state.position.x - hoverRingSize / 2,
|
|
166
|
+
top: state.position.y - hoverRingSize / 2,
|
|
167
|
+
width: hoverRingSize,
|
|
168
|
+
height: hoverRingSize,
|
|
169
|
+
borderRadius: '50%',
|
|
170
|
+
borderWidth: 2,
|
|
171
|
+
borderStyle: 'solid',
|
|
172
|
+
borderColor: fill,
|
|
173
|
+
opacity: 0.3,
|
|
174
|
+
transitionProperty: 'all',
|
|
175
|
+
transitionDuration: `${animationDuration}ms`,
|
|
176
|
+
transitionTimingFunction: 'ease-out',
|
|
177
|
+
zIndex: zIndex - 1,
|
|
178
|
+
mixBlendMode: blendMode,
|
|
179
|
+
} }))] }));
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=animated-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-cursor.js","sourceRoot":"","sources":["../../../src/backends/gui/animated-cursor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.AnimatedTestimonials = AnimatedTestimonials;
|
|
38
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
|
+
/**
|
|
40
|
+
* AnimatedTestimonials — one testimonial at a time in a card, advancing on a
|
|
41
|
+
* timer and by pressing a dot below it.
|
|
42
|
+
*
|
|
43
|
+
* The card holds a real `<blockquote>`: the quote is its `<p>`, the author a
|
|
44
|
+
* `<cite>` in its `<footer>`, so a reader announces it as a quotation.
|
|
45
|
+
*
|
|
46
|
+
* Mount is the animation: each testimonial gets a fresh `key`, so a change
|
|
47
|
+
* remounts the card and `hz-fade-up` (styles/motion.css) plays. The outgoing
|
|
48
|
+
* card unmounts with no exit.
|
|
49
|
+
*/
|
|
50
|
+
const gui_1 = require("@hanzo/gui");
|
|
51
|
+
const React = __importStar(require("react"));
|
|
52
|
+
const avatar_1 = require("./avatar.cjs");
|
|
53
|
+
const card_1 = require("./card.cjs");
|
|
54
|
+
const gesture_1 = require("./gesture.cjs");
|
|
55
|
+
const slot_1 = require("./slot.cjs");
|
|
56
|
+
const Frame = (0, gui_1.styled)(gui_1.YStack, {
|
|
57
|
+
name: 'AnimatedTestimonials',
|
|
58
|
+
position: 'relative',
|
|
59
|
+
minH: 200,
|
|
60
|
+
gap: '$4',
|
|
61
|
+
});
|
|
62
|
+
const Quote = (0, gui_1.styled)(gui_1.SizableText, {
|
|
63
|
+
name: 'AnimatedTestimonialsQuote',
|
|
64
|
+
size: '$5',
|
|
65
|
+
fontStyle: 'italic',
|
|
66
|
+
});
|
|
67
|
+
/** A `<cite>` is italic by default; the author reads upright under the quote. */
|
|
68
|
+
const Author = (0, gui_1.styled)(gui_1.SizableText, {
|
|
69
|
+
name: 'AnimatedTestimonialsAuthor',
|
|
70
|
+
fontWeight: '500',
|
|
71
|
+
fontStyle: 'normal',
|
|
72
|
+
});
|
|
73
|
+
const Meta = (0, gui_1.styled)(gui_1.SizableText, {
|
|
74
|
+
name: 'AnimatedTestimonialsMeta',
|
|
75
|
+
size: '$2',
|
|
76
|
+
color: '$quiet',
|
|
77
|
+
});
|
|
78
|
+
/** A dot is 8px; `touch()` below meets the 44px floor without growing it. */
|
|
79
|
+
const DOT = 8;
|
|
80
|
+
const Dot = (0, gui_1.styled)(gui_1.YStack, {
|
|
81
|
+
name: 'AnimatedTestimonialsDot',
|
|
82
|
+
height: DOT,
|
|
83
|
+
rounded: 999,
|
|
84
|
+
bg: '$edge',
|
|
85
|
+
cursor: 'pointer',
|
|
86
|
+
variants: {
|
|
87
|
+
active: {
|
|
88
|
+
true: { width: DOT * 4, bg: '$ink' },
|
|
89
|
+
false: { width: DOT, hoverStyle: { bg: '$quiet' } },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
defaultVariants: { active: false },
|
|
93
|
+
});
|
|
94
|
+
function AnimatedTestimonials({ testimonials, autoPlay = true, duration = 5000, ...props }) {
|
|
95
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
if (!autoPlay || testimonials.length < 2)
|
|
98
|
+
return;
|
|
99
|
+
const id = setInterval(() => {
|
|
100
|
+
setActiveIndex((prev) => (prev + 1) % testimonials.length);
|
|
101
|
+
}, duration);
|
|
102
|
+
return () => clearInterval(id);
|
|
103
|
+
}, [autoPlay, duration, testimonials.length]);
|
|
104
|
+
// One index for the card and the dots, so a list that shrank under the
|
|
105
|
+
// current one never shows a card with no dot lit.
|
|
106
|
+
const index = Math.min(activeIndex, testimonials.length - 1);
|
|
107
|
+
const active = testimonials[index];
|
|
108
|
+
return ((0, jsx_runtime_1.jsxs)(Frame, { ...(0, slot_1.slot)('animated-testimonials'), ...props, children: [active && ((0, jsx_runtime_1.jsx)(card_1.Card, { className: "hz-fade-up", children: (0, jsx_runtime_1.jsx)(card_1.CardContent, { children: (0, jsx_runtime_1.jsxs)(gui_1.YStack, { render: "blockquote", gap: "$4", children: [(0, jsx_runtime_1.jsxs)(Quote, { ...(0, slot_1.slot)('animated-testimonials-quote'), render: "p", children: ["\u201C", active.content, "\u201D"] }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { render: "footer", items: "center", gap: "$4", children: [(0, jsx_runtime_1.jsxs)(avatar_1.Avatar, { children: [(0, jsx_runtime_1.jsx)(avatar_1.AvatarImage, { src: active.avatar }), (0, jsx_runtime_1.jsx)(avatar_1.AvatarFallback, { children: active.author.slice(0, 2).toUpperCase() })] }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { children: [(0, jsx_runtime_1.jsx)(Author, { ...(0, slot_1.slot)('animated-testimonials-author'), render: "cite", children: active.author }), active.role && ((0, jsx_runtime_1.jsxs)(Meta, { ...(0, slot_1.slot)('animated-testimonials-meta'), render: "p", children: [active.role, active.company && `, ${active.company}`] }))] })] })] }) }) }, active.id)), (0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('animated-testimonials-dots'), justify: "center", gap: "$2", children: testimonials.map((t, i) => ((0, jsx_runtime_1.jsx)(Dot, { ...(0, slot_1.slot)('animated-testimonials-dot'), render: (0, jsx_runtime_1.jsx)("button", { type: "button" }), active: i === index, "aria-label": `Go to testimonial ${i + 1}`, "aria-current": i === index, style: { transition: 'width .3s ease, background-color .3s ease' }, onPress: () => setActiveIndex(i), ...(0, gesture_1.touch)(DOT, 44) }, t.id))) })] }));
|
|
109
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type Testimonial = {
|
|
3
|
+
id: string;
|
|
4
|
+
content: string;
|
|
5
|
+
author: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
company?: string;
|
|
8
|
+
avatar?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const Frame: import("@hanzogui/web").GuiComponent<import("@hanzogui/web").TamaDefer, import("@hanzogui/web").GuiElement, import("@hanzogui/core").RNGuiViewNonStyleProps, import("@hanzogui/web").StackStyleBase, {
|
|
11
|
+
elevation?: number | import("@hanzogui/web").SizeTokens | undefined;
|
|
12
|
+
fullscreen?: boolean | undefined;
|
|
13
|
+
}, import("@hanzogui/web").StaticConfigPublic>;
|
|
14
|
+
export type AnimatedTestimonialsProps = Omit<React.ComponentProps<typeof Frame>, 'children'> & {
|
|
15
|
+
testimonials: Testimonial[];
|
|
16
|
+
/** Advance on its own. Default true. */
|
|
17
|
+
autoPlay?: boolean;
|
|
18
|
+
/** Milliseconds between slides. Default 5000. */
|
|
19
|
+
duration?: number;
|
|
20
|
+
};
|
|
21
|
+
export declare function AnimatedTestimonials({ testimonials, autoPlay, duration, ...props }: AnimatedTestimonialsProps): React.JSX.Element;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=animated-testimonials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-testimonials.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/animated-testimonials.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,QAAA,MAAM,KAAK;;;8CAKT,CAAA;AAyCF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAC7F,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,QAAe,EACf,QAAe,EACf,GAAG,KAAK,EACT,EAAE,yBAAyB,qBAgE3B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AnimatedTestimonials — one testimonial at a time in a card, advancing on a
|
|
5
|
+
* timer and by pressing a dot below it.
|
|
6
|
+
*
|
|
7
|
+
* The card holds a real `<blockquote>`: the quote is its `<p>`, the author a
|
|
8
|
+
* `<cite>` in its `<footer>`, so a reader announces it as a quotation.
|
|
9
|
+
*
|
|
10
|
+
* Mount is the animation: each testimonial gets a fresh `key`, so a change
|
|
11
|
+
* remounts the card and `hz-fade-up` (styles/motion.css) plays. The outgoing
|
|
12
|
+
* card unmounts with no exit.
|
|
13
|
+
*/
|
|
14
|
+
import { SizableText, XStack, YStack, styled } from '@hanzo/gui';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
import { Avatar, AvatarFallback, AvatarImage } from './avatar.js';
|
|
17
|
+
import { Card, CardContent } from './card.js';
|
|
18
|
+
import { touch } from './gesture.js';
|
|
19
|
+
import { slot } from './slot.js';
|
|
20
|
+
const Frame = styled(YStack, {
|
|
21
|
+
name: 'AnimatedTestimonials',
|
|
22
|
+
position: 'relative',
|
|
23
|
+
minH: 200,
|
|
24
|
+
gap: '$4',
|
|
25
|
+
});
|
|
26
|
+
const Quote = styled(SizableText, {
|
|
27
|
+
name: 'AnimatedTestimonialsQuote',
|
|
28
|
+
size: '$5',
|
|
29
|
+
fontStyle: 'italic',
|
|
30
|
+
});
|
|
31
|
+
/** A `<cite>` is italic by default; the author reads upright under the quote. */
|
|
32
|
+
const Author = styled(SizableText, {
|
|
33
|
+
name: 'AnimatedTestimonialsAuthor',
|
|
34
|
+
fontWeight: '500',
|
|
35
|
+
fontStyle: 'normal',
|
|
36
|
+
});
|
|
37
|
+
const Meta = styled(SizableText, {
|
|
38
|
+
name: 'AnimatedTestimonialsMeta',
|
|
39
|
+
size: '$2',
|
|
40
|
+
color: '$quiet',
|
|
41
|
+
});
|
|
42
|
+
/** A dot is 8px; `touch()` below meets the 44px floor without growing it. */
|
|
43
|
+
const DOT = 8;
|
|
44
|
+
const Dot = styled(YStack, {
|
|
45
|
+
name: 'AnimatedTestimonialsDot',
|
|
46
|
+
height: DOT,
|
|
47
|
+
rounded: 999,
|
|
48
|
+
bg: '$edge',
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
variants: {
|
|
51
|
+
active: {
|
|
52
|
+
true: { width: DOT * 4, bg: '$ink' },
|
|
53
|
+
false: { width: DOT, hoverStyle: { bg: '$quiet' } },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
defaultVariants: { active: false },
|
|
57
|
+
});
|
|
58
|
+
export function AnimatedTestimonials({ testimonials, autoPlay = true, duration = 5000, ...props }) {
|
|
59
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (!autoPlay || testimonials.length < 2)
|
|
62
|
+
return;
|
|
63
|
+
const id = setInterval(() => {
|
|
64
|
+
setActiveIndex((prev) => (prev + 1) % testimonials.length);
|
|
65
|
+
}, duration);
|
|
66
|
+
return () => clearInterval(id);
|
|
67
|
+
}, [autoPlay, duration, testimonials.length]);
|
|
68
|
+
// One index for the card and the dots, so a list that shrank under the
|
|
69
|
+
// current one never shows a card with no dot lit.
|
|
70
|
+
const index = Math.min(activeIndex, testimonials.length - 1);
|
|
71
|
+
const active = testimonials[index];
|
|
72
|
+
return (_jsxs(Frame, { ...slot('animated-testimonials'), ...props, children: [active && (_jsx(Card, { className: "hz-fade-up", children: _jsx(CardContent, { children: _jsxs(YStack, { render: "blockquote", gap: "$4", children: [_jsxs(Quote, { ...slot('animated-testimonials-quote'), render: "p", children: ["\u201C", active.content, "\u201D"] }), _jsxs(XStack, { render: "footer", items: "center", gap: "$4", children: [_jsxs(Avatar, { children: [_jsx(AvatarImage, { src: active.avatar }), _jsx(AvatarFallback, { children: active.author.slice(0, 2).toUpperCase() })] }), _jsxs(YStack, { children: [_jsx(Author, { ...slot('animated-testimonials-author'), render: "cite", children: active.author }), active.role && (_jsxs(Meta, { ...slot('animated-testimonials-meta'), render: "p", children: [active.role, active.company && `, ${active.company}`] }))] })] })] }) }) }, active.id)), _jsx(XStack, { ...slot('animated-testimonials-dots'), justify: "center", gap: "$2", children: testimonials.map((t, i) => (_jsx(Dot, { ...slot('animated-testimonials-dot'), render: _jsx("button", { type: "button" }), active: i === index, "aria-label": `Go to testimonial ${i + 1}`, "aria-current": i === index, style: { transition: 'width .3s ease, background-color .3s ease' }, onPress: () => setActiveIndex(i), ...touch(DOT, 44) }, t.id))) })] }));
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=animated-testimonials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-testimonials.js","sourceRoot":"","sources":["../../../src/backends/gui/animated-testimonials.tsx"],"names":[],"mappings":"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"}
|