@yewdiel21/motion-kit 0.14.0 → 0.16.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 +178 -155
- package/bin/motion-kit.mjs +3 -3
- package/dist/auto/index.d.ts +6 -3
- package/dist/auto/index.js +8 -2
- package/dist/auto/index.js.map +1 -1
- package/dist/{chunk-EY4BJKC4.js → chunk-NFZIX3IA.js} +2 -2
- package/dist/chunk-RBEKR5UZ.js +3660 -0
- package/dist/chunk-RBEKR5UZ.js.map +1 -0
- package/dist/{chunk-CU6SXW3E.js → chunk-SDQ776LG.js} +1596 -485
- package/dist/chunk-SDQ776LG.js.map +1 -0
- package/dist/css/components/add-to-cart.css +72 -0
- package/dist/css/components/bidirectional-slider.css +168 -0
- package/dist/css/components/chart.css +7 -0
- package/dist/css/components/data-table.css +121 -26
- package/dist/css/components/filter-builder.css +10 -15
- package/dist/css/components/heatmap-chart.css +398 -0
- package/dist/css/components/hover-card.css +8 -2
- package/dist/css/components/image-preview.css +90 -2
- package/dist/css/components/json-viewer.css +33 -1
- package/dist/css/components/nav.css +102 -102
- package/dist/css/components/orbit-menu.css +130 -0
- package/dist/css/components/pagination.css +10 -5
- package/dist/css/components/pie-chart.css +397 -0
- package/dist/css/components/pill-button.css +135 -0
- package/dist/css/components/radar-chart.css +377 -0
- package/dist/css/components/radial-chart.css +412 -0
- package/dist/css/components/sankey-chart.css +365 -0
- package/dist/css/components/scroll-island.css +114 -0
- package/dist/css/components/select.css +136 -0
- package/dist/css/components/toolbar.css +122 -0
- package/dist/css/components/tree-view.css +79 -0
- package/dist/css/components/voice-dots.css +70 -0
- package/dist/elements/index.d.ts +180 -7
- package/dist/elements/index.js +293 -7
- package/dist/elements/index.js.map +1 -1
- package/dist/{engine-Cn6IhE3z.d.ts → engine-5zRIssTO.d.ts} +11 -1
- package/dist/index.css +1849 -168
- package/dist/index.d.ts +4 -4
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +117 -11
- package/dist/react/index.js +320 -13
- package/dist/react/index.js.map +1 -1
- package/dist/{reduced-BBUusld6.d.ts → reduced-DQwVXQYb.d.ts} +1 -1
- package/dist/{reveal-DEvp6U6T.d.ts → reveal-DRpNO4KP.d.ts} +109 -4
- package/dist/store-Da3zSh8Z.d.ts +457 -0
- package/package.json +9 -16
- package/registry.json +1096 -948
- package/src/auto/auto-init.ts +6 -0
- package/src/components/add-to-cart/README.md +49 -0
- package/src/components/add-to-cart/add-to-cart.css +71 -0
- package/src/components/add-to-cart/element.ts +20 -0
- package/src/components/add-to-cart/engine.ts +206 -0
- package/src/components/add-to-cart/react.tsx +33 -0
- package/src/components/bidirectional-slider/README.md +46 -0
- package/src/components/bidirectional-slider/bidirectional-slider.css +167 -0
- package/src/components/bidirectional-slider/element.ts +42 -0
- package/src/components/bidirectional-slider/engine.ts +289 -0
- package/src/components/bidirectional-slider/react.tsx +45 -0
- package/src/components/chart/README.md +49 -24
- package/src/components/chart/chart.css +7 -0
- package/src/components/chart/element.ts +4 -1
- package/src/components/chart/engine.ts +153 -34
- package/src/components/chart/parts.ts +237 -0
- package/src/components/chart/react.tsx +2 -2
- package/src/components/chart/use-chart.ts +55 -0
- package/src/components/data-table/README.md +3 -3
- package/src/components/data-table/data-table.css +121 -26
- package/src/components/data-table/engine.ts +81 -29
- package/src/components/editable-table/engine.ts +67 -11
- package/src/components/filter-builder/engine.ts +33 -2
- package/src/components/filter-builder/filter-builder.css +10 -15
- package/src/components/heatmap-chart/README.md +48 -0
- package/src/components/heatmap-chart/element.ts +21 -0
- package/src/components/heatmap-chart/engine.ts +281 -0
- package/src/components/heatmap-chart/heatmap-chart.css +93 -0
- package/src/components/heatmap-chart/react.tsx +14 -0
- package/src/components/hover-card/engine.ts +30 -1
- package/src/components/hover-card/hover-card.css +8 -2
- package/src/components/image-preview/element.ts +1 -1
- package/src/components/image-preview/engine.ts +95 -15
- package/src/components/image-preview/image-preview.css +90 -2
- package/src/components/image-preview/react.tsx +3 -3
- package/src/components/json-viewer/engine.ts +11 -2
- package/src/components/json-viewer/json-viewer.css +33 -1
- package/src/components/nav/README.md +12 -10
- package/src/components/nav/element.ts +5 -5
- package/src/components/nav/engine.ts +21 -14
- package/src/components/nav/nav.css +102 -102
- package/src/components/nav/react.tsx +4 -4
- package/src/components/orbit-menu/README.md +46 -0
- package/src/components/orbit-menu/element.ts +27 -0
- package/src/components/orbit-menu/engine.ts +179 -0
- package/src/components/orbit-menu/orbit-menu.css +129 -0
- package/src/components/orbit-menu/react.tsx +44 -0
- package/src/components/pagination/README.md +1 -1
- package/src/components/pagination/engine.ts +50 -4
- package/src/components/pagination/pagination.css +10 -5
- package/src/components/pie-chart/README.md +48 -0
- package/src/components/pie-chart/element.ts +30 -0
- package/src/components/pie-chart/engine.ts +283 -0
- package/src/components/pie-chart/pie-chart.css +92 -0
- package/src/components/pie-chart/react.tsx +14 -0
- package/src/components/pill-button/README.md +46 -0
- package/src/components/pill-button/element.ts +16 -0
- package/src/components/pill-button/engine.ts +77 -0
- package/src/components/pill-button/pill-button.css +134 -0
- package/src/components/pill-button/react.tsx +32 -0
- package/src/components/radar-chart/README.md +48 -0
- package/src/components/radar-chart/element.ts +30 -0
- package/src/components/radar-chart/engine.ts +327 -0
- package/src/components/radar-chart/radar-chart.css +72 -0
- package/src/components/radar-chart/react.tsx +14 -0
- package/src/components/radial-chart/README.md +58 -0
- package/src/components/radial-chart/element.ts +30 -0
- package/src/components/radial-chart/engine.ts +317 -0
- package/src/components/radial-chart/radial-chart.css +107 -0
- package/src/components/radial-chart/react.tsx +14 -0
- package/src/components/sankey-chart/README.md +48 -0
- package/src/components/sankey-chart/element.ts +31 -0
- package/src/components/sankey-chart/engine.ts +401 -0
- package/src/components/sankey-chart/react.tsx +14 -0
- package/src/components/sankey-chart/sankey-chart.css +60 -0
- package/src/components/scroll-island/README.md +44 -0
- package/src/components/scroll-island/element.ts +16 -0
- package/src/components/scroll-island/engine.ts +274 -0
- package/src/components/scroll-island/react.tsx +26 -0
- package/src/components/scroll-island/scroll-island.css +113 -0
- package/src/components/select/README.md +34 -0
- package/src/components/select/element.ts +26 -0
- package/src/components/select/engine.ts +267 -0
- package/src/components/select/react.tsx +33 -0
- package/src/components/select/select.css +135 -0
- package/src/components/toolbar/README.md +46 -0
- package/src/components/toolbar/element.ts +30 -0
- package/src/components/toolbar/engine.ts +230 -0
- package/src/components/toolbar/react.tsx +34 -0
- package/src/components/toolbar/toolbar.css +121 -0
- package/src/components/tree-view/element.ts +1 -1
- package/src/components/tree-view/engine.ts +174 -0
- package/src/components/tree-view/react.tsx +7 -3
- package/src/components/tree-view/tree-view.css +79 -0
- package/src/components/voice-dots/README.md +48 -0
- package/src/components/voice-dots/element.ts +45 -0
- package/src/components/voice-dots/engine.ts +243 -0
- package/src/components/voice-dots/react.tsx +52 -0
- package/src/components/voice-dots/voice-dots.css +69 -0
- package/src/elements/index.ts +39 -0
- package/src/index.ts +16 -0
- package/src/motion/liquid.ts +14 -4
- package/src/react/index.ts +14 -1
- package/dist/chunk-6P4P7NU6.js +0 -1401
- package/dist/chunk-6P4P7NU6.js.map +0 -1
- package/dist/chunk-CU6SXW3E.js.map +0 -1
- package/dist/store-DjkTBX7f.d.ts +0 -195
- /package/dist/{chunk-EY4BJKC4.js.map → chunk-NFZIX3IA.js.map} +0 -0
package/dist/chunk-6P4P7NU6.js
DELETED
|
@@ -1,1401 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EASE,
|
|
3
|
-
Flip,
|
|
4
|
-
bezier,
|
|
5
|
-
channels,
|
|
6
|
-
easeInCubic,
|
|
7
|
-
easeOut,
|
|
8
|
-
easeOutCubic,
|
|
9
|
-
eased,
|
|
10
|
-
flowIn,
|
|
11
|
-
flowInPalette,
|
|
12
|
-
gsap,
|
|
13
|
-
motionSpring,
|
|
14
|
-
registerMotion,
|
|
15
|
-
revealFrom,
|
|
16
|
-
splitGraphemes,
|
|
17
|
-
splitWords,
|
|
18
|
-
spring,
|
|
19
|
-
springEase
|
|
20
|
-
} from "./chunk-CU6SXW3E.js";
|
|
21
|
-
import {
|
|
22
|
-
THEMES,
|
|
23
|
-
THEME_EVENT,
|
|
24
|
-
getTheme,
|
|
25
|
-
setTheme
|
|
26
|
-
} from "./chunk-RVSKEBIC.js";
|
|
27
|
-
import {
|
|
28
|
-
TIMING,
|
|
29
|
-
createScope,
|
|
30
|
-
entrances,
|
|
31
|
-
reduced,
|
|
32
|
-
subscribe
|
|
33
|
-
} from "./chunk-DOOMULVV.js";
|
|
34
|
-
import {
|
|
35
|
-
inert,
|
|
36
|
-
warnOnce
|
|
37
|
-
} from "./chunk-TGV27ZWM.js";
|
|
38
|
-
|
|
39
|
-
// src/motion/chat.ts
|
|
40
|
-
var isUser = (bubble) => bubble.dataset.mkRole === "user" || bubble.classList.contains("user");
|
|
41
|
-
function enterMessages(bubbles, { textSelector = "[data-mk-text], .message-text" } = {}) {
|
|
42
|
-
if (!bubbles.length || reduced()) return null;
|
|
43
|
-
registerMotion();
|
|
44
|
-
const reverts = [];
|
|
45
|
-
const cleanup = () => {
|
|
46
|
-
for (const revert of reverts.splice(0)) revert();
|
|
47
|
-
};
|
|
48
|
-
const timeline = gsap.timeline({ onComplete: cleanup, onInterrupt: cleanup });
|
|
49
|
-
bubbles.forEach((bubble, index) => {
|
|
50
|
-
gsap.set(bubble, { transformOrigin: isUser(bubble) ? "100% 50%" : "0% 50%" });
|
|
51
|
-
timeline.fromTo(
|
|
52
|
-
bubble,
|
|
53
|
-
{ scale: 0.5, autoAlpha: 0, filter: "blur(24px)" },
|
|
54
|
-
{ scale: 1, autoAlpha: 1, filter: "blur(0px)", duration: TIMING.bubble, ease: EASE.signature, clearProps: "transform,opacity,visibility,filter" },
|
|
55
|
-
index * TIMING.bubbleStagger
|
|
56
|
-
);
|
|
57
|
-
const text = bubble.querySelector(textSelector);
|
|
58
|
-
if (!text) return;
|
|
59
|
-
const before = text.offsetHeight;
|
|
60
|
-
const split = splitWords(text);
|
|
61
|
-
const lineHeight = parseFloat(getComputedStyle(text).lineHeight) || 0;
|
|
62
|
-
let targets = split.parts;
|
|
63
|
-
if (!split.parts.length || text.offsetHeight - before > lineHeight / 2) {
|
|
64
|
-
split.revert();
|
|
65
|
-
targets = [text];
|
|
66
|
-
} else reverts.push(() => split.revert());
|
|
67
|
-
timeline.fromTo(
|
|
68
|
-
targets,
|
|
69
|
-
{ y: 16, autoAlpha: 0, filter: "blur(6px)" },
|
|
70
|
-
{
|
|
71
|
-
y: 0,
|
|
72
|
-
autoAlpha: 1,
|
|
73
|
-
filter: "blur(0px)",
|
|
74
|
-
duration: TIMING.word,
|
|
75
|
-
ease: "back.out(1)",
|
|
76
|
-
stagger: TIMING.wordStagger,
|
|
77
|
-
clearProps: "transform,opacity,visibility,filter"
|
|
78
|
-
},
|
|
79
|
-
index * TIMING.bubbleStagger
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
return timeline;
|
|
83
|
-
}
|
|
84
|
-
function fitComposer(field, min = 64) {
|
|
85
|
-
const from = field.offsetHeight;
|
|
86
|
-
field.style.height = "auto";
|
|
87
|
-
const to = Math.max(min, field.scrollHeight);
|
|
88
|
-
field.style.height = `${from}px`;
|
|
89
|
-
if (from === to || reduced()) {
|
|
90
|
-
field.style.height = `${to}px`;
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
registerMotion();
|
|
94
|
-
return gsap.to(field, { height: to, duration: TIMING.composer, ease: "power2.out", overwrite: "auto" });
|
|
95
|
-
}
|
|
96
|
-
function scrollToLatest(scroller) {
|
|
97
|
-
const top = scroller.scrollHeight - scroller.clientHeight;
|
|
98
|
-
if (reduced()) {
|
|
99
|
-
scroller.scrollTop = top;
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
registerMotion();
|
|
103
|
-
const state = { top: scroller.scrollTop };
|
|
104
|
-
return gsap.to(state, {
|
|
105
|
-
top,
|
|
106
|
-
duration: TIMING.chatScroll,
|
|
107
|
-
ease: EASE.chatScroll,
|
|
108
|
-
overwrite: "auto",
|
|
109
|
-
onUpdate: () => {
|
|
110
|
-
scroller.scrollTop = state.top;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// src/components/theme-picker/engine.ts
|
|
116
|
-
function createThemePicker(root, options = {}) {
|
|
117
|
-
if (!root) {
|
|
118
|
-
warnOnce("theme-picker: missing root");
|
|
119
|
-
return inert({ select() {
|
|
120
|
-
} });
|
|
121
|
-
}
|
|
122
|
-
registerMotion();
|
|
123
|
-
const scope = createScope();
|
|
124
|
-
const ctx = gsap.context(() => {
|
|
125
|
-
});
|
|
126
|
-
const generated = [];
|
|
127
|
-
let swatches = [...root.querySelectorAll("[data-mk-swatch]")];
|
|
128
|
-
if (!swatches.length) {
|
|
129
|
-
const byId = new Map(THEMES.map((theme) => [theme.id, theme]));
|
|
130
|
-
const ids = options.themes ?? THEMES.filter((theme) => options.showHidden || !theme.hidden).map((theme) => theme.id);
|
|
131
|
-
for (const id of ids) {
|
|
132
|
-
const theme = byId.get(id);
|
|
133
|
-
if (!theme) {
|
|
134
|
-
warnOnce(`theme-picker: unknown theme "${id}"`);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const button = document.createElement("button");
|
|
138
|
-
button.type = "button";
|
|
139
|
-
button.className = "mk-swatch";
|
|
140
|
-
button.dataset.mkSwatch = theme.id;
|
|
141
|
-
button.title = theme.name;
|
|
142
|
-
button.setAttribute("aria-label", theme.name);
|
|
143
|
-
button.style.setProperty("--mk-swatch", theme.seed);
|
|
144
|
-
root.append(button);
|
|
145
|
-
generated.push(button);
|
|
146
|
-
}
|
|
147
|
-
swatches = generated;
|
|
148
|
-
}
|
|
149
|
-
scope.attr(root, "role", "group");
|
|
150
|
-
if (!root.hasAttribute("aria-label")) scope.attr(root, "aria-label", options.labels?.group ?? "Tema");
|
|
151
|
-
const sync = () => {
|
|
152
|
-
const current = getTheme().id;
|
|
153
|
-
for (const swatch of swatches) scope.attr(swatch, "aria-pressed", String(swatch.dataset.mkSwatch === current));
|
|
154
|
-
};
|
|
155
|
-
const select = (id) => {
|
|
156
|
-
setTheme(id);
|
|
157
|
-
options.onChange?.(id);
|
|
158
|
-
};
|
|
159
|
-
scope.on(root, "click", (event) => {
|
|
160
|
-
const swatch = event.target?.closest("[data-mk-swatch]");
|
|
161
|
-
if (swatch && root.contains(swatch) && swatch.dataset.mkSwatch) select(swatch.dataset.mkSwatch);
|
|
162
|
-
});
|
|
163
|
-
scope.on(document, THEME_EVENT, sync);
|
|
164
|
-
sync();
|
|
165
|
-
if (entrances()) ctx.add(() => flowInPalette(swatches));
|
|
166
|
-
root.dataset.mkReady = "";
|
|
167
|
-
return {
|
|
168
|
-
select,
|
|
169
|
-
destroy() {
|
|
170
|
-
scope.dispose();
|
|
171
|
-
ctx.revert();
|
|
172
|
-
for (const button of generated.splice(0)) button.remove();
|
|
173
|
-
delete root.dataset.mkReady;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// src/components/toast/store.ts
|
|
179
|
-
var TOAST_LIMIT = 5;
|
|
180
|
-
var items = [];
|
|
181
|
-
var nextId = 1;
|
|
182
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
183
|
-
var emit = () => {
|
|
184
|
-
for (const listener of listeners) listener();
|
|
185
|
-
};
|
|
186
|
-
var getToasts = () => items;
|
|
187
|
-
function subscribeToasts(listener) {
|
|
188
|
-
listeners.add(listener);
|
|
189
|
-
return () => {
|
|
190
|
-
listeners.delete(listener);
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
function show(message, { origin = null, duration = 4e3, tone = "success", action, icon = true } = {}) {
|
|
194
|
-
const id = nextId++;
|
|
195
|
-
const live = items.filter((item) => !item.closing);
|
|
196
|
-
const extra = new Set(live.slice(0, Math.max(0, live.length - TOAST_LIMIT + 1)).map((item) => item.id));
|
|
197
|
-
items = [
|
|
198
|
-
...items.map((item) => extra.has(item.id) ? { ...item, closing: true } : item),
|
|
199
|
-
{ id, message, origin, duration, tone, action: action ?? null, icon, closing: false }
|
|
200
|
-
];
|
|
201
|
-
emit();
|
|
202
|
-
return id;
|
|
203
|
-
}
|
|
204
|
-
function dismissToast(id) {
|
|
205
|
-
const item = items.find((entry) => entry.id === id);
|
|
206
|
-
if (!item || item.closing) return;
|
|
207
|
-
items = items.map((entry) => entry.id === id ? { ...entry, closing: true } : entry);
|
|
208
|
-
emit();
|
|
209
|
-
}
|
|
210
|
-
function removeToast(id) {
|
|
211
|
-
toastTimers.clear(id);
|
|
212
|
-
items = items.filter((entry) => entry.id !== id);
|
|
213
|
-
emit();
|
|
214
|
-
}
|
|
215
|
-
function clearToasts() {
|
|
216
|
-
for (const item of items) toastTimers.clear(item.id);
|
|
217
|
-
items = [];
|
|
218
|
-
emit();
|
|
219
|
-
}
|
|
220
|
-
var toast = Object.assign(show, { dismiss: dismissToast, clear: clearToasts });
|
|
221
|
-
var running = /* @__PURE__ */ new Map();
|
|
222
|
-
var toastTimers = {
|
|
223
|
-
start(id, duration) {
|
|
224
|
-
if (duration <= 0 || running.has(id)) return;
|
|
225
|
-
running.set(id, { handle: setTimeout(() => dismissToast(id), duration), remaining: duration, started: Date.now() });
|
|
226
|
-
},
|
|
227
|
-
pause(id) {
|
|
228
|
-
const timer = running.get(id);
|
|
229
|
-
if (!timer?.handle) return;
|
|
230
|
-
clearTimeout(timer.handle);
|
|
231
|
-
timer.remaining = Math.max(0, timer.remaining - (Date.now() - timer.started));
|
|
232
|
-
timer.handle = null;
|
|
233
|
-
},
|
|
234
|
-
resume(id) {
|
|
235
|
-
const timer = running.get(id);
|
|
236
|
-
if (!timer || timer.handle) return;
|
|
237
|
-
if (timer.remaining <= 0) {
|
|
238
|
-
dismissToast(id);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
timer.started = Date.now();
|
|
242
|
-
timer.handle = setTimeout(() => dismissToast(id), timer.remaining);
|
|
243
|
-
},
|
|
244
|
-
clear(id) {
|
|
245
|
-
const timer = running.get(id);
|
|
246
|
-
if (timer?.handle) clearTimeout(timer.handle);
|
|
247
|
-
running.delete(id);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// src/components/icons.ts
|
|
252
|
-
var ICON_PATHS = {
|
|
253
|
-
close: "M6 6l12 12M18 6L6 18",
|
|
254
|
-
expand: "M14 4h6v6M10 20H4v-6M20 4l-7 7M4 20l7-7",
|
|
255
|
-
shrink: "M4 14h6v6M20 10h-6V4M10 14l-7 7M14 10l7-7",
|
|
256
|
-
check: "M5 12.5l4.5 4.5L19 7.5",
|
|
257
|
-
alert: "M12 8v5M12 16.5v.01M10.3 3.9L2.6 17.3a2 2 0 001.7 3h15.4a2 2 0 001.7-3L13.7 3.9a2 2 0 00-3.4 0z",
|
|
258
|
-
info: "M12 11v6M12 7.5v.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
259
|
-
send: "M12 19V5M5.5 11.5L12 5l6.5 6.5",
|
|
260
|
-
mic: "M12 3a3 3 0 00-3 3v6a3 3 0 006 0V6a3 3 0 00-3-3zM5.5 11a6.5 6.5 0 0013 0M12 17.5V21",
|
|
261
|
-
sparkle: "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z",
|
|
262
|
-
grip: "M9 6h.01M15 6h.01M9 12h.01M15 12h.01M9 18h.01M15 18h.01"
|
|
263
|
-
};
|
|
264
|
-
function iconSvg(name, size = 20) {
|
|
265
|
-
return `<svg aria-hidden="true" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${ICON_PATHS[name]}"/></svg>`;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/components/toast/engine.ts
|
|
269
|
-
var STACK = { peek: 16, scaleStep: 0.05, minScale: 0.7, maxVisible: 3, gap: 8, baseZ: 1e3 };
|
|
270
|
-
var DISTANCE = { enter: 12, enterBlur: 6, exit: 24, exitBlur: 4, fade: 120 };
|
|
271
|
-
var GESTURE = { swipe: 48, wheel: 80, expandDrag: 24, resistance: 0.15, wheelEndMs: 140 };
|
|
272
|
-
var TONE_ICONS = { success: "check", error: "alert", info: "info", neutral: null };
|
|
273
|
-
function layout(count, expanded, heights) {
|
|
274
|
-
let offset = 0;
|
|
275
|
-
return Array.from({ length: count }, (_, index) => {
|
|
276
|
-
if (expanded) {
|
|
277
|
-
const slot = { y: offset, scale: 1, opacity: 1, visible: true, zIndex: STACK.baseZ - index };
|
|
278
|
-
offset += (heights[index] ?? 0) + STACK.gap;
|
|
279
|
-
return slot;
|
|
280
|
-
}
|
|
281
|
-
const visible = index < STACK.maxVisible;
|
|
282
|
-
return {
|
|
283
|
-
y: index * STACK.peek,
|
|
284
|
-
scale: Math.max(STACK.minScale, 1 - index * STACK.scaleStep),
|
|
285
|
-
opacity: visible ? 1 : 0,
|
|
286
|
-
visible,
|
|
287
|
-
zIndex: STACK.baseZ - index
|
|
288
|
-
};
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
function revealLetters(element) {
|
|
292
|
-
const text = element.textContent ?? "";
|
|
293
|
-
const T = TIMING.toast;
|
|
294
|
-
if (!text || text.length > 64 || reduced()) return;
|
|
295
|
-
const letters = splitGraphemes(text, 64);
|
|
296
|
-
const original = element.innerHTML;
|
|
297
|
-
const width = element.getBoundingClientRect().width;
|
|
298
|
-
const fragment = document.createDocumentFragment();
|
|
299
|
-
const chars = [];
|
|
300
|
-
let word = null;
|
|
301
|
-
for (const letter of letters) {
|
|
302
|
-
if (!letter.trim()) {
|
|
303
|
-
word = null;
|
|
304
|
-
fragment.append(document.createTextNode(letter));
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
if (!word) {
|
|
308
|
-
word = document.createElement("span");
|
|
309
|
-
word.style.display = "inline-block";
|
|
310
|
-
word.style.whiteSpace = "nowrap";
|
|
311
|
-
fragment.append(word);
|
|
312
|
-
}
|
|
313
|
-
const char = document.createElement("span");
|
|
314
|
-
char.textContent = letter;
|
|
315
|
-
char.style.display = "inline-block";
|
|
316
|
-
word.append(char);
|
|
317
|
-
chars.push(char);
|
|
318
|
-
}
|
|
319
|
-
if (width) element.style.width = `${width}px`;
|
|
320
|
-
element.textContent = "";
|
|
321
|
-
element.append(fragment);
|
|
322
|
-
gsap.fromTo(
|
|
323
|
-
chars,
|
|
324
|
-
{ opacity: 0, yPercent: 40, filter: "blur(4px)" },
|
|
325
|
-
{
|
|
326
|
-
opacity: 1,
|
|
327
|
-
yPercent: 0,
|
|
328
|
-
filter: "blur(0px)",
|
|
329
|
-
duration: T.chars,
|
|
330
|
-
ease: easeOutCubic,
|
|
331
|
-
delay: (index) => T.charsDelay + index * T.charsStagger,
|
|
332
|
-
onComplete: () => {
|
|
333
|
-
element.innerHTML = original;
|
|
334
|
-
element.style.width = "";
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
function morphFrom(card, shell, body, origin) {
|
|
340
|
-
const T = TIMING.toast;
|
|
341
|
-
const box = shell.getBoundingClientRect();
|
|
342
|
-
const start = origin.getBoundingClientRect();
|
|
343
|
-
const look = getComputedStyle(origin);
|
|
344
|
-
const own = getComputedStyle(shell);
|
|
345
|
-
const endRadius = parseFloat(own.borderTopLeftRadius) || 20;
|
|
346
|
-
const startRadius = Math.min(parseFloat(look.borderTopLeftRadius) || 0, start.height / 2);
|
|
347
|
-
card.style.width = `${box.width}px`;
|
|
348
|
-
card.style.height = `${box.height}px`;
|
|
349
|
-
body.style.width = `${body.getBoundingClientRect().width}px`;
|
|
350
|
-
const towardX = start.left + start.width / 2 < innerWidth / 2 ? 1 : -1;
|
|
351
|
-
const towardY = start.top + start.height / 2 < innerHeight / 2 ? 1 : -1;
|
|
352
|
-
const offset = { x: start.left - box.left, y: start.top - box.top };
|
|
353
|
-
Object.assign(shell.style, {
|
|
354
|
-
position: "absolute",
|
|
355
|
-
top: "0px",
|
|
356
|
-
left: "0px",
|
|
357
|
-
width: `${start.width}px`,
|
|
358
|
-
height: `${start.height}px`,
|
|
359
|
-
borderRadius: `${startRadius}px`,
|
|
360
|
-
background: look.backgroundColor,
|
|
361
|
-
transform: `translate(${offset.x}px, ${offset.y}px)`,
|
|
362
|
-
transition: "none"
|
|
363
|
-
});
|
|
364
|
-
Object.assign(body.style, { transform: "scale(2)", transformOrigin: "center center", filter: "blur(8px)", opacity: "0" });
|
|
365
|
-
origin.style.transition = "opacity 160ms ease";
|
|
366
|
-
origin.style.opacity = "0";
|
|
367
|
-
void shell.offsetWidth;
|
|
368
|
-
shell.style.transition = `background ${T.morphColor}s ease-out ${T.morphColorDelay}s, box-shadow ${T.morphShadow}s ease-out ${T.morphShadowDelay}s`;
|
|
369
|
-
shell.style.background = "";
|
|
370
|
-
body.style.transition = `opacity ${T.morphColor}s ease-out ${T.morphColorDelay}s`;
|
|
371
|
-
body.style.opacity = "1";
|
|
372
|
-
const motion = channels(
|
|
373
|
-
{ x: offset.x, y: offset.y, width: start.width, height: start.height, radius: startRadius, scale: 2, blur: 8 },
|
|
374
|
-
(name, value) => {
|
|
375
|
-
if (name === "x") offset.x = value;
|
|
376
|
-
else if (name === "y") offset.y = value;
|
|
377
|
-
else if (name === "width") shell.style.width = `${value}px`;
|
|
378
|
-
else if (name === "height") shell.style.height = `${value}px`;
|
|
379
|
-
else if (name === "radius") shell.style.borderRadius = `${value}px`;
|
|
380
|
-
else if (name === "scale") body.style.transform = `scale(${value})`;
|
|
381
|
-
else if (name === "blur") body.style.filter = `blur(${value}px)`;
|
|
382
|
-
if (name === "x" || name === "y") shell.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
void motion.animate(
|
|
386
|
-
{ x: 0, y: 0, width: box.width, height: box.height, radius: endRadius, scale: 1, blur: 0 },
|
|
387
|
-
{
|
|
388
|
-
x: spring(144, 14, { velocity: 2400, direction: towardX }),
|
|
389
|
-
y: spring(144, 14, { velocity: 2400, direction: towardY }),
|
|
390
|
-
width: eased(T.morphSize, bezier(0.8, 0.3, 0.5, 0.8)),
|
|
391
|
-
height: eased(T.morphSize, bezier(0.8, 0.3, 0.5, 0.8)),
|
|
392
|
-
radius: eased(T.morphRadius, easeOut),
|
|
393
|
-
scale: eased(T.morphContent, easeOut),
|
|
394
|
-
blur: eased(T.morphContent, easeOut)
|
|
395
|
-
}
|
|
396
|
-
);
|
|
397
|
-
let settled = false;
|
|
398
|
-
const settle = () => {
|
|
399
|
-
if (settled) return;
|
|
400
|
-
settled = true;
|
|
401
|
-
clearTimeout(timer);
|
|
402
|
-
motion.stop();
|
|
403
|
-
for (const name of ["position", "top", "left", "width", "height", "border-radius", "background", "transform", "transition"]) shell.style.removeProperty(name);
|
|
404
|
-
card.style.removeProperty("width");
|
|
405
|
-
card.style.removeProperty("height");
|
|
406
|
-
for (const name of ["transform", "transform-origin", "filter", "opacity", "width", "transition"]) body.style.removeProperty(name);
|
|
407
|
-
origin.style.transition = "opacity 300ms ease";
|
|
408
|
-
origin.style.opacity = "";
|
|
409
|
-
setTimeout(() => {
|
|
410
|
-
origin.style.transition = "";
|
|
411
|
-
}, 320);
|
|
412
|
-
};
|
|
413
|
-
const timer = setTimeout(settle, T.safetyMs);
|
|
414
|
-
return settle;
|
|
415
|
-
}
|
|
416
|
-
function buildCard(item, dismissLabel) {
|
|
417
|
-
const card = document.createElement("div");
|
|
418
|
-
card.className = "mk-toast";
|
|
419
|
-
card.dataset.mkToast = String(item.id);
|
|
420
|
-
card.dataset.mkTone = item.tone;
|
|
421
|
-
const shell = document.createElement("div");
|
|
422
|
-
shell.className = "mk-toast__shell";
|
|
423
|
-
const body = document.createElement("div");
|
|
424
|
-
body.className = "mk-toast__body";
|
|
425
|
-
const icon = item.icon ? TONE_ICONS[item.tone] : null;
|
|
426
|
-
if (icon) {
|
|
427
|
-
const holder = document.createElement("span");
|
|
428
|
-
holder.className = "mk-toast__icon";
|
|
429
|
-
holder.innerHTML = iconSvg(icon, 18);
|
|
430
|
-
body.append(holder);
|
|
431
|
-
}
|
|
432
|
-
const message = document.createElement("span");
|
|
433
|
-
message.className = "mk-toast__message";
|
|
434
|
-
message.textContent = item.message;
|
|
435
|
-
body.append(message);
|
|
436
|
-
if (item.action) {
|
|
437
|
-
const action = document.createElement("button");
|
|
438
|
-
action.type = "button";
|
|
439
|
-
action.className = "mk-toast__action";
|
|
440
|
-
action.dataset.mkToastAction = "";
|
|
441
|
-
action.textContent = item.action.label;
|
|
442
|
-
body.append(action);
|
|
443
|
-
}
|
|
444
|
-
const dismiss = document.createElement("button");
|
|
445
|
-
dismiss.type = "button";
|
|
446
|
-
dismiss.className = "mk-toast__dismiss";
|
|
447
|
-
dismiss.dataset.mkToastDismiss = "";
|
|
448
|
-
dismiss.setAttribute("aria-label", dismissLabel);
|
|
449
|
-
dismiss.innerHTML = iconSvg("close", 14);
|
|
450
|
-
body.append(dismiss);
|
|
451
|
-
shell.append(body);
|
|
452
|
-
card.append(shell);
|
|
453
|
-
return { card, shell, body, message };
|
|
454
|
-
}
|
|
455
|
-
function createToaster(host, options = {}) {
|
|
456
|
-
if (!host) {
|
|
457
|
-
warnOnce("toaster: missing host");
|
|
458
|
-
return inert();
|
|
459
|
-
}
|
|
460
|
-
registerMotion();
|
|
461
|
-
const dismissLabel = options.labels?.dismiss ?? "Cerrar";
|
|
462
|
-
const scope = createScope();
|
|
463
|
-
const cards = /* @__PURE__ */ new Map();
|
|
464
|
-
const entered = /* @__PURE__ */ new Set();
|
|
465
|
-
const leaving = /* @__PURE__ */ new Set();
|
|
466
|
-
const dragging = /* @__PURE__ */ new Set();
|
|
467
|
-
const settles = /* @__PURE__ */ new Map();
|
|
468
|
-
let expanded = false;
|
|
469
|
-
let collapse;
|
|
470
|
-
const popover = typeof host.showPopover === "function";
|
|
471
|
-
scope.attr(host, "role", "status");
|
|
472
|
-
scope.attr(host, "aria-live", "polite");
|
|
473
|
-
if (popover) scope.attr(host, "popover", "manual");
|
|
474
|
-
const live = () => getToasts().filter((item) => !item.closing).reverse().map((item) => ({ item, entry: cards.get(item.id) })).filter((pair) => pair.entry !== void 0);
|
|
475
|
-
const slotsFor = () => {
|
|
476
|
-
const entries = live();
|
|
477
|
-
return { entries, slots: layout(entries.length, expanded, entries.map(({ entry }) => entry.card.offsetHeight)) };
|
|
478
|
-
};
|
|
479
|
-
const relayout = (duration = TIMING.toast.relayout, ease = easeOutCubic, stagger = 0) => {
|
|
480
|
-
const { entries, slots } = slotsFor();
|
|
481
|
-
entries.forEach(({ item, entry }, index) => {
|
|
482
|
-
const slot = slots[index];
|
|
483
|
-
if (!slot) return;
|
|
484
|
-
entry.card.style.zIndex = String(slot.zIndex);
|
|
485
|
-
entry.card.style.pointerEvents = slot.visible ? "auto" : "none";
|
|
486
|
-
entry.card.setAttribute("aria-hidden", slot.visible ? "false" : "true");
|
|
487
|
-
if (dragging.has(item.id) || !entered.has(item.id)) return;
|
|
488
|
-
gsap.to(entry.card, {
|
|
489
|
-
xPercent: -50,
|
|
490
|
-
y: slot.y,
|
|
491
|
-
scale: slot.scale,
|
|
492
|
-
opacity: slot.opacity,
|
|
493
|
-
duration: reduced() ? 0 : duration,
|
|
494
|
-
ease,
|
|
495
|
-
delay: stagger * index,
|
|
496
|
-
overwrite: "auto"
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
};
|
|
500
|
-
const restingY = (id) => {
|
|
501
|
-
const { entries, slots } = slotsFor();
|
|
502
|
-
return slots[entries.findIndex(({ item }) => item.id === id)]?.y ?? 0;
|
|
503
|
-
};
|
|
504
|
-
const openDeck = () => {
|
|
505
|
-
clearTimeout(collapse);
|
|
506
|
-
if (expanded) return;
|
|
507
|
-
expanded = true;
|
|
508
|
-
for (const { item } of live()) toastTimers.pause(item.id);
|
|
509
|
-
relayout(TIMING.toast.expand, springEase(0.55), TIMING.toast.expandStagger);
|
|
510
|
-
};
|
|
511
|
-
const closeDeck = () => {
|
|
512
|
-
clearTimeout(collapse);
|
|
513
|
-
collapse = setTimeout(() => {
|
|
514
|
-
if (!expanded) return;
|
|
515
|
-
expanded = false;
|
|
516
|
-
for (const { item } of live()) toastTimers.resume(item.id);
|
|
517
|
-
relayout(TIMING.toast.expand, springEase(0.55), TIMING.toast.expandStagger);
|
|
518
|
-
}, TIMING.toast.collapseDelayMs);
|
|
519
|
-
};
|
|
520
|
-
const showLayer = (visible) => {
|
|
521
|
-
if (!popover) return;
|
|
522
|
-
try {
|
|
523
|
-
if (host.matches(":popover-open")) host.hidePopover();
|
|
524
|
-
if (visible) host.showPopover();
|
|
525
|
-
} catch {
|
|
526
|
-
}
|
|
527
|
-
};
|
|
528
|
-
const attachGestures = (item, card) => {
|
|
529
|
-
let start = null;
|
|
530
|
-
let wheel = null;
|
|
531
|
-
const move = (offset) => gsap.set(card, { y: restingY(item.id) + offset, opacity: Math.max(0, 1 + Math.min(0, offset) / DISTANCE.fade) });
|
|
532
|
-
const cancel = () => {
|
|
533
|
-
dragging.delete(item.id);
|
|
534
|
-
gsap.to(card, { y: restingY(item.id), opacity: 1, duration: 0.25, ease: easeOutCubic });
|
|
535
|
-
if (!expanded) toastTimers.resume(item.id);
|
|
536
|
-
};
|
|
537
|
-
card.addEventListener("mouseenter", openDeck);
|
|
538
|
-
card.addEventListener("mouseleave", closeDeck);
|
|
539
|
-
card.addEventListener("pointerdown", (event) => {
|
|
540
|
-
if (event.button !== 0 || event.target.closest('button, a, input, textarea, select, [role="button"]')) return;
|
|
541
|
-
start = { y: event.clientY, type: event.pointerType };
|
|
542
|
-
card.setPointerCapture?.(event.pointerId);
|
|
543
|
-
dragging.add(item.id);
|
|
544
|
-
gsap.killTweensOf(card);
|
|
545
|
-
toastTimers.pause(item.id);
|
|
546
|
-
});
|
|
547
|
-
card.addEventListener("pointermove", (event) => {
|
|
548
|
-
if (!start) return;
|
|
549
|
-
const dy = event.clientY - start.y;
|
|
550
|
-
move(dy < 0 ? dy : start.type === "touch" ? dy * GESTURE.resistance : 0);
|
|
551
|
-
});
|
|
552
|
-
const up = (event) => {
|
|
553
|
-
if (!start) return;
|
|
554
|
-
const dy = event.clientY - start.y;
|
|
555
|
-
const type = start.type;
|
|
556
|
-
start = null;
|
|
557
|
-
if (dy < -GESTURE.swipe) {
|
|
558
|
-
dragging.delete(item.id);
|
|
559
|
-
dismissToast(item.id);
|
|
560
|
-
} else if (type === "touch" && dy > GESTURE.expandDrag) {
|
|
561
|
-
cancel();
|
|
562
|
-
if (expanded) closeDeck();
|
|
563
|
-
else openDeck();
|
|
564
|
-
} else cancel();
|
|
565
|
-
};
|
|
566
|
-
card.addEventListener("pointerup", up);
|
|
567
|
-
card.addEventListener("pointercancel", up);
|
|
568
|
-
card.addEventListener(
|
|
569
|
-
"wheel",
|
|
570
|
-
(event) => {
|
|
571
|
-
const delta = event.deltaMode === 1 ? event.deltaY * 16 : event.deltaMode === 2 ? event.deltaY * innerHeight : event.deltaY;
|
|
572
|
-
if (!wheel) {
|
|
573
|
-
wheel = { offset: 0, end: void 0 };
|
|
574
|
-
dragging.add(item.id);
|
|
575
|
-
toastTimers.pause(item.id);
|
|
576
|
-
}
|
|
577
|
-
wheel.offset = Math.min(0, wheel.offset - delta);
|
|
578
|
-
move(wheel.offset);
|
|
579
|
-
clearTimeout(wheel.end);
|
|
580
|
-
wheel.end = setTimeout(() => {
|
|
581
|
-
const offset = wheel?.offset ?? 0;
|
|
582
|
-
wheel = null;
|
|
583
|
-
if (offset < -GESTURE.wheel) {
|
|
584
|
-
dragging.delete(item.id);
|
|
585
|
-
dismissToast(item.id);
|
|
586
|
-
} else cancel();
|
|
587
|
-
}, GESTURE.wheelEndMs);
|
|
588
|
-
},
|
|
589
|
-
{ passive: true }
|
|
590
|
-
);
|
|
591
|
-
card.addEventListener("click", (event) => {
|
|
592
|
-
const target = event.target;
|
|
593
|
-
if (target.closest("[data-mk-toast-dismiss]")) dismissToast(item.id);
|
|
594
|
-
else if (target.closest("[data-mk-toast-action]")) {
|
|
595
|
-
item.action?.onClick();
|
|
596
|
-
dismissToast(item.id);
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
};
|
|
600
|
-
const finishCard = (id) => {
|
|
601
|
-
settles.get(id)?.();
|
|
602
|
-
settles.delete(id);
|
|
603
|
-
const entry = cards.get(id);
|
|
604
|
-
if (entry) {
|
|
605
|
-
gsap.killTweensOf(entry.card);
|
|
606
|
-
entry.card.remove();
|
|
607
|
-
}
|
|
608
|
-
cards.delete(id);
|
|
609
|
-
entered.delete(id);
|
|
610
|
-
leaving.delete(id);
|
|
611
|
-
dragging.delete(id);
|
|
612
|
-
};
|
|
613
|
-
const render = () => {
|
|
614
|
-
const list = getToasts();
|
|
615
|
-
const ids = new Set(list.map((item) => item.id));
|
|
616
|
-
for (const id of [...cards.keys()]) if (!ids.has(id)) finishCard(id);
|
|
617
|
-
for (const item of list) {
|
|
618
|
-
if (!cards.has(item.id) && !item.closing) {
|
|
619
|
-
const entry = buildCard(item, dismissLabel);
|
|
620
|
-
cards.set(item.id, entry);
|
|
621
|
-
host.append(entry.card);
|
|
622
|
-
gsap.set(entry.card, { xPercent: -50 });
|
|
623
|
-
attachGestures(item, entry.card);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
showLayer(list.some((item) => !item.closing) || leaving.size > 0);
|
|
627
|
-
for (const item of list) {
|
|
628
|
-
const entry = cards.get(item.id);
|
|
629
|
-
if (!entry) continue;
|
|
630
|
-
if (!entered.has(item.id) && !item.closing) {
|
|
631
|
-
entered.add(item.id);
|
|
632
|
-
toastTimers.start(item.id, item.duration);
|
|
633
|
-
relayout();
|
|
634
|
-
gsap.killTweensOf(entry.card);
|
|
635
|
-
gsap.set(entry.card, { xPercent: -50, y: restingY(item.id), scale: 1, opacity: 1 });
|
|
636
|
-
if (expanded) toastTimers.pause(item.id);
|
|
637
|
-
revealLetters(entry.message);
|
|
638
|
-
if (reduced()) continue;
|
|
639
|
-
if (item.origin?.isConnected) settles.set(item.id, morphFrom(entry.card, entry.shell, entry.body, item.origin));
|
|
640
|
-
else {
|
|
641
|
-
const y = restingY(item.id);
|
|
642
|
-
gsap.fromTo(
|
|
643
|
-
entry.card,
|
|
644
|
-
{ y: y - DISTANCE.enter, opacity: 0, filter: `blur(${DISTANCE.enterBlur}px)` },
|
|
645
|
-
{ y, opacity: 1, filter: "blur(0px)", duration: TIMING.toast.enter, ease: springEase(0.58, -3.5), clearProps: "filter" }
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
if (item.closing && !leaving.has(item.id)) {
|
|
650
|
-
leaving.add(item.id);
|
|
651
|
-
toastTimers.clear(item.id);
|
|
652
|
-
entry.card.style.zIndex = String(STACK.baseZ + 1);
|
|
653
|
-
const done = () => {
|
|
654
|
-
finishCard(item.id);
|
|
655
|
-
removeToast(item.id);
|
|
656
|
-
};
|
|
657
|
-
if (reduced()) done();
|
|
658
|
-
else {
|
|
659
|
-
gsap.to(entry.card, {
|
|
660
|
-
y: Number(gsap.getProperty(entry.card, "y")) - DISTANCE.exit,
|
|
661
|
-
opacity: 0,
|
|
662
|
-
filter: `blur(${DISTANCE.exitBlur}px)`,
|
|
663
|
-
duration: TIMING.toast.exit,
|
|
664
|
-
ease: easeInCubic,
|
|
665
|
-
overwrite: "auto",
|
|
666
|
-
onComplete: done
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
relayout();
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
scope.add(subscribeToasts(render));
|
|
674
|
-
render();
|
|
675
|
-
host.dataset.mkReady = "";
|
|
676
|
-
return {
|
|
677
|
-
destroy() {
|
|
678
|
-
clearTimeout(collapse);
|
|
679
|
-
for (const id of [...cards.keys()]) finishCard(id);
|
|
680
|
-
showLayer(false);
|
|
681
|
-
scope.dispose();
|
|
682
|
-
delete host.dataset.mkReady;
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// src/components/chat/engine.ts
|
|
688
|
-
var MESSAGE = "[data-mk-message]";
|
|
689
|
-
var PENDING_ID = "mk-chat-pending";
|
|
690
|
-
var part = (root, name) => root.querySelector(`[data-mk-part="${name}"]`);
|
|
691
|
-
var capturePending = (placeholder) => {
|
|
692
|
-
registerMotion();
|
|
693
|
-
return placeholder ? Flip.getState(placeholder) : null;
|
|
694
|
-
};
|
|
695
|
-
function morphPendingToReply(state, reply, scroller) {
|
|
696
|
-
registerMotion();
|
|
697
|
-
if (!state || reduced()) {
|
|
698
|
-
if (!reduced()) enterMessages([reply]);
|
|
699
|
-
return null;
|
|
700
|
-
}
|
|
701
|
-
const text = reply.querySelector("[data-mk-text], .message-text");
|
|
702
|
-
if (text) text.style.width = `${text.getBoundingClientRect().width}px`;
|
|
703
|
-
reply.setAttribute("data-flip-id", PENDING_ID);
|
|
704
|
-
const words = text && !text.querySelector("table, pre") ? splitWords(text) : null;
|
|
705
|
-
if (words?.parts.length) {
|
|
706
|
-
gsap.fromTo(
|
|
707
|
-
words.parts,
|
|
708
|
-
{ y: 16, autoAlpha: 0, filter: "blur(6px)" },
|
|
709
|
-
{
|
|
710
|
-
y: 0,
|
|
711
|
-
autoAlpha: 1,
|
|
712
|
-
filter: "blur(0px)",
|
|
713
|
-
duration: TIMING.word,
|
|
714
|
-
ease: "back.out(1)",
|
|
715
|
-
stagger: TIMING.wordStagger,
|
|
716
|
-
clearProps: "transform,opacity,visibility,filter",
|
|
717
|
-
onComplete: () => words.revert()
|
|
718
|
-
}
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
return Flip.from(state, {
|
|
722
|
-
targets: reply,
|
|
723
|
-
duration: TIMING.bubble,
|
|
724
|
-
scale: false,
|
|
725
|
-
ease: EASE.signature,
|
|
726
|
-
onUpdate: () => {
|
|
727
|
-
if (scroller) scroller.scrollTop = scroller.scrollHeight;
|
|
728
|
-
},
|
|
729
|
-
onComplete: () => {
|
|
730
|
-
reply.removeAttribute("data-flip-id");
|
|
731
|
-
if (text) text.style.width = "";
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
function chatMessage(role, text) {
|
|
736
|
-
const message = document.createElement("div");
|
|
737
|
-
message.dataset.mkMessage = "";
|
|
738
|
-
message.dataset.mkRole = role;
|
|
739
|
-
const body = document.createElement("span");
|
|
740
|
-
body.dataset.mkText = "";
|
|
741
|
-
body.textContent = text;
|
|
742
|
-
message.append(body);
|
|
743
|
-
return message;
|
|
744
|
-
}
|
|
745
|
-
function createChat(root, options = {}) {
|
|
746
|
-
const messages = root ? part(root, "messages") : null;
|
|
747
|
-
if (!root || !messages) {
|
|
748
|
-
warnOnce('chat: needs a [data-mk-part="messages"] container');
|
|
749
|
-
return inert({
|
|
750
|
-
add: () => document.createElement("div"),
|
|
751
|
-
pending() {
|
|
752
|
-
},
|
|
753
|
-
reply: () => document.createElement("div"),
|
|
754
|
-
confirm() {
|
|
755
|
-
},
|
|
756
|
-
setRecording() {
|
|
757
|
-
},
|
|
758
|
-
scrollToLatest() {
|
|
759
|
-
},
|
|
760
|
-
adopt() {
|
|
761
|
-
},
|
|
762
|
-
messages: null
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
registerMotion();
|
|
766
|
-
const { clearOnSend = true } = options;
|
|
767
|
-
const thinking = options.labels?.thinking ?? "Pensando...";
|
|
768
|
-
const scope = createScope();
|
|
769
|
-
const composer = part(root, "composer");
|
|
770
|
-
const input = composer?.querySelector('[data-mk-part="input"], textarea') ?? null;
|
|
771
|
-
const mic = part(root, "mic");
|
|
772
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
773
|
-
let placeholder = null;
|
|
774
|
-
let pendingTimer;
|
|
775
|
-
let waiting = false;
|
|
776
|
-
scope.attr(messages, "role", "log");
|
|
777
|
-
scope.attr(messages, "aria-live", "polite");
|
|
778
|
-
const existing = [...messages.querySelectorAll(MESSAGE)];
|
|
779
|
-
if (options.animateExisting) enterMessages(existing);
|
|
780
|
-
for (const message of existing) seen.add(message);
|
|
781
|
-
const observer = scope.observe(
|
|
782
|
-
new MutationObserver((records) => {
|
|
783
|
-
const fresh = [];
|
|
784
|
-
for (const record of records) {
|
|
785
|
-
for (const node of record.addedNodes) {
|
|
786
|
-
if (!(node instanceof HTMLElement) || !node.matches(MESSAGE) || seen.has(node)) continue;
|
|
787
|
-
seen.add(node);
|
|
788
|
-
fresh.push(node);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
if (!fresh.length) return;
|
|
792
|
-
enterMessages(fresh);
|
|
793
|
-
scrollToLatest(messages);
|
|
794
|
-
})
|
|
795
|
-
);
|
|
796
|
-
observer.observe(messages, { childList: true });
|
|
797
|
-
const clearPending = () => {
|
|
798
|
-
clearTimeout(pendingTimer);
|
|
799
|
-
pendingTimer = void 0;
|
|
800
|
-
waiting = false;
|
|
801
|
-
};
|
|
802
|
-
const add = (role, text) => {
|
|
803
|
-
const message = chatMessage(role, text);
|
|
804
|
-
messages.append(message);
|
|
805
|
-
return message;
|
|
806
|
-
};
|
|
807
|
-
const pending = () => {
|
|
808
|
-
clearPending();
|
|
809
|
-
waiting = true;
|
|
810
|
-
pendingTimer = setTimeout(() => {
|
|
811
|
-
pendingTimer = void 0;
|
|
812
|
-
if (!waiting || placeholder) return;
|
|
813
|
-
placeholder = document.createElement("div");
|
|
814
|
-
placeholder.dataset.mkMessage = "";
|
|
815
|
-
placeholder.dataset.mkRole = "assistant";
|
|
816
|
-
placeholder.dataset.mkPending = "";
|
|
817
|
-
placeholder.setAttribute("data-flip-id", PENDING_ID);
|
|
818
|
-
placeholder.innerHTML = `<span class="mk-chat__thinking">${iconSvg("sparkle", 18)}<span></span></span>`;
|
|
819
|
-
placeholder.querySelector("span span").textContent = thinking;
|
|
820
|
-
seen.add(placeholder);
|
|
821
|
-
messages.append(placeholder);
|
|
822
|
-
enterMessages([placeholder]);
|
|
823
|
-
scrollToLatest(messages);
|
|
824
|
-
}, TIMING.chat.pendingDelayMs);
|
|
825
|
-
};
|
|
826
|
-
const reply = (content) => {
|
|
827
|
-
const message = typeof content === "string" ? chatMessage("assistant", content) : content;
|
|
828
|
-
clearPending();
|
|
829
|
-
seen.add(message);
|
|
830
|
-
const was = placeholder;
|
|
831
|
-
placeholder = null;
|
|
832
|
-
if (was?.isConnected) {
|
|
833
|
-
const state = capturePending(was);
|
|
834
|
-
was.replaceWith(message);
|
|
835
|
-
morphPendingToReply(state, message, messages);
|
|
836
|
-
} else {
|
|
837
|
-
if (!message.isConnected) messages.append(message);
|
|
838
|
-
enterMessages([message]);
|
|
839
|
-
scrollToLatest(messages);
|
|
840
|
-
}
|
|
841
|
-
return message;
|
|
842
|
-
};
|
|
843
|
-
const confirm = (bar) => {
|
|
844
|
-
if (reduced()) return;
|
|
845
|
-
flowIn(bar);
|
|
846
|
-
const words = splitWords(bar);
|
|
847
|
-
if (!words.parts.length) return;
|
|
848
|
-
gsap.fromTo(
|
|
849
|
-
words.parts,
|
|
850
|
-
{ y: 16, autoAlpha: 0, filter: "blur(6px)" },
|
|
851
|
-
{
|
|
852
|
-
y: 0,
|
|
853
|
-
autoAlpha: 1,
|
|
854
|
-
filter: "blur(0px)",
|
|
855
|
-
duration: TIMING.word,
|
|
856
|
-
ease: "back.out(1)",
|
|
857
|
-
stagger: TIMING.chat.confirmWordStagger,
|
|
858
|
-
clearProps: "transform,opacity,visibility,filter",
|
|
859
|
-
onComplete: () => words.revert()
|
|
860
|
-
}
|
|
861
|
-
);
|
|
862
|
-
};
|
|
863
|
-
const send = () => {
|
|
864
|
-
if (!input) return;
|
|
865
|
-
const text = input.value.trim();
|
|
866
|
-
if (!text) return;
|
|
867
|
-
const allowed = root.dispatchEvent(new CustomEvent("mk:send", { bubbles: true, cancelable: true, detail: { text } }));
|
|
868
|
-
if (!allowed) return;
|
|
869
|
-
options.onSend?.(text);
|
|
870
|
-
if (clearOnSend) {
|
|
871
|
-
input.value = "";
|
|
872
|
-
fitComposer(input);
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
if (input) {
|
|
876
|
-
scope.on(input, "input", () => fitComposer(input));
|
|
877
|
-
scope.on(input, "keydown", (event) => {
|
|
878
|
-
const key = event;
|
|
879
|
-
if (key.key !== "Enter" || key.shiftKey || key.isComposing) return;
|
|
880
|
-
event.preventDefault();
|
|
881
|
-
send();
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
if (composer) {
|
|
885
|
-
scope.on(composer, "submit", (event) => {
|
|
886
|
-
event.preventDefault();
|
|
887
|
-
send();
|
|
888
|
-
});
|
|
889
|
-
const sendButton = part(root, "send");
|
|
890
|
-
if (sendButton && !composer.matches("form")) scope.on(sendButton, "click", send);
|
|
891
|
-
}
|
|
892
|
-
scope.add(() => {
|
|
893
|
-
clearPending();
|
|
894
|
-
placeholder?.remove();
|
|
895
|
-
placeholder = null;
|
|
896
|
-
});
|
|
897
|
-
if (input) fitComposer(input);
|
|
898
|
-
root.dataset.mkReady = "";
|
|
899
|
-
return {
|
|
900
|
-
add,
|
|
901
|
-
pending,
|
|
902
|
-
reply,
|
|
903
|
-
confirm,
|
|
904
|
-
setRecording(on) {
|
|
905
|
-
if (mic) scope.attr(mic, "data-mk-recording", on ? "" : null);
|
|
906
|
-
},
|
|
907
|
-
scrollToLatest: () => scrollToLatest(messages),
|
|
908
|
-
adopt: (message) => seen.add(message),
|
|
909
|
-
messages,
|
|
910
|
-
destroy() {
|
|
911
|
-
scope.dispose();
|
|
912
|
-
if (input) input.style.removeProperty("height");
|
|
913
|
-
delete root.dataset.mkReady;
|
|
914
|
-
}
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
// src/components/nav/engine.ts
|
|
919
|
-
var NAV_STYLES = ["clasica", "bonita", "dock", "guapa", "tasks", "melon", "tunnel"];
|
|
920
|
-
var ITEM = ".mk-nav__buttons .mk-nav__button";
|
|
921
|
-
var isStyle = (value) => NAV_STYLES.includes(value);
|
|
922
|
-
var valueOf = (item) => item.dataset.mkValue ?? item.getAttribute("href") ?? void 0;
|
|
923
|
-
var read = (key) => {
|
|
924
|
-
try {
|
|
925
|
-
return localStorage.getItem(key);
|
|
926
|
-
} catch {
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
|
-
};
|
|
930
|
-
var write = (key, value) => {
|
|
931
|
-
try {
|
|
932
|
-
localStorage.setItem(key, value);
|
|
933
|
-
} catch {
|
|
934
|
-
}
|
|
935
|
-
};
|
|
936
|
-
function revealShell(nav) {
|
|
937
|
-
if (!entrances()) return null;
|
|
938
|
-
const shell = nav.closest(".mk-shell") ?? nav.parentElement;
|
|
939
|
-
if (!shell) return null;
|
|
940
|
-
const origin = nav.querySelector(".mk-nav__button--first") ?? nav.querySelector(ITEM) ?? nav;
|
|
941
|
-
return revealFrom(shell, { origin });
|
|
942
|
-
}
|
|
943
|
-
function createNav(root, options = {}) {
|
|
944
|
-
if (!root) {
|
|
945
|
-
warnOnce("nav: missing root");
|
|
946
|
-
return inert({ select() {
|
|
947
|
-
}, setStyle() {
|
|
948
|
-
}, setAccent() {
|
|
949
|
-
}, index: -1, style: "bonita", items: [] });
|
|
950
|
-
}
|
|
951
|
-
const scope = createScope();
|
|
952
|
-
const items2 = () => [...root.querySelectorAll(ITEM)];
|
|
953
|
-
const fromClass = NAV_STYLES.find((name) => root.classList.contains(`mk-nav--${name}`));
|
|
954
|
-
const saved = options.storageKey ? read(options.storageKey) : null;
|
|
955
|
-
let style = options.style ?? (isStyle(saved) ? saved : void 0) ?? fromClass ?? "bonita";
|
|
956
|
-
const paint = (accent2) => {
|
|
957
|
-
const classes = new Set(root.classList);
|
|
958
|
-
for (const name of NAV_STYLES) classes.delete(`mk-nav--${name}`);
|
|
959
|
-
classes.add("mk-nav");
|
|
960
|
-
classes.add(`mk-nav--${style}`);
|
|
961
|
-
if (accent2) classes.add("mk-nav--accent");
|
|
962
|
-
else classes.delete("mk-nav--accent");
|
|
963
|
-
scope.attr(root, "class", [...classes].join(" "));
|
|
964
|
-
};
|
|
965
|
-
let accent = options.accent ?? root.classList.contains("mk-nav--accent");
|
|
966
|
-
paint(accent);
|
|
967
|
-
if (root.localName !== "nav") scope.attr(root, "role", "navigation");
|
|
968
|
-
if (!root.hasAttribute("aria-label") && !root.hasAttribute("aria-labelledby")) scope.attr(root, "aria-label", options.labels?.nav ?? "Principal");
|
|
969
|
-
const mark = (current) => {
|
|
970
|
-
for (const item of items2()) scope.attr(item, "aria-current", item === current ? "page" : null);
|
|
971
|
-
};
|
|
972
|
-
const find = (target) => {
|
|
973
|
-
const list = items2();
|
|
974
|
-
return typeof target === "number" ? list[target] : list.find((item) => valueOf(item) === target);
|
|
975
|
-
};
|
|
976
|
-
scope.on(root, "click", (event) => {
|
|
977
|
-
const item = event.target?.closest(ITEM);
|
|
978
|
-
if (!item || !root.contains(item)) return;
|
|
979
|
-
const index = items2().indexOf(item);
|
|
980
|
-
const value = valueOf(item);
|
|
981
|
-
const detail = { index, value, item, originalEvent: event };
|
|
982
|
-
if (!root.dispatchEvent(new CustomEvent("mk:navigate", { bubbles: true, cancelable: true, detail }))) {
|
|
983
|
-
event.preventDefault();
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
mark(item);
|
|
987
|
-
options.onSelect?.(index, value, item);
|
|
988
|
-
});
|
|
989
|
-
let reveal = null;
|
|
990
|
-
if (options.reveal) reveal = revealShell(root);
|
|
991
|
-
root.dataset.mkReady = "";
|
|
992
|
-
return {
|
|
993
|
-
select(target) {
|
|
994
|
-
const item = find(target);
|
|
995
|
-
if (!item) warnOnce(`nav: no item ${JSON.stringify(target)}`);
|
|
996
|
-
else mark(item);
|
|
997
|
-
},
|
|
998
|
-
setStyle(next) {
|
|
999
|
-
if (!isStyle(next)) {
|
|
1000
|
-
warnOnce(`nav: unknown style "${String(next)}" (use ${NAV_STYLES.join(", ")})`);
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
style = next;
|
|
1004
|
-
paint(accent);
|
|
1005
|
-
if (options.storageKey) write(options.storageKey, next);
|
|
1006
|
-
},
|
|
1007
|
-
setAccent(on) {
|
|
1008
|
-
accent = on;
|
|
1009
|
-
paint(accent);
|
|
1010
|
-
},
|
|
1011
|
-
get index() {
|
|
1012
|
-
return items2().findIndex((item) => item.getAttribute("aria-current") === "page");
|
|
1013
|
-
},
|
|
1014
|
-
get style() {
|
|
1015
|
-
return style;
|
|
1016
|
-
},
|
|
1017
|
-
get items() {
|
|
1018
|
-
return items2();
|
|
1019
|
-
},
|
|
1020
|
-
destroy() {
|
|
1021
|
-
reveal?.cancel();
|
|
1022
|
-
reveal = null;
|
|
1023
|
-
scope.dispose();
|
|
1024
|
-
delete root.dataset.mkReady;
|
|
1025
|
-
}
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// src/components/cursor/engine.ts
|
|
1030
|
-
var svg = (extra = "") => `<svg aria-hidden="true" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"${extra}><path d="${ICON_PATHS.send}"/></svg>`;
|
|
1031
|
-
function backgroundTone(el) {
|
|
1032
|
-
if (!el) return "light";
|
|
1033
|
-
if (el.closest("img, video, picture, canvas")) return "dark";
|
|
1034
|
-
for (let node = el; node; node = node.parentElement) {
|
|
1035
|
-
const match = getComputedStyle(node).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
1036
|
-
if (!match) continue;
|
|
1037
|
-
const alpha = match[4] === void 0 ? 1 : parseFloat(match[4]);
|
|
1038
|
-
if (alpha <= 0.1) continue;
|
|
1039
|
-
const luminance = (Number(match[1]) * 299 + Number(match[2]) * 587 + Number(match[3]) * 114) / 1e3;
|
|
1040
|
-
return luminance > TIMING.cursor.light ? "light" : "dark";
|
|
1041
|
-
}
|
|
1042
|
-
return "light";
|
|
1043
|
-
}
|
|
1044
|
-
function createCursor(host, options = {}) {
|
|
1045
|
-
const variant = options.variant ?? "chip";
|
|
1046
|
-
const idle = inert({ visible: false, label: "", position: { x: 0, y: 0 }, tone: "light", update() {
|
|
1047
|
-
} });
|
|
1048
|
-
if (!host) {
|
|
1049
|
-
warnOnce("cursor: missing element");
|
|
1050
|
-
return idle;
|
|
1051
|
-
}
|
|
1052
|
-
const coarse = typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
1053
|
-
if (coarse || reduced()) return idle;
|
|
1054
|
-
registerMotion();
|
|
1055
|
-
const scope = createScope();
|
|
1056
|
-
const t = TIMING.cursor;
|
|
1057
|
-
const zone = options.zone ?? (variant === "dot" ? host.parentElement ?? document.body : document.documentElement);
|
|
1058
|
-
scope.attr(host, "class", [host.getAttribute("class"), "mk-cursor", `mk-cursor--${variant}`].filter(Boolean).join(" "));
|
|
1059
|
-
scope.attr(host, "aria-hidden", "true");
|
|
1060
|
-
const pointer = { x: -100, y: -100, placed: false };
|
|
1061
|
-
const position = { x: -100, y: -100 };
|
|
1062
|
-
const velocity = { x: 0, y: 0 };
|
|
1063
|
-
let visible = false;
|
|
1064
|
-
let label = "";
|
|
1065
|
-
let tone = "light";
|
|
1066
|
-
const made = [];
|
|
1067
|
-
let chip = null;
|
|
1068
|
-
let chipLabel = null;
|
|
1069
|
-
let chipIcon = null;
|
|
1070
|
-
let word = null;
|
|
1071
|
-
const size = { width: 0, height: 0 };
|
|
1072
|
-
if (variant === "chip") {
|
|
1073
|
-
chip = document.createElement("span");
|
|
1074
|
-
chip.className = "mk-cursor__chip mk-glass";
|
|
1075
|
-
chip.innerHTML = '<span class="mk-glass__shadow" aria-hidden="true"></span><span class="mk-cursor__icon mk-icon"></span><span class="mk-cursor__label"></span>';
|
|
1076
|
-
chipIcon = chip.querySelector(".mk-cursor__icon");
|
|
1077
|
-
chipLabel = chip.querySelector(".mk-cursor__label");
|
|
1078
|
-
host.append(chip);
|
|
1079
|
-
made.push(chip);
|
|
1080
|
-
gsap.set(chip, { autoAlpha: 0, scale: 0.8 });
|
|
1081
|
-
} else {
|
|
1082
|
-
const arrows = options.arrows ?? "y";
|
|
1083
|
-
scope.attr(host, "data-mk-arrows", arrows);
|
|
1084
|
-
scope.attr(zone, "data-mk-cursor-zone", "");
|
|
1085
|
-
const dot = document.createElement("span");
|
|
1086
|
-
dot.className = "mk-cursor__dot";
|
|
1087
|
-
dot.innerHTML = arrows === "none" ? '<span class="mk-cursor__circle"></span><span class="mk-cursor__text"><span class="mk-cursor__word"></span></span>' : `<span class="mk-cursor__circle"></span><span class="mk-cursor__arrow mk-cursor__arrow--start">${svg()}</span><span class="mk-cursor__text"><span class="mk-cursor__word"></span></span><span class="mk-cursor__arrow mk-cursor__arrow--end">${svg()}</span>`;
|
|
1088
|
-
word = dot.querySelector(".mk-cursor__word");
|
|
1089
|
-
if (word) word.textContent = options.text ?? "";
|
|
1090
|
-
host.append(dot);
|
|
1091
|
-
made.push(dot);
|
|
1092
|
-
scope.attr(host, "data-mk-hidden", "");
|
|
1093
|
-
}
|
|
1094
|
-
const appear = motionSpring(...TIMING.springs.cursorAppear);
|
|
1095
|
-
const showChip = (next) => {
|
|
1096
|
-
if (!chip || !chipLabel || !chipIcon) return;
|
|
1097
|
-
const text = next?.dataset.mkCursor ?? "";
|
|
1098
|
-
if (text) {
|
|
1099
|
-
label = text;
|
|
1100
|
-
chipLabel.textContent = text;
|
|
1101
|
-
chipIcon.textContent = next?.dataset.mkCursorIcon ?? "";
|
|
1102
|
-
chip.classList.toggle("mk-cursor__chip--circle", next?.dataset.mkCursorShape === "circle");
|
|
1103
|
-
const box = chip.getBoundingClientRect();
|
|
1104
|
-
size.width = box.width;
|
|
1105
|
-
size.height = box.height;
|
|
1106
|
-
}
|
|
1107
|
-
if (Boolean(text) === visible) return;
|
|
1108
|
-
visible = Boolean(text);
|
|
1109
|
-
if (!visible) label = "";
|
|
1110
|
-
gsap.to(chip, {
|
|
1111
|
-
autoAlpha: visible ? 1 : 0,
|
|
1112
|
-
scale: visible ? 1 : 0.8,
|
|
1113
|
-
duration: appear.duration,
|
|
1114
|
-
ease: appear.ease,
|
|
1115
|
-
overwrite: "auto"
|
|
1116
|
-
});
|
|
1117
|
-
};
|
|
1118
|
-
let inZone = false;
|
|
1119
|
-
let overHide = false;
|
|
1120
|
-
const syncDot = () => scope.attr(host, "data-mk-hidden", inZone && !overHide ? null : "");
|
|
1121
|
-
const wordFor = (target) => {
|
|
1122
|
-
if (!word) return;
|
|
1123
|
-
const source = target?.closest("[data-mk-cursor]");
|
|
1124
|
-
word.textContent = source && zone.contains(source) ? source.dataset.mkCursor ?? "" : options.text ?? "";
|
|
1125
|
-
};
|
|
1126
|
-
const place = (event) => {
|
|
1127
|
-
if (event.pointerType === "touch") return;
|
|
1128
|
-
pointer.x = event.clientX;
|
|
1129
|
-
pointer.y = event.clientY;
|
|
1130
|
-
if (!pointer.placed) {
|
|
1131
|
-
pointer.placed = true;
|
|
1132
|
-
if (variant === "dot") {
|
|
1133
|
-
position.x = pointer.x;
|
|
1134
|
-
position.y = pointer.y;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1138
|
-
scope.on(window, "pointermove", (event) => place(event), { passive: true });
|
|
1139
|
-
const overTarget = variant === "chip" ? document : zone;
|
|
1140
|
-
scope.on(overTarget, "pointerover", (event) => {
|
|
1141
|
-
const target = event.target;
|
|
1142
|
-
if (variant === "chip") {
|
|
1143
|
-
const source = target?.closest("[data-mk-cursor]") ?? null;
|
|
1144
|
-
showChip(source && zone.contains(source) ? source : null);
|
|
1145
|
-
return;
|
|
1146
|
-
}
|
|
1147
|
-
overHide = Boolean(target?.closest("[data-mk-cursor-hide]"));
|
|
1148
|
-
wordFor(target);
|
|
1149
|
-
syncDot();
|
|
1150
|
-
});
|
|
1151
|
-
if (variant === "dot") {
|
|
1152
|
-
scope.on(zone, "pointerenter", (event) => {
|
|
1153
|
-
place(event);
|
|
1154
|
-
inZone = true;
|
|
1155
|
-
visible = true;
|
|
1156
|
-
syncDot();
|
|
1157
|
-
});
|
|
1158
|
-
scope.on(zone, "pointerleave", () => {
|
|
1159
|
-
inZone = false;
|
|
1160
|
-
visible = false;
|
|
1161
|
-
scope.attr(host, "data-mk-pressed", null);
|
|
1162
|
-
syncDot();
|
|
1163
|
-
});
|
|
1164
|
-
scope.on(zone, "pointerdown", () => scope.attr(host, "data-mk-pressed", ""));
|
|
1165
|
-
scope.on(window, "pointerup", () => scope.attr(host, "data-mk-pressed", null));
|
|
1166
|
-
}
|
|
1167
|
-
const [stiffness, damping] = TIMING.springs.cursorFollow;
|
|
1168
|
-
const update = (dt = 1) => {
|
|
1169
|
-
if (variant === "chip") {
|
|
1170
|
-
const halfW = size.width / 2 + t.gutter;
|
|
1171
|
-
const halfH = size.height / 2 + t.gutter;
|
|
1172
|
-
const tx = size.width ? Math.min(Math.max(pointer.x, halfW), innerWidth - halfW) : pointer.x;
|
|
1173
|
-
const ty = size.height ? Math.min(Math.max(pointer.y, halfH), innerHeight - halfH) : pointer.y;
|
|
1174
|
-
const steps = Math.max(1, Math.ceil(dt * 4));
|
|
1175
|
-
const h = dt / 60 / steps;
|
|
1176
|
-
for (let i = 0; i < steps; i++) {
|
|
1177
|
-
velocity.x += (stiffness * (tx - position.x) - damping * velocity.x) * h;
|
|
1178
|
-
velocity.y += (stiffness * (ty - position.y) - damping * velocity.y) * h;
|
|
1179
|
-
position.x += velocity.x * h;
|
|
1180
|
-
position.y += velocity.y * h;
|
|
1181
|
-
}
|
|
1182
|
-
if (visible && typeof document.elementFromPoint === "function") {
|
|
1183
|
-
tone = backgroundTone(document.elementFromPoint(pointer.x, pointer.y));
|
|
1184
|
-
if (chip && chip.dataset.mkTone !== tone) chip.dataset.mkTone = tone;
|
|
1185
|
-
}
|
|
1186
|
-
} else {
|
|
1187
|
-
const step = 1 - Math.pow(1 - t.dotLerp, dt);
|
|
1188
|
-
position.x += (pointer.x - position.x) * step;
|
|
1189
|
-
position.y += (pointer.y - position.y) * step;
|
|
1190
|
-
}
|
|
1191
|
-
host.style.transform = `translate3d(${position.x.toFixed(2)}px, ${position.y.toFixed(2)}px, 0) translate(-50%, -50%)`;
|
|
1192
|
-
};
|
|
1193
|
-
scope.add(subscribe(update));
|
|
1194
|
-
update(0);
|
|
1195
|
-
host.dataset.mkReady = "";
|
|
1196
|
-
return {
|
|
1197
|
-
get visible() {
|
|
1198
|
-
return visible;
|
|
1199
|
-
},
|
|
1200
|
-
get label() {
|
|
1201
|
-
return label;
|
|
1202
|
-
},
|
|
1203
|
-
get position() {
|
|
1204
|
-
return { ...position };
|
|
1205
|
-
},
|
|
1206
|
-
get tone() {
|
|
1207
|
-
return tone;
|
|
1208
|
-
},
|
|
1209
|
-
update,
|
|
1210
|
-
destroy() {
|
|
1211
|
-
if (chip) gsap.killTweensOf(chip);
|
|
1212
|
-
scope.dispose();
|
|
1213
|
-
for (const node of made.splice(0)) node.remove();
|
|
1214
|
-
host.style.removeProperty("transform");
|
|
1215
|
-
if (host.getAttribute("style") === "") host.removeAttribute("style");
|
|
1216
|
-
delete host.dataset.mkReady;
|
|
1217
|
-
}
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
// src/components/island/engine.ts
|
|
1222
|
-
var sequence = 0;
|
|
1223
|
-
var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1224
|
-
function createIsland(root, options = {}) {
|
|
1225
|
-
const shell = root?.querySelector(".mk-island__shell") ?? null;
|
|
1226
|
-
const panels = root ? [...root.querySelectorAll("[data-mk-island-panel]")] : [];
|
|
1227
|
-
if (!root || !shell || !panels.length) {
|
|
1228
|
-
warnOnce("island: needs .mk-island__shell and at least one [data-mk-island-panel]");
|
|
1229
|
-
return inert({ open() {
|
|
1230
|
-
}, close() {
|
|
1231
|
-
}, toggle() {
|
|
1232
|
-
}, ready() {
|
|
1233
|
-
}, active: null });
|
|
1234
|
-
}
|
|
1235
|
-
const t = TIMING.island;
|
|
1236
|
-
const { entrance = true, closeOnScroll = true, closeOnNavigate = true, focusOnOpen = true, onChange } = options;
|
|
1237
|
-
registerMotion();
|
|
1238
|
-
const scope = createScope();
|
|
1239
|
-
const toggles = [...root.querySelectorAll("[data-mk-island-toggle]")];
|
|
1240
|
-
const cta = root.querySelector(".mk-island__cta");
|
|
1241
|
-
const backdrops = [...root.querySelectorAll("[data-mk-island-close]")];
|
|
1242
|
-
let active = null;
|
|
1243
|
-
let trigger = null;
|
|
1244
|
-
let scrollStart = 0;
|
|
1245
|
-
let armed = false;
|
|
1246
|
-
let focusTimer;
|
|
1247
|
-
let entered = false;
|
|
1248
|
-
let entry = null;
|
|
1249
|
-
const flag = (el, name, on) => {
|
|
1250
|
-
if (el) scope.attr(el, name, on ? "" : null);
|
|
1251
|
-
};
|
|
1252
|
-
const sync = () => {
|
|
1253
|
-
for (const panel of panels) {
|
|
1254
|
-
const on = panel.dataset.mkIslandPanel === active;
|
|
1255
|
-
if (active) flag(panel, "hidden", !on);
|
|
1256
|
-
scope.attr(panel, "aria-hidden", String(!on));
|
|
1257
|
-
flag(panel, "inert", !on);
|
|
1258
|
-
}
|
|
1259
|
-
for (const button of toggles) scope.attr(button, "aria-expanded", String(button.dataset.mkIslandToggle === active));
|
|
1260
|
-
flag(cta, "inert", Boolean(active));
|
|
1261
|
-
for (const backdrop of backdrops) {
|
|
1262
|
-
flag(backdrop, "inert", !active);
|
|
1263
|
-
scope.attr(backdrop, "aria-hidden", String(!active));
|
|
1264
|
-
}
|
|
1265
|
-
flag(root, "data-mk-open", Boolean(active));
|
|
1266
|
-
scope.attr(root, "data-mk-active", active);
|
|
1267
|
-
};
|
|
1268
|
-
const stagger = (opening) => {
|
|
1269
|
-
root.querySelectorAll("[data-mk-island-item]").forEach((item, index) => {
|
|
1270
|
-
scope.attr(item, "style", item.getAttribute("style"));
|
|
1271
|
-
item.style.setProperty("--mk-island-delay", opening ? `${Number((t.delay + index * t.step).toFixed(3))}s` : "0s");
|
|
1272
|
-
});
|
|
1273
|
-
};
|
|
1274
|
-
const close = (returnFocus = true) => {
|
|
1275
|
-
if (scope.disposed || !active) return;
|
|
1276
|
-
clearTimeout(focusTimer);
|
|
1277
|
-
active = null;
|
|
1278
|
-
stagger(false);
|
|
1279
|
-
sync();
|
|
1280
|
-
onChange?.(null);
|
|
1281
|
-
if (returnFocus && trigger?.isConnected) trigger.focus({ preventScroll: true });
|
|
1282
|
-
};
|
|
1283
|
-
const open = (name = panels[0].dataset.mkIslandPanel, source) => {
|
|
1284
|
-
if (scope.disposed) return;
|
|
1285
|
-
const panel = panels.find((el) => el.dataset.mkIslandPanel === name);
|
|
1286
|
-
if (!panel) return;
|
|
1287
|
-
const wasClosed = !active;
|
|
1288
|
-
clearTimeout(focusTimer);
|
|
1289
|
-
active = name;
|
|
1290
|
-
trigger = source ?? toggles.find((el) => el.dataset.mkIslandToggle === name) ?? trigger;
|
|
1291
|
-
if (wasClosed) {
|
|
1292
|
-
scrollStart = window.scrollY;
|
|
1293
|
-
armed = false;
|
|
1294
|
-
stagger(true);
|
|
1295
|
-
}
|
|
1296
|
-
sync();
|
|
1297
|
-
onChange?.(name);
|
|
1298
|
-
if (focusOnOpen)
|
|
1299
|
-
focusTimer = setTimeout(
|
|
1300
|
-
() => {
|
|
1301
|
-
if (active !== name) return;
|
|
1302
|
-
const first = [...panel.querySelectorAll(FOCUSABLE)].find((el) => !el.closest("[hidden], [inert]"));
|
|
1303
|
-
(first ?? panel).focus({ preventScroll: true });
|
|
1304
|
-
},
|
|
1305
|
-
reduced() ? 0 : t.focus
|
|
1306
|
-
);
|
|
1307
|
-
};
|
|
1308
|
-
const toggle = (name = panels[0].dataset.mkIslandPanel, source) => active === name ? close(false) : open(name, source);
|
|
1309
|
-
for (const panel of panels) {
|
|
1310
|
-
if (!panel.id) scope.attr(panel, "id", `mk-island-panel-${++sequence}`);
|
|
1311
|
-
if (!panel.hasAttribute("tabindex")) scope.attr(panel, "tabindex", "-1");
|
|
1312
|
-
if (!panel.hasAttribute("role")) scope.attr(panel, "role", "region");
|
|
1313
|
-
}
|
|
1314
|
-
for (const button of toggles) {
|
|
1315
|
-
const panel = panels.find((el) => el.dataset.mkIslandPanel === button.dataset.mkIslandToggle);
|
|
1316
|
-
if (panel) scope.attr(button, "aria-controls", panel.id);
|
|
1317
|
-
scope.on(button, "click", () => toggle(button.dataset.mkIslandToggle, button));
|
|
1318
|
-
}
|
|
1319
|
-
for (const backdrop of backdrops) scope.on(backdrop, "click", () => close(true));
|
|
1320
|
-
scope.on(document, "keydown", (event) => {
|
|
1321
|
-
if (active && event.key === "Escape") {
|
|
1322
|
-
event.preventDefault();
|
|
1323
|
-
close(true);
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
if (closeOnNavigate)
|
|
1327
|
-
scope.on(root, "click", (event) => {
|
|
1328
|
-
const link = event.target?.closest?.("a[href]");
|
|
1329
|
-
if (active && link?.closest("[data-mk-island-panel]")) close(false);
|
|
1330
|
-
});
|
|
1331
|
-
if (closeOnScroll) {
|
|
1332
|
-
const arm = () => {
|
|
1333
|
-
if (active) armed = true;
|
|
1334
|
-
};
|
|
1335
|
-
scope.on(window, "wheel", arm, { passive: true });
|
|
1336
|
-
scope.on(window, "touchmove", arm, { passive: true });
|
|
1337
|
-
scope.on(
|
|
1338
|
-
window,
|
|
1339
|
-
"scroll",
|
|
1340
|
-
() => {
|
|
1341
|
-
if (active && armed && Math.abs(window.scrollY - scrollStart) > 1) close(false);
|
|
1342
|
-
},
|
|
1343
|
-
{ passive: true }
|
|
1344
|
-
);
|
|
1345
|
-
}
|
|
1346
|
-
scope.add(() => clearTimeout(focusTimer));
|
|
1347
|
-
const moving = entrance !== false && entrances();
|
|
1348
|
-
if (moving) gsap.set(shell, { yPercent: t.offset });
|
|
1349
|
-
const ready = () => {
|
|
1350
|
-
if (scope.disposed || entered || !moving) return;
|
|
1351
|
-
entered = true;
|
|
1352
|
-
entry = gsap.to(shell, { yPercent: 0, duration: t.entrance });
|
|
1353
|
-
};
|
|
1354
|
-
scope.add(() => {
|
|
1355
|
-
entry?.kill();
|
|
1356
|
-
if (moving) gsap.set(shell, { clearProps: "transform,translate" });
|
|
1357
|
-
if (shell.getAttribute("style") === "") shell.removeAttribute("style");
|
|
1358
|
-
});
|
|
1359
|
-
sync();
|
|
1360
|
-
if (entrance === true) ready();
|
|
1361
|
-
scope.attr(root, "data-mk-ready", "");
|
|
1362
|
-
return {
|
|
1363
|
-
open: (name) => open(name),
|
|
1364
|
-
close,
|
|
1365
|
-
toggle: (name) => toggle(name),
|
|
1366
|
-
ready,
|
|
1367
|
-
get active() {
|
|
1368
|
-
return active;
|
|
1369
|
-
},
|
|
1370
|
-
destroy: () => scope.dispose()
|
|
1371
|
-
};
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
export {
|
|
1375
|
-
enterMessages,
|
|
1376
|
-
fitComposer,
|
|
1377
|
-
scrollToLatest,
|
|
1378
|
-
createThemePicker,
|
|
1379
|
-
ICON_PATHS,
|
|
1380
|
-
iconSvg,
|
|
1381
|
-
TOAST_LIMIT,
|
|
1382
|
-
getToasts,
|
|
1383
|
-
subscribeToasts,
|
|
1384
|
-
dismissToast,
|
|
1385
|
-
removeToast,
|
|
1386
|
-
clearToasts,
|
|
1387
|
-
toast,
|
|
1388
|
-
toastTimers,
|
|
1389
|
-
createToaster,
|
|
1390
|
-
PENDING_ID,
|
|
1391
|
-
capturePending,
|
|
1392
|
-
morphPendingToReply,
|
|
1393
|
-
chatMessage,
|
|
1394
|
-
createChat,
|
|
1395
|
-
NAV_STYLES,
|
|
1396
|
-
revealShell,
|
|
1397
|
-
createNav,
|
|
1398
|
-
createCursor,
|
|
1399
|
-
createIsland
|
|
1400
|
-
};
|
|
1401
|
-
//# sourceMappingURL=chunk-6P4P7NU6.js.map
|