@fluix-ui/vanilla 0.0.8 → 0.0.9

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.
@@ -68,7 +68,7 @@ var Fluix = (function (exports) {
68
68
  samples.push([t, 1]);
69
69
  return samples;
70
70
  }
71
- function animateSpring(el, properties, config) {
71
+ function animateSpring(el2, properties, config) {
72
72
  const samples = simulateSpring(config);
73
73
  const totalTime = samples[samples.length - 1][0];
74
74
  const durationMs = Math.round(totalTime * 1e3);
@@ -85,7 +85,7 @@ var Fluix = (function (exports) {
85
85
  keyframes.push(frame);
86
86
  }
87
87
  try {
88
- return el.animate(keyframes, {
88
+ return el2.animate(keyframes, {
89
89
  duration: durationMs,
90
90
  fill: "forwards",
91
91
  easing: "linear"
@@ -216,13 +216,17 @@ var Fluix = (function (exports) {
216
216
  });
217
217
  }
218
218
  function dismiss(id) {
219
- const { toasts } = store.getSnapshot();
220
- const item = toasts.find((t) => t.id === id);
221
- if (!item || item.exiting) return;
222
- store.update((prev) => ({
223
- ...prev,
224
- toasts: prev.toasts.map((t) => t.id === id ? { ...t, exiting: true } : t)
225
- }));
219
+ let shouldScheduleRemoval = false;
220
+ store.update((prev) => {
221
+ const item = prev.toasts.find((t) => t.id === id);
222
+ if (!item || item.exiting) return prev;
223
+ shouldScheduleRemoval = true;
224
+ return {
225
+ ...prev,
226
+ toasts: prev.toasts.map((t) => t.id === id ? { ...t, exiting: true } : t)
227
+ };
228
+ });
229
+ if (!shouldScheduleRemoval) return;
226
230
  const timer = setTimeout(() => {
227
231
  exitTimers.delete(id);
228
232
  store.update((prev) => ({
@@ -233,16 +237,20 @@ var Fluix = (function (exports) {
233
237
  exitTimers.set(id, timer);
234
238
  }
235
239
  function clear(position) {
236
- store.update((prev) => ({
237
- ...prev,
238
- toasts: position ? prev.toasts.filter((t) => t.position !== position) : []
239
- }));
240
+ store.update((prev) => {
241
+ if (prev.toasts.length === 0) return prev;
242
+ const filtered = position ? prev.toasts.filter((t) => t.position !== position) : [];
243
+ if (filtered.length === prev.toasts.length) return prev;
244
+ return { ...prev, toasts: filtered };
245
+ });
240
246
  }
241
247
  function configure(config) {
242
- store.update((prev) => ({
243
- ...prev,
244
- config: { ...prev.config, ...config }
245
- }));
248
+ store.update((prev) => {
249
+ const merged = { ...prev.config, ...config };
250
+ const keys = Object.keys(merged);
251
+ if (keys.every((k) => prev.config[k] === merged[k])) return prev;
252
+ return { ...prev, config: merged };
253
+ });
246
254
  }
247
255
  function destroy() {
248
256
  for (const timer of exitTimers.values()) clearTimeout(timer);
@@ -492,7 +500,12 @@ var Fluix = (function (exports) {
492
500
  });
493
501
  }
494
502
  function configure(config) {
495
- store.update((prev) => ({ ...prev, config: { ...prev.config, ...config } }));
503
+ store.update((prev) => {
504
+ const merged = { ...prev.config, ...config };
505
+ const keys = Object.keys(merged);
506
+ if (keys.every((k) => prev.config[k] === merged[k])) return prev;
507
+ return { ...prev, config: merged };
508
+ });
496
509
  }
497
510
  function destroy() {
498
511
  }
@@ -694,7 +707,11 @@ var Fluix = (function (exports) {
694
707
  if (!a || !b) return false;
695
708
  return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height && a.radius === b.radius && a.visible === b.visible;
696
709
  }
710
+ var springCache = /* @__PURE__ */ new Map();
697
711
  function simulateSpringValues(config) {
712
+ const key = `${config.stiffness}-${config.damping}-${config.mass}`;
713
+ const cached = springCache.get(key);
714
+ if (cached) return cached;
698
715
  const { stiffness, damping, mass } = config;
699
716
  const dt = 1 / 120;
700
717
  const maxDuration = 3;
@@ -714,6 +731,7 @@ var Fluix = (function (exports) {
714
731
  if (Math.abs(position - 1) < 1e-3 && Math.abs(velocity) < 1e-3) break;
715
732
  }
716
733
  samples.push(1);
734
+ springCache.set(key, samples);
717
735
  return samples;
718
736
  }
719
737
  function lerp(a, b, t) {
@@ -923,74 +941,146 @@ var Fluix = (function (exports) {
923
941
  requestAnimationFrame(tick);
924
942
  return handle;
925
943
  }
926
- var STRETCH_MS = 150;
927
- function animatePillMorph(rect, from, to, spring) {
928
- const stretchedX = Math.min(from.x, to.x);
929
- const stretchedRight = Math.max(from.x + from.width, to.x + to.width);
930
- const stretchedWidth = stretchedRight - stretchedX;
931
- const contractSamples = simulateSpringValues({
932
- stiffness: spring.stiffness * 2.5,
933
- damping: spring.damping * 1.6,
944
+ function setRectAttrs(el2, x, y, w, h, r) {
945
+ el2.setAttribute("x", String(x));
946
+ el2.setAttribute("y", String(y));
947
+ el2.setAttribute("width", String(Math.max(0, w)));
948
+ el2.setAttribute("height", String(Math.max(0, h)));
949
+ el2.setAttribute("rx", String(Math.max(0, r)));
950
+ el2.setAttribute("ry", String(Math.max(0, r)));
951
+ }
952
+ function animatePillMorph(rect, ghost, from, to, spring, _orientation) {
953
+ const growSamples = simulateSpringValues({
954
+ stiffness: spring.stiffness * 1.4,
955
+ damping: spring.damping * 1.2,
934
956
  mass: spring.mass
935
957
  });
936
- const contractCount = contractSamples.length;
937
- const contractMs = contractCount / 120 * 1e3;
938
- const totalMs = STRETCH_MS + contractMs;
958
+ const growCount = growSamples.length;
959
+ const growMs = growCount / 120 * 1e3;
960
+ const totalMs = growMs;
961
+ const ghostDuration = totalMs * 0.8;
939
962
  const startTime = performance.now();
940
963
  let cancelled = false;
941
964
  const handle = {
942
965
  onfinish: null,
943
966
  cancel() {
944
967
  cancelled = true;
968
+ if (ghost) ghost.setAttribute("opacity", "0");
945
969
  }
946
970
  };
947
- function applyRect(x, y, w, h, r) {
948
- rect.setAttribute("x", String(x));
949
- rect.setAttribute("y", String(y));
950
- rect.setAttribute("width", String(w));
951
- rect.setAttribute("height", String(h));
952
- rect.setAttribute("rx", String(r));
953
- rect.setAttribute("ry", String(r));
971
+ if (ghost) {
972
+ setRectAttrs(ghost, from.x, from.y, from.width, from.height, from.radius);
973
+ ghost.setAttribute("opacity", "1");
954
974
  }
975
+ const toCx = to.x + to.width / 2;
976
+ const toCy = to.y + to.height / 2;
977
+ setRectAttrs(rect, toCx, toCy, 0, 0, 0);
955
978
  function tick() {
956
979
  if (cancelled) return;
957
980
  const elapsed = performance.now() - startTime;
958
- if (elapsed < STRETCH_MS) {
959
- const t = easeOutCubic(elapsed / STRETCH_MS);
960
- applyRect(
961
- lerp(from.x, stretchedX, t),
962
- lerp(from.y, to.y, t),
963
- lerp(from.width, stretchedWidth, t),
964
- lerp(from.height, to.height, t),
965
- lerp(from.radius, to.radius, t)
966
- );
967
- } else {
968
- const phaseElapsed = elapsed - STRETCH_MS;
969
- const phaseProgress = Math.min(phaseElapsed / contractMs, 1);
970
- const idx = Math.min(Math.floor(phaseProgress * (contractCount - 1)), contractCount - 1);
971
- const t = contractSamples[idx];
972
- applyRect(
973
- lerp(stretchedX, to.x, t),
974
- to.y,
975
- lerp(stretchedWidth, to.width, t),
976
- to.height,
977
- to.radius
978
- );
981
+ const progress = Math.min(elapsed / totalMs, 1);
982
+ const idx = Math.min(Math.floor(progress * (growCount - 1)), growCount - 1);
983
+ const gt = growSamples[idx];
984
+ setRectAttrs(
985
+ rect,
986
+ lerp(toCx, to.x, gt),
987
+ lerp(toCy, to.y, gt),
988
+ lerp(0, to.width, gt),
989
+ lerp(0, to.height, gt),
990
+ lerp(0, to.radius, gt)
991
+ );
992
+ if (ghost) {
993
+ if (elapsed < ghostDuration) {
994
+ const raw = elapsed / ghostDuration;
995
+ const shrink = raw * raw;
996
+ const scale = 1 - shrink;
997
+ const drift = easeOutCubic(raw) * 0.25;
998
+ const cx = lerp(from.x + from.width / 2, to.x + to.width / 2, drift);
999
+ const cy = lerp(from.y + from.height / 2, to.y + to.height / 2, drift);
1000
+ const w = from.width * scale;
1001
+ const h = from.height * scale;
1002
+ setRectAttrs(
1003
+ ghost,
1004
+ cx - w / 2,
1005
+ cy - h / 2,
1006
+ w,
1007
+ h,
1008
+ from.radius * scale
1009
+ );
1010
+ } else {
1011
+ ghost.setAttribute("opacity", "0");
1012
+ }
979
1013
  }
980
- if (elapsed < totalMs) {
1014
+ if (progress < 1) {
981
1015
  requestAnimationFrame(tick);
982
1016
  } else {
1017
+ if (ghost) ghost.setAttribute("opacity", "0");
983
1018
  handle.onfinish?.();
984
1019
  }
985
1020
  }
986
1021
  requestAnimationFrame(tick);
987
1022
  return handle;
988
1023
  }
1024
+ function buildPillWaypoints(root, oldId, newId, padding, variant, orientation, from, to) {
1025
+ if (!oldId || !newId) return [from, to];
1026
+ const items = Array.from(root.querySelectorAll(ITEM_SELECTOR));
1027
+ const ids = items.map((el2) => el2.dataset["menuId"] ?? "");
1028
+ const oldIdx = ids.indexOf(oldId);
1029
+ const newIdx = ids.indexOf(newId);
1030
+ if (oldIdx === -1 || newIdx === -1 || Math.abs(newIdx - oldIdx) <= 1) {
1031
+ return [from, to];
1032
+ }
1033
+ const step = newIdx > oldIdx ? 1 : -1;
1034
+ const waypoints = [from];
1035
+ for (let i = oldIdx + step; i !== newIdx; i += step) {
1036
+ const id = ids[i];
1037
+ const frame = readItemFrame(root, id, padding, variant, orientation);
1038
+ if (frame) waypoints.push(frame);
1039
+ }
1040
+ waypoints.push(to);
1041
+ return waypoints;
1042
+ }
1043
+ function chainPillMorphs(rect, ghost, waypoints, spring, orientation) {
1044
+ if (waypoints.length <= 2) {
1045
+ return animatePillMorph(rect, ghost, waypoints[0], waypoints[waypoints.length - 1], spring);
1046
+ }
1047
+ const steps = waypoints.length - 1;
1048
+ let currentStep = 0;
1049
+ let activeAnim = null;
1050
+ const stepSpring = {
1051
+ stiffness: spring.stiffness * (1 + steps * 0.6),
1052
+ damping: spring.damping * (1 + steps * 0.3),
1053
+ mass: spring.mass
1054
+ };
1055
+ const handle = {
1056
+ onfinish: null,
1057
+ cancel() {
1058
+ activeAnim?.cancel();
1059
+ activeAnim = null;
1060
+ }
1061
+ };
1062
+ function runStep() {
1063
+ if (currentStep >= steps) {
1064
+ handle.onfinish?.();
1065
+ return;
1066
+ }
1067
+ const from = waypoints[currentStep];
1068
+ const to = waypoints[currentStep + 1];
1069
+ activeAnim = animatePillMorph(rect, ghost, from, to, stepSpring);
1070
+ activeAnim.onfinish = () => {
1071
+ currentStep++;
1072
+ setRectAttrs(rect, to.x, to.y, to.width, to.height, to.radius);
1073
+ runStep();
1074
+ };
1075
+ }
1076
+ runStep();
1077
+ return handle;
1078
+ }
989
1079
  function connectMenu(options) {
990
1080
  const spring = options.spring ?? FLUIX_SPRING;
991
- const padding = options.padding ?? 6;
992
1081
  const variant = options.variant;
993
1082
  const orientation = options.orientation;
1083
+ const padding = options.padding ?? (variant === "pill" ? 0 : 6);
994
1084
  const cleanups = [];
995
1085
  let currentAnimation = null;
996
1086
  let lastFrame = null;
@@ -1002,11 +1092,11 @@ var Fluix = (function (exports) {
1002
1092
  let animNewId = null;
1003
1093
  let animPhase = null;
1004
1094
  function setItemState(id, state) {
1005
- const el = options.root.querySelector(
1095
+ const el2 = options.root.querySelector(
1006
1096
  `${ITEM_SELECTOR}[data-menu-id="${CSS.escape(id)}"]`
1007
1097
  );
1008
- if (el && el.dataset["state"] !== state) {
1009
- el.dataset["state"] = state;
1098
+ if (el2 && el2.dataset["state"] !== state) {
1099
+ el2.dataset["state"] = state;
1010
1100
  }
1011
1101
  }
1012
1102
  function enforceAnimStates() {
@@ -1026,6 +1116,9 @@ var Fluix = (function (exports) {
1026
1116
  }
1027
1117
  function apply(frame) {
1028
1118
  applyFrame(options.indicator, frame, variant, orientation);
1119
+ if (options.ghostIndicator) {
1120
+ options.ghostIndicator.setAttribute("opacity", "0");
1121
+ }
1029
1122
  }
1030
1123
  const updateIndicator = (immediate = false) => {
1031
1124
  const activeId = options.getActiveId();
@@ -1138,11 +1231,23 @@ var Fluix = (function (exports) {
1138
1231
  onEnterStart
1139
1232
  );
1140
1233
  } else {
1141
- animation = animatePillMorph(
1142
- options.indicator,
1234
+ const waypoints = buildPillWaypoints(
1235
+ options.root,
1236
+ oldActiveId,
1237
+ newActiveId,
1238
+ padding,
1239
+ variant,
1240
+ orientation,
1143
1241
  from,
1144
- to,
1145
- { stiffness: spring.stiffness ?? 170, damping: spring.damping ?? 18, mass: spring.mass ?? 1 }
1242
+ to
1243
+ );
1244
+ const springConfig = { stiffness: spring.stiffness ?? 170, damping: spring.damping ?? 18, mass: spring.mass ?? 1 };
1245
+ const ghost = options.ghostIndicator ?? null;
1246
+ animation = chainPillMorphs(
1247
+ options.indicator,
1248
+ ghost,
1249
+ waypoints,
1250
+ springConfig
1146
1251
  );
1147
1252
  }
1148
1253
  if (!animation) {
@@ -1167,7 +1272,7 @@ var Fluix = (function (exports) {
1167
1272
  }
1168
1273
  };
1169
1274
  };
1170
- const sync = (immediate = false) => {
1275
+ const sync2 = (immediate = false) => {
1171
1276
  cancelAnimationFrame(rafId);
1172
1277
  rafId = requestAnimationFrame(() => updateIndicator(immediate));
1173
1278
  };
@@ -1185,7 +1290,7 @@ var Fluix = (function (exports) {
1185
1290
  cleanups.push(() => options.root.removeEventListener("click", handleClick));
1186
1291
  const scheduleSync = () => {
1187
1292
  if (animPhase) return;
1188
- sync(false);
1293
+ sync2(false);
1189
1294
  };
1190
1295
  resizeObserver = new ResizeObserver(scheduleSync);
1191
1296
  resizeObserver.observe(options.root);
@@ -1196,18 +1301,24 @@ var Fluix = (function (exports) {
1196
1301
  resizeObserver?.disconnect();
1197
1302
  resizeObserver = null;
1198
1303
  });
1199
- mutationObserver = new MutationObserver(() => {
1200
- if (animPhase) {
1201
- enforceAnimStates();
1202
- return;
1203
- }
1304
+ function rebuildResizeObserver() {
1204
1305
  if (!resizeObserver) return;
1205
1306
  resizeObserver.disconnect();
1206
1307
  resizeObserver.observe(options.root);
1207
1308
  for (const item of options.root.querySelectorAll(ITEM_SELECTOR)) {
1208
1309
  resizeObserver.observe(item);
1209
1310
  }
1210
- sync(false);
1311
+ }
1312
+ mutationObserver = new MutationObserver((mutations) => {
1313
+ if (animPhase) {
1314
+ enforceAnimStates();
1315
+ return;
1316
+ }
1317
+ const hasStructuralChange = mutations.some((m) => m.type === "childList");
1318
+ if (hasStructuralChange) {
1319
+ rebuildResizeObserver();
1320
+ }
1321
+ sync2(false);
1211
1322
  });
1212
1323
  mutationObserver.observe(options.root, {
1213
1324
  childList: true,
@@ -1219,9 +1330,9 @@ var Fluix = (function (exports) {
1219
1330
  mutationObserver?.disconnect();
1220
1331
  mutationObserver = null;
1221
1332
  });
1222
- sync(true);
1333
+ sync2(true);
1223
1334
  return {
1224
- sync,
1335
+ sync: sync2,
1225
1336
  destroy() {
1226
1337
  cancelAnimationFrame(rafId);
1227
1338
  if (currentAnimation) {
@@ -1234,125 +1345,99 @@ var Fluix = (function (exports) {
1234
1345
  };
1235
1346
  }
1236
1347
 
1237
- // src/toast.ts
1238
- var WIDTH = 350;
1239
- var HEIGHT = 40;
1240
- var PILL_PADDING = 10;
1241
- var MIN_EXPAND_RATIO = 2.25;
1242
- var HEADER_EXIT_MS = 600 * 0.7;
1243
- var BODY_MERGE_OVERLAP = 6;
1348
+ // src/shared.ts
1244
1349
  var SVG_NS = "http://www.w3.org/2000/svg";
1350
+ var GOO_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10";
1351
+ function applyAttrs(el2, attrs) {
1352
+ for (const [key, value] of Object.entries(attrs)) {
1353
+ el2.setAttribute(key, value);
1354
+ }
1355
+ }
1356
+ function clearChildren(el2) {
1357
+ while (el2.firstChild) el2.removeChild(el2.firstChild);
1358
+ }
1359
+ function createGooeyFilter(filterId, blur2) {
1360
+ const defs = document.createElementNS(SVG_NS, "defs");
1361
+ const filter = document.createElementNS(SVG_NS, "filter");
1362
+ filter.setAttribute("id", filterId);
1363
+ for (const [k, v] of Object.entries({ x: "-50%", y: "-50%", width: "200%", height: "200%" })) {
1364
+ filter.setAttribute(k, v);
1365
+ }
1366
+ filter.setAttribute("color-interpolation-filters", "sRGB");
1367
+ const feBlur = document.createElementNS(SVG_NS, "feGaussianBlur");
1368
+ feBlur.setAttribute("in", "SourceGraphic");
1369
+ feBlur.setAttribute("stdDeviation", String(blur2));
1370
+ feBlur.setAttribute("result", "blur");
1371
+ const feCM = document.createElementNS(SVG_NS, "feColorMatrix");
1372
+ feCM.setAttribute("in", "blur");
1373
+ feCM.setAttribute("type", "matrix");
1374
+ feCM.setAttribute("values", GOO_MATRIX);
1375
+ feCM.setAttribute("result", "goo");
1376
+ const feComp = document.createElementNS(SVG_NS, "feComposite");
1377
+ feComp.setAttribute("in", "SourceGraphic");
1378
+ feComp.setAttribute("in2", "goo");
1379
+ feComp.setAttribute("operator", "atop");
1380
+ filter.appendChild(feBlur);
1381
+ filter.appendChild(feCM);
1382
+ filter.appendChild(feComp);
1383
+ defs.appendChild(filter);
1384
+ const g = document.createElementNS(SVG_NS, "g");
1385
+ g.setAttribute("filter", `url(#${filterId})`);
1386
+ return { g, defs, feBlur };
1387
+ }
1388
+ function zeroRect(el2) {
1389
+ for (const attr of ["x", "y", "width", "height", "rx", "ry"]) {
1390
+ el2.setAttribute(attr, "0");
1391
+ }
1392
+ }
1393
+
1394
+ // src/toast/icons.ts
1395
+ function el(tag, attrs) {
1396
+ return { tag, attrs };
1397
+ }
1398
+ function line(x1, y1, x2, y2) {
1399
+ return el("line", { x1, y1, x2, y2 });
1400
+ }
1401
+ var ICON_DEFS = {
1402
+ success: { children: [el("polyline", { points: "20 6 9 17 4 12" })] },
1403
+ error: { children: [line("18", "6", "6", "18"), line("6", "6", "18", "18")] },
1404
+ warning: { children: [
1405
+ el("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
1406
+ line("12", "9", "12", "13"),
1407
+ line("12", "17", "12.01", "17")
1408
+ ] },
1409
+ info: { children: [
1410
+ el("circle", { cx: "12", cy: "12", r: "10" }),
1411
+ line("12", "16", "12", "12"),
1412
+ line("12", "8", "12.01", "8")
1413
+ ] },
1414
+ loading: { spin: true, children: [
1415
+ line("12", "2", "12", "6"),
1416
+ line("12", "18", "12", "22"),
1417
+ line("4.93", "4.93", "7.76", "7.76"),
1418
+ line("16.24", "16.24", "19.07", "19.07"),
1419
+ line("2", "12", "6", "12"),
1420
+ line("18", "12", "22", "12"),
1421
+ line("4.93", "19.07", "7.76", "16.24"),
1422
+ line("16.24", "7.76", "19.07", "4.93")
1423
+ ] },
1424
+ action: { children: [
1425
+ el("circle", { cx: "12", cy: "12", r: "10" }),
1426
+ el("polygon", { points: "10 8 16 12 10 16 10 8", fill: "currentColor", stroke: "none" })
1427
+ ] }
1428
+ };
1245
1429
  function createSvgIcon(state) {
1430
+ const def = ICON_DEFS[state];
1431
+ if (!def) return null;
1246
1432
  const svg = document.createElementNS(SVG_NS, "svg");
1247
- svg.setAttribute("width", "14");
1248
- svg.setAttribute("height", "14");
1249
- svg.setAttribute("viewBox", "0 0 24 24");
1250
- svg.setAttribute("fill", "none");
1251
- svg.setAttribute("stroke", "currentColor");
1252
- svg.setAttribute("stroke-width", "2.5");
1253
- svg.setAttribute("stroke-linecap", "round");
1254
- svg.setAttribute("stroke-linejoin", "round");
1255
- svg.setAttribute("aria-hidden", "true");
1256
- switch (state) {
1257
- case "success": {
1258
- const p = document.createElementNS(SVG_NS, "polyline");
1259
- p.setAttribute("points", "20 6 9 17 4 12");
1260
- svg.appendChild(p);
1261
- break;
1262
- }
1263
- case "error": {
1264
- const l1 = document.createElementNS(SVG_NS, "line");
1265
- l1.setAttribute("x1", "18");
1266
- l1.setAttribute("y1", "6");
1267
- l1.setAttribute("x2", "6");
1268
- l1.setAttribute("y2", "18");
1269
- const l2 = document.createElementNS(SVG_NS, "line");
1270
- l2.setAttribute("x1", "6");
1271
- l2.setAttribute("y1", "6");
1272
- l2.setAttribute("x2", "18");
1273
- l2.setAttribute("y2", "18");
1274
- svg.appendChild(l1);
1275
- svg.appendChild(l2);
1276
- break;
1277
- }
1278
- case "warning": {
1279
- const path = document.createElementNS(SVG_NS, "path");
1280
- path.setAttribute(
1281
- "d",
1282
- "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
1283
- );
1284
- const l1 = document.createElementNS(SVG_NS, "line");
1285
- l1.setAttribute("x1", "12");
1286
- l1.setAttribute("y1", "9");
1287
- l1.setAttribute("x2", "12");
1288
- l1.setAttribute("y2", "13");
1289
- const l2 = document.createElementNS(SVG_NS, "line");
1290
- l2.setAttribute("x1", "12");
1291
- l2.setAttribute("y1", "17");
1292
- l2.setAttribute("x2", "12.01");
1293
- l2.setAttribute("y2", "17");
1294
- svg.appendChild(path);
1295
- svg.appendChild(l1);
1296
- svg.appendChild(l2);
1297
- break;
1298
- }
1299
- case "info": {
1300
- const c = document.createElementNS(SVG_NS, "circle");
1301
- c.setAttribute("cx", "12");
1302
- c.setAttribute("cy", "12");
1303
- c.setAttribute("r", "10");
1304
- const l1 = document.createElementNS(SVG_NS, "line");
1305
- l1.setAttribute("x1", "12");
1306
- l1.setAttribute("y1", "16");
1307
- l1.setAttribute("x2", "12");
1308
- l1.setAttribute("y2", "12");
1309
- const l2 = document.createElementNS(SVG_NS, "line");
1310
- l2.setAttribute("x1", "12");
1311
- l2.setAttribute("y1", "8");
1312
- l2.setAttribute("x2", "12.01");
1313
- l2.setAttribute("y2", "8");
1314
- svg.appendChild(c);
1315
- svg.appendChild(l1);
1316
- svg.appendChild(l2);
1317
- break;
1318
- }
1319
- case "loading": {
1320
- svg.setAttribute("data-fluix-icon", "spin");
1321
- const lines = [
1322
- ["12", "2", "12", "6"],
1323
- ["12", "18", "12", "22"],
1324
- ["4.93", "4.93", "7.76", "7.76"],
1325
- ["16.24", "16.24", "19.07", "19.07"],
1326
- ["2", "12", "6", "12"],
1327
- ["18", "12", "22", "12"],
1328
- ["4.93", "19.07", "7.76", "16.24"],
1329
- ["16.24", "7.76", "19.07", "4.93"]
1330
- ];
1331
- for (const [x1, y1, x2, y2] of lines) {
1332
- const l = document.createElementNS(SVG_NS, "line");
1333
- l.setAttribute("x1", x1);
1334
- l.setAttribute("y1", y1);
1335
- l.setAttribute("x2", x2);
1336
- l.setAttribute("y2", y2);
1337
- svg.appendChild(l);
1338
- }
1339
- break;
1340
- }
1341
- case "action": {
1342
- const c = document.createElementNS(SVG_NS, "circle");
1343
- c.setAttribute("cx", "12");
1344
- c.setAttribute("cy", "12");
1345
- c.setAttribute("r", "10");
1346
- const poly = document.createElementNS(SVG_NS, "polygon");
1347
- poly.setAttribute("points", "10 8 16 12 10 16 10 8");
1348
- poly.setAttribute("fill", "currentColor");
1349
- poly.setAttribute("stroke", "none");
1350
- svg.appendChild(c);
1351
- svg.appendChild(poly);
1352
- break;
1353
- }
1354
- default:
1355
- return null;
1433
+ for (const [k, v] of Object.entries({ width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2.5", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" })) {
1434
+ svg.setAttribute(k, v);
1435
+ }
1436
+ if (def.spin) svg.setAttribute("data-fluix-icon", "spin");
1437
+ for (const child of def.children) {
1438
+ const node = document.createElementNS(SVG_NS, child.tag);
1439
+ for (const [k, v] of Object.entries(child.attrs)) node.setAttribute(k, v);
1440
+ svg.appendChild(node);
1356
1441
  }
1357
1442
  return svg;
1358
1443
  }
@@ -1372,77 +1457,27 @@ var Fluix = (function (exports) {
1372
1457
  const svgIcon = createSvgIcon(state);
1373
1458
  if (svgIcon) container.appendChild(svgIcon);
1374
1459
  }
1375
- function getPillAlign2(position) {
1376
- if (position.includes("right")) return "right";
1377
- if (position.includes("center")) return "center";
1378
- return "left";
1379
- }
1380
- function applyAttrs(el, attrs) {
1381
- for (const [key, value] of Object.entries(attrs)) {
1382
- el.setAttribute(key, value);
1383
- }
1384
- }
1385
- function resolveOffsetValue(v) {
1386
- return typeof v === "number" ? `${v}px` : v;
1387
- }
1388
- function applyViewportOffset(el, offset, position) {
1389
- el.style.top = "";
1390
- el.style.right = "";
1391
- el.style.bottom = "";
1392
- el.style.left = "";
1393
- el.style.paddingLeft = "";
1394
- el.style.paddingRight = "";
1395
- if (offset == null) return;
1396
- let top;
1397
- let right;
1398
- let bottom;
1399
- let left;
1400
- if (typeof offset === "number" || typeof offset === "string") {
1401
- const v = resolveOffsetValue(offset);
1402
- top = v;
1403
- right = v;
1404
- bottom = v;
1405
- left = v;
1406
- } else {
1407
- if (offset.top != null) top = resolveOffsetValue(offset.top);
1408
- if (offset.right != null) right = resolveOffsetValue(offset.right);
1409
- if (offset.bottom != null) bottom = resolveOffsetValue(offset.bottom);
1410
- if (offset.left != null) left = resolveOffsetValue(offset.left);
1411
- }
1412
- if (position.startsWith("top") && top) el.style.top = top;
1413
- if (position.startsWith("bottom") && bottom) el.style.bottom = bottom;
1414
- if (position.endsWith("right") && right) el.style.right = right;
1415
- if (position.endsWith("left") && left) el.style.left = left;
1416
- if (position.endsWith("center")) {
1417
- if (left) el.style.paddingLeft = left;
1418
- if (right) el.style.paddingRight = right;
1419
- }
1420
- }
1421
- function setTimer(inst, fn, ms) {
1422
- const id = setTimeout(() => {
1423
- inst.timers.delete(id);
1424
- fn();
1425
- }, ms);
1426
- inst.timers.add(id);
1427
- return id;
1428
- }
1460
+
1461
+ // src/toast/update.ts
1462
+ var PILL_PADDING = 10;
1463
+ var MIN_EXPAND_RATIO = 2.25;
1464
+ var HEADER_EXIT_MS = 600 * 0.7;
1465
+ var BODY_MERGE_OVERLAP = 6;
1429
1466
  function computeLayout(item, inst) {
1430
1467
  const { ready, expanded: isExpanded } = inst.localState;
1431
1468
  const roundness = item.roundness ?? TOAST_DEFAULTS.roundness;
1432
- const blur = Math.min(10, Math.max(6, roundness * 0.45));
1469
+ const blur2 = Math.min(10, Math.max(6, roundness * 0.45));
1433
1470
  const hasDesc = Boolean(item.description) || Boolean(item.button);
1434
1471
  const isLoading = item.state === "loading";
1435
1472
  const open = hasDesc && isExpanded && !isLoading;
1436
- const position = getPillAlign2(item.position);
1473
+ const position = getPillAlignLocal(item.position);
1437
1474
  const edge = item.position.startsWith("top") ? "bottom" : "top";
1438
1475
  const resolvedPillWidth = Math.max(inst.pillWidth || HEIGHT, HEIGHT);
1439
- const pillHeight = HEIGHT + blur * 3;
1476
+ const pillHeight = HEIGHT + blur2 * 3;
1440
1477
  const pillX = position === "right" ? WIDTH - resolvedPillWidth : position === "center" ? (WIDTH - resolvedPillWidth) / 2 : 0;
1441
1478
  const minExpanded = HEIGHT * MIN_EXPAND_RATIO;
1442
1479
  const rawExpanded = hasDesc ? Math.max(minExpanded, HEIGHT + inst.contentHeight) : minExpanded;
1443
- if (open) {
1444
- inst.frozenExpanded = rawExpanded;
1445
- }
1480
+ if (open) inst.frozenExpanded = rawExpanded;
1446
1481
  const expanded = open ? rawExpanded : inst.frozenExpanded;
1447
1482
  const expandedContent = Math.max(0, expanded - HEIGHT);
1448
1483
  const svgHeight = hasDesc ? Math.max(expanded, minExpanded) : HEIGHT;
@@ -1450,7 +1485,7 @@ var Fluix = (function (exports) {
1450
1485
  ready,
1451
1486
  isExpanded,
1452
1487
  roundness,
1453
- blur,
1488
+ blur: blur2,
1454
1489
  hasDesc,
1455
1490
  isLoading,
1456
1491
  open,
@@ -1465,244 +1500,19 @@ var Fluix = (function (exports) {
1465
1500
  svgHeight
1466
1501
  };
1467
1502
  }
1468
- function createInstance(item, machine2) {
1469
- const localState = { ready: false, expanded: false };
1470
- const roundness = item.roundness ?? TOAST_DEFAULTS.roundness;
1471
- const blur = Math.min(10, Math.max(6, roundness * 0.45));
1472
- const filterId = `fluix-gooey-${item.id.replace(/[^a-z0-9-]/gi, "-")}`;
1473
- const hasDesc = Boolean(item.description) || Boolean(item.button);
1474
- const el = document.createElement("button");
1475
- el.type = "button";
1476
- const canvasDiv = document.createElement("div");
1477
- const minExpanded = HEIGHT * MIN_EXPAND_RATIO;
1478
- const initialSvgHeight = hasDesc ? minExpanded : HEIGHT;
1479
- const svg = document.createElementNS(SVG_NS, "svg");
1480
- svg.setAttribute("data-fluix-svg", "");
1481
- svg.setAttribute("width", String(WIDTH));
1482
- svg.setAttribute("height", String(initialSvgHeight));
1483
- svg.setAttribute("viewBox", `0 0 ${WIDTH} ${initialSvgHeight}`);
1484
- svg.setAttribute("aria-hidden", "true");
1485
- const defs = document.createElementNS(SVG_NS, "defs");
1486
- const filter = document.createElementNS(SVG_NS, "filter");
1487
- filter.setAttribute("id", filterId);
1488
- filter.setAttribute("x", "-20%");
1489
- filter.setAttribute("y", "-20%");
1490
- filter.setAttribute("width", "140%");
1491
- filter.setAttribute("height", "140%");
1492
- filter.setAttribute("color-interpolation-filters", "sRGB");
1493
- const feBlur = document.createElementNS(SVG_NS, "feGaussianBlur");
1494
- feBlur.setAttribute("in", "SourceGraphic");
1495
- feBlur.setAttribute("stdDeviation", String(blur));
1496
- feBlur.setAttribute("result", "blur");
1497
- const feCM = document.createElementNS(SVG_NS, "feColorMatrix");
1498
- feCM.setAttribute("in", "blur");
1499
- feCM.setAttribute("type", "matrix");
1500
- feCM.setAttribute("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10");
1501
- feCM.setAttribute("result", "goo");
1502
- const feComp = document.createElementNS(SVG_NS, "feComposite");
1503
- feComp.setAttribute("in", "SourceGraphic");
1504
- feComp.setAttribute("in2", "goo");
1505
- feComp.setAttribute("operator", "atop");
1506
- filter.appendChild(feBlur);
1507
- filter.appendChild(feCM);
1508
- filter.appendChild(feComp);
1509
- defs.appendChild(filter);
1510
- svg.appendChild(defs);
1511
- const g = document.createElementNS(SVG_NS, "g");
1512
- g.setAttribute("filter", `url(#${filterId})`);
1513
- const initialPillX = getPillAlign2(item.position) === "right" ? WIDTH - HEIGHT : getPillAlign2(item.position) === "center" ? (WIDTH - HEIGHT) / 2 : 0;
1514
- const pillEl = document.createElementNS(SVG_NS, "rect");
1515
- pillEl.setAttribute("data-fluix-pill", "");
1516
- pillEl.setAttribute("x", String(initialPillX));
1517
- pillEl.setAttribute("y", "0");
1518
- pillEl.setAttribute("width", String(HEIGHT));
1519
- pillEl.setAttribute("height", String(HEIGHT));
1520
- pillEl.setAttribute("rx", String(roundness));
1521
- pillEl.setAttribute("ry", String(roundness));
1522
- pillEl.setAttribute("fill", item.fill ?? "var(--fluix-surface-contrast)");
1523
- const bodyEl = document.createElementNS(SVG_NS, "rect");
1524
- bodyEl.setAttribute("data-fluix-body", "");
1525
- bodyEl.setAttribute("x", "0");
1526
- bodyEl.setAttribute("y", String(HEIGHT));
1527
- bodyEl.setAttribute("width", String(WIDTH));
1528
- bodyEl.setAttribute("height", "0");
1529
- bodyEl.setAttribute("rx", String(roundness));
1530
- bodyEl.setAttribute("ry", String(roundness));
1531
- bodyEl.setAttribute("fill", item.fill ?? "var(--fluix-surface-contrast)");
1532
- bodyEl.setAttribute("opacity", "0");
1533
- g.appendChild(pillEl);
1534
- g.appendChild(bodyEl);
1535
- svg.appendChild(g);
1536
- canvasDiv.appendChild(svg);
1537
- el.appendChild(canvasDiv);
1538
- const headerEl = document.createElement("div");
1539
- const headerStackEl = document.createElement("div");
1540
- headerStackEl.setAttribute("data-fluix-header-stack", "");
1541
- const innerEl = document.createElement("div");
1542
- innerEl.setAttribute("data-fluix-header-inner", "");
1543
- innerEl.setAttribute("data-layer", "current");
1544
- const badgeEl = document.createElement("div");
1545
- renderIconInto(badgeEl, item.icon, item.state);
1546
- const titleEl = document.createElement("span");
1547
- titleEl.textContent = item.title ?? item.state;
1548
- innerEl.appendChild(badgeEl);
1549
- innerEl.appendChild(titleEl);
1550
- headerStackEl.appendChild(innerEl);
1551
- headerEl.appendChild(headerStackEl);
1552
- el.appendChild(headerEl);
1553
- let contentEl = null;
1554
- let descriptionEl = null;
1555
- if (hasDesc) {
1556
- contentEl = document.createElement("div");
1557
- descriptionEl = document.createElement("div");
1558
- if (item.styles?.description) {
1559
- descriptionEl.className = item.styles.description;
1560
- }
1561
- if (item.description != null) {
1562
- if (typeof item.description === "string") {
1563
- descriptionEl.textContent = item.description;
1564
- } else if (item.description instanceof HTMLElement) {
1565
- descriptionEl.appendChild(item.description);
1566
- }
1567
- }
1568
- if (item.button) {
1569
- const btn = document.createElement("button");
1570
- btn.type = "button";
1571
- btn.textContent = item.button.title;
1572
- if (item.styles?.button) {
1573
- btn.className = item.styles.button;
1574
- }
1575
- btn.addEventListener("click", (e) => {
1576
- e.stopPropagation();
1577
- item.button?.onClick();
1578
- });
1579
- descriptionEl.appendChild(btn);
1580
- }
1581
- contentEl.appendChild(descriptionEl);
1582
- el.appendChild(contentEl);
1583
- }
1584
- const attrs = Toaster.getAttrs(item, localState);
1585
- applyAttrs(el, attrs.root);
1586
- applyAttrs(canvasDiv, attrs.canvas);
1587
- applyAttrs(headerEl, attrs.header);
1588
- applyAttrs(badgeEl, attrs.badge);
1589
- if (item.styles?.badge) badgeEl.className = item.styles.badge;
1590
- applyAttrs(titleEl, attrs.title);
1591
- if (item.styles?.title) titleEl.className = item.styles.title;
1592
- if (contentEl) applyAttrs(contentEl, attrs.content);
1593
- if (descriptionEl) applyAttrs(descriptionEl, attrs.description);
1594
- if (item.button && descriptionEl) {
1595
- const btnEl = descriptionEl.querySelector("button");
1596
- if (btnEl) applyAttrs(btnEl, attrs.button);
1597
- }
1598
- const headerKey = `${item.state}-${item.title ?? item.state}`;
1599
- const inst = {
1600
- el,
1601
- pillEl,
1602
- bodyEl,
1603
- svgEl: svg,
1604
- headerEl,
1605
- innerEl,
1606
- headerStackEl,
1607
- contentEl,
1608
- descriptionEl,
1609
- localState,
1610
- pillWidth: 0,
1611
- contentHeight: 0,
1612
- frozenExpanded: HEIGHT * MIN_EXPAND_RATIO,
1613
- hovering: false,
1614
- pendingDismiss: false,
1615
- dismissRequested: false,
1616
- pillRo: null,
1617
- contentRo: null,
1618
- pillAnim: null,
1619
- prevPill: { x: initialPillX, width: HEIGHT, height: HEIGHT },
1620
- pillFirst: true,
1621
- headerKey,
1622
- headerPad: null,
1623
- connectHandle: null,
1624
- timers: /* @__PURE__ */ new Set(),
1625
- item
1626
- };
1627
- inst.pillRo = new ResizeObserver(() => {
1628
- requestAnimationFrame(() => {
1629
- measurePillWidth(inst);
1630
- applyVars(inst, inst.item);
1631
- animatePill(inst, inst.item);
1632
- });
1633
- });
1634
- inst.pillRo.observe(innerEl);
1635
- if (descriptionEl) {
1636
- inst.contentRo = new ResizeObserver(() => {
1637
- requestAnimationFrame(() => {
1638
- const h = descriptionEl.scrollHeight;
1639
- if (h !== inst.contentHeight) {
1640
- inst.contentHeight = h;
1641
- applyVars(inst, inst.item);
1642
- }
1643
- });
1644
- });
1645
- inst.contentRo.observe(descriptionEl);
1646
- }
1647
- inst.connectHandle = Toaster.connect(
1648
- el,
1649
- {
1650
- onExpand: () => {
1651
- if (inst.item.exiting || inst.dismissRequested) return;
1652
- inst.localState.expanded = true;
1653
- applyUpdate(inst, inst.item);
1654
- },
1655
- onCollapse: () => {
1656
- if (inst.item.exiting || inst.dismissRequested) return;
1657
- if (inst.item.autopilot !== false) return;
1658
- inst.localState.expanded = false;
1659
- applyUpdate(inst, inst.item);
1660
- },
1661
- onDismiss: () => {
1662
- if (inst.dismissRequested) return;
1663
- inst.dismissRequested = true;
1664
- machine2.dismiss(item.id);
1665
- },
1666
- onHoverStart: () => {
1667
- inst.hovering = true;
1668
- },
1669
- onHoverEnd: () => {
1670
- inst.hovering = false;
1671
- if (inst.pendingDismiss) {
1672
- inst.pendingDismiss = false;
1673
- if (inst.dismissRequested) return;
1674
- inst.dismissRequested = true;
1675
- machine2.dismiss(inst.item.id);
1676
- }
1677
- }
1678
- },
1679
- item
1680
- );
1681
- applyVars(inst, item);
1682
- setTimer(
1683
- inst,
1684
- () => {
1685
- inst.localState.ready = true;
1686
- applyUpdate(inst, inst.item);
1687
- setupAutopilot(inst, inst.item);
1688
- },
1689
- 32
1690
- );
1691
- setupAutoDismiss(inst, item, machine2);
1692
- measurePillWidth(inst);
1693
- return inst;
1694
- }
1695
- function measurePillWidth(inst) {
1696
- if (!inst.el.isConnected) return;
1697
- if (inst.headerPad === null) {
1698
- const cs = getComputedStyle(inst.headerEl);
1699
- inst.headerPad = Number.parseFloat(cs.paddingLeft) + Number.parseFloat(cs.paddingRight);
1700
- }
1701
- const intrinsicWidth = inst.innerEl.getBoundingClientRect().width;
1702
- const w = intrinsicWidth + inst.headerPad + PILL_PADDING;
1703
- if (w > PILL_PADDING && w !== inst.pillWidth) {
1704
- inst.pillWidth = w;
1503
+ function getPillAlignLocal(position) {
1504
+ if (position.includes("right")) return "right";
1505
+ if (position.includes("center")) return "center";
1506
+ return "left";
1507
+ }
1508
+ function measurePillWidth(inst) {
1509
+ if (!inst.el.isConnected) return;
1510
+ if (inst.headerPad === null) {
1511
+ const cs = getComputedStyle(inst.headerEl);
1512
+ inst.headerPad = Number.parseFloat(cs.paddingLeft) + Number.parseFloat(cs.paddingRight);
1705
1513
  }
1514
+ const w = inst.innerEl.getBoundingClientRect().width + inst.headerPad + PILL_PADDING;
1515
+ if (w > PILL_PADDING && w !== inst.pillWidth) inst.pillWidth = w;
1706
1516
  }
1707
1517
  function applyVars(inst, item) {
1708
1518
  const layout = computeLayout(item, inst);
@@ -1719,15 +1529,12 @@ var Fluix = (function (exports) {
1719
1529
  "--_bh": `${open ? expandedContent : 0}px`,
1720
1530
  "--_bo": `${open ? 1 : 0}`
1721
1531
  };
1722
- for (const [key, value] of Object.entries(vars)) {
1723
- inst.el.style.setProperty(key, value);
1724
- }
1532
+ for (const [key, value] of Object.entries(vars)) inst.el.style.setProperty(key, value);
1725
1533
  inst.svgEl.setAttribute("height", String(svgHeight));
1726
1534
  inst.svgEl.setAttribute("viewBox", `0 0 ${WIDTH} ${svgHeight}`);
1727
1535
  }
1728
1536
  function animatePill(inst, item) {
1729
- const layout = computeLayout(item, inst);
1730
- const { open, resolvedPillWidth, pillX, pillHeight } = layout;
1537
+ const { open, resolvedPillWidth, pillX, pillHeight } = computeLayout(item, inst);
1731
1538
  const prev = inst.prevPill;
1732
1539
  const next = { x: pillX, width: resolvedPillWidth, height: open ? pillHeight : HEIGHT };
1733
1540
  if (prev.x === next.x && prev.width === next.width && prev.height === next.height) return;
@@ -1740,88 +1547,59 @@ var Fluix = (function (exports) {
1740
1547
  inst.prevPill = next;
1741
1548
  return;
1742
1549
  }
1743
- const anim = animateSpring(
1744
- inst.pillEl,
1745
- {
1746
- x: { from: prev.x, to: next.x, unit: "px" },
1747
- width: { from: prev.width, to: next.width, unit: "px" },
1748
- height: { from: prev.height, to: next.height, unit: "px" }
1749
- },
1750
- FLUIX_SPRING
1751
- );
1752
- inst.pillAnim = anim;
1550
+ inst.pillAnim = animateSpring(inst.pillEl, {
1551
+ x: { from: prev.x, to: next.x, unit: "px" },
1552
+ width: { from: prev.width, to: next.width, unit: "px" },
1553
+ height: { from: prev.height, to: next.height, unit: "px" }
1554
+ }, FLUIX_SPRING);
1753
1555
  inst.prevPill = next;
1754
1556
  }
1557
+ function setTimer(inst, fn, ms) {
1558
+ const id = setTimeout(() => {
1559
+ inst.timers.delete(id);
1560
+ fn();
1561
+ }, ms);
1562
+ inst.timers.add(id);
1563
+ return id;
1564
+ }
1755
1565
  function setupAutoDismiss(inst, item, machine2) {
1756
1566
  const duration = item.duration;
1757
1567
  if (duration == null || duration <= 0) return;
1758
- setTimer(
1759
- inst,
1760
- () => {
1761
- if (inst.hovering) {
1762
- inst.pendingDismiss = true;
1763
- setTimer(
1764
- inst,
1765
- () => {
1766
- if (inst.dismissRequested) return;
1767
- inst.dismissRequested = true;
1768
- inst.pendingDismiss = false;
1769
- machine2.dismiss(item.id);
1770
- },
1771
- 1200
1772
- );
1773
- return;
1774
- }
1775
- inst.pendingDismiss = false;
1776
- inst.dismissRequested = true;
1777
- machine2.dismiss(item.id);
1778
- },
1779
- duration
1780
- );
1568
+ setTimer(inst, () => {
1569
+ if (inst.hovering) {
1570
+ inst.pendingDismiss = true;
1571
+ setTimer(inst, () => {
1572
+ if (inst.dismissRequested) return;
1573
+ inst.dismissRequested = true;
1574
+ inst.pendingDismiss = false;
1575
+ machine2.dismiss(item.id);
1576
+ }, 1200);
1577
+ return;
1578
+ }
1579
+ inst.pendingDismiss = false;
1580
+ inst.dismissRequested = true;
1581
+ machine2.dismiss(item.id);
1582
+ }, duration);
1781
1583
  }
1782
1584
  function setupAutopilot(inst, item, machine2) {
1783
1585
  if (item.autoExpandDelayMs != null && item.autoExpandDelayMs > 0) {
1784
- setTimer(
1785
- inst,
1786
- () => {
1787
- if (!inst.hovering) {
1788
- inst.localState.expanded = true;
1789
- applyUpdate(inst, inst.item);
1790
- }
1791
- },
1792
- item.autoExpandDelayMs
1793
- );
1586
+ setTimer(inst, () => {
1587
+ if (!inst.hovering) {
1588
+ inst.localState.expanded = true;
1589
+ applyUpdate(inst, inst.item);
1590
+ }
1591
+ }, item.autoExpandDelayMs);
1794
1592
  }
1795
1593
  if (item.autoCollapseDelayMs != null && item.autoCollapseDelayMs > 0) {
1796
- setTimer(
1797
- inst,
1798
- () => {
1799
- if (!inst.hovering) {
1800
- inst.localState.expanded = false;
1801
- applyUpdate(inst, inst.item);
1802
- }
1803
- },
1804
- item.autoCollapseDelayMs
1805
- );
1594
+ setTimer(inst, () => {
1595
+ if (!inst.hovering) {
1596
+ inst.localState.expanded = false;
1597
+ applyUpdate(inst, inst.item);
1598
+ }
1599
+ }, item.autoCollapseDelayMs);
1806
1600
  }
1807
1601
  }
1808
- function applyUpdate(inst, item, _machine) {
1809
- inst.item = item;
1810
- const attrs = Toaster.getAttrs(item, inst.localState);
1811
- applyAttrs(inst.el, attrs.root);
1812
- const canvasEl = inst.el.querySelector("[data-fluix-canvas]");
1813
- if (canvasEl) applyAttrs(canvasEl, attrs.canvas);
1814
- applyAttrs(inst.headerEl, attrs.header);
1815
- const badgeEl = inst.innerEl.querySelector("[data-fluix-badge]");
1816
- if (badgeEl) {
1817
- applyAttrs(badgeEl, attrs.badge);
1818
- if (item.styles?.badge) badgeEl.className = item.styles.badge;
1819
- }
1820
- const titleEl = inst.innerEl.querySelector("[data-fluix-title]");
1821
- if (titleEl) {
1822
- applyAttrs(titleEl, attrs.title);
1823
- if (item.styles?.title) titleEl.className = item.styles.title;
1824
- }
1602
+ function updateDescription(inst, item, attrs) {
1825
1603
  const hasDesc = Boolean(item.description) || Boolean(item.button);
1826
1604
  if (hasDesc && !inst.contentEl) {
1827
1605
  const contentEl = document.createElement("div");
@@ -1842,37 +1620,50 @@ var Fluix = (function (exports) {
1842
1620
  inst.contentRo.observe(descriptionEl);
1843
1621
  }
1844
1622
  if (inst.contentEl) applyAttrs(inst.contentEl, attrs.content);
1845
- if (inst.descriptionEl) {
1846
- applyAttrs(inst.descriptionEl, attrs.description);
1847
- if (item.styles?.description) {
1848
- inst.descriptionEl.className = item.styles.description;
1849
- }
1850
- const existingBtn = inst.descriptionEl.querySelector("[data-fluix-button]");
1851
- inst.descriptionEl.textContent = "";
1852
- if (item.description != null) {
1853
- if (typeof item.description === "string") {
1854
- inst.descriptionEl.textContent = item.description;
1855
- } else if (item.description instanceof HTMLElement) {
1856
- inst.descriptionEl.appendChild(item.description);
1857
- }
1858
- }
1859
- if (item.button) {
1860
- let btnEl = existingBtn;
1861
- if (!btnEl) {
1862
- btnEl = document.createElement("button");
1863
- btnEl.type = "button";
1864
- }
1865
- btnEl.textContent = item.button.title;
1866
- if (item.styles?.button) btnEl.className = item.styles.button;
1867
- applyAttrs(btnEl, attrs.button);
1868
- const newBtn = btnEl.cloneNode(true);
1869
- newBtn.addEventListener("click", (e) => {
1870
- e.stopPropagation();
1871
- item.button?.onClick();
1872
- });
1873
- inst.descriptionEl.appendChild(newBtn);
1874
- }
1623
+ if (!inst.descriptionEl) return;
1624
+ applyAttrs(inst.descriptionEl, attrs.description);
1625
+ if (item.styles?.description) inst.descriptionEl.className = item.styles.description;
1626
+ const existingBtn = inst.descriptionEl.querySelector("[data-fluix-button]");
1627
+ inst.descriptionEl.textContent = "";
1628
+ if (item.description != null) {
1629
+ if (typeof item.description === "string") inst.descriptionEl.textContent = item.description;
1630
+ else if (item.description instanceof HTMLElement) inst.descriptionEl.appendChild(item.description);
1631
+ }
1632
+ if (item.button) {
1633
+ let btnEl = existingBtn;
1634
+ if (!btnEl) {
1635
+ btnEl = document.createElement("button");
1636
+ btnEl.type = "button";
1637
+ }
1638
+ btnEl.textContent = item.button.title;
1639
+ if (item.styles?.button) btnEl.className = item.styles.button;
1640
+ applyAttrs(btnEl, attrs.button);
1641
+ const newBtn = btnEl.cloneNode(true);
1642
+ newBtn.addEventListener("click", (e) => {
1643
+ e.stopPropagation();
1644
+ item.button?.onClick();
1645
+ });
1646
+ inst.descriptionEl.appendChild(newBtn);
1647
+ }
1648
+ }
1649
+ function applyUpdate(inst, item, _machine) {
1650
+ inst.item = item;
1651
+ const attrs = Toaster.getAttrs(item, inst.localState);
1652
+ applyAttrs(inst.el, attrs.root);
1653
+ const canvasEl = inst.el.querySelector("[data-fluix-canvas]");
1654
+ if (canvasEl) applyAttrs(canvasEl, attrs.canvas);
1655
+ applyAttrs(inst.headerEl, attrs.header);
1656
+ const badgeEl = inst.innerEl.querySelector("[data-fluix-badge]");
1657
+ if (badgeEl) {
1658
+ applyAttrs(badgeEl, attrs.badge);
1659
+ if (item.styles?.badge) badgeEl.className = item.styles.badge;
1660
+ }
1661
+ const titleEl = inst.innerEl.querySelector("[data-fluix-title]");
1662
+ if (titleEl) {
1663
+ applyAttrs(titleEl, attrs.title);
1664
+ if (item.styles?.title) titleEl.className = item.styles.title;
1875
1665
  }
1666
+ updateDescription(inst, item, attrs);
1876
1667
  inst.pillEl.setAttribute("fill", item.fill ?? "var(--fluix-surface-contrast)");
1877
1668
  inst.bodyEl.setAttribute("fill", item.fill ?? "var(--fluix-surface-contrast)");
1878
1669
  const newHeaderKey = `${item.state}-${item.title ?? item.state}`;
@@ -1904,13 +1695,9 @@ var Fluix = (function (exports) {
1904
1695
  inst.innerEl = newInner;
1905
1696
  inst.pillRo.unobserve(oldInner);
1906
1697
  inst.pillRo.observe(newInner);
1907
- setTimer(
1908
- inst,
1909
- () => {
1910
- oldInner.remove();
1911
- },
1912
- HEADER_EXIT_MS
1913
- );
1698
+ setTimer(inst, () => {
1699
+ oldInner.remove();
1700
+ }, HEADER_EXIT_MS);
1914
1701
  requestAnimationFrame(() => {
1915
1702
  measurePillWidth(inst);
1916
1703
  applyVars(inst, inst.item);
@@ -1926,560 +1713,874 @@ var Fluix = (function (exports) {
1926
1713
  inst.connectHandle.destroy();
1927
1714
  inst.el.remove();
1928
1715
  }
1929
- function createToaster(config) {
1930
- const machine2 = Toaster.getMachine();
1931
- let currentConfig = config;
1932
- if (currentConfig) machine2.configure(currentConfig);
1933
- const instances = /* @__PURE__ */ new Map();
1934
- const viewports = /* @__PURE__ */ new Map();
1935
- function ensureViewport(position, layout, offset) {
1936
- let vp = viewports.get(position);
1937
- if (!vp) {
1938
- vp = document.createElement("section");
1939
- const vpAttrs = Toaster.getViewportAttrs(position, layout);
1940
- applyAttrs(vp, vpAttrs);
1941
- applyViewportOffset(vp, offset, position);
1942
- document.body.appendChild(vp);
1943
- viewports.set(position, vp);
1944
- }
1945
- return vp;
1716
+
1717
+ // src/toast/instance.ts
1718
+ var WIDTH = 350;
1719
+ var HEIGHT = 40;
1720
+ var MIN_EXPAND_RATIO2 = 2.25;
1721
+ function getPillAlign2(position) {
1722
+ if (position.includes("right")) return "right";
1723
+ if (position.includes("center")) return "center";
1724
+ return "left";
1725
+ }
1726
+ function buildInstanceSvg(item, roundness, blur2) {
1727
+ const filterId = `fluix-gooey-${item.id.replace(/[^a-z0-9-]/gi, "-")}`;
1728
+ const hasDesc = Boolean(item.description) || Boolean(item.button);
1729
+ const initialSvgHeight = hasDesc ? HEIGHT * MIN_EXPAND_RATIO2 : HEIGHT;
1730
+ const svg = document.createElementNS(SVG_NS, "svg");
1731
+ svg.setAttribute("data-fluix-svg", "");
1732
+ svg.setAttribute("width", String(WIDTH));
1733
+ svg.setAttribute("height", String(initialSvgHeight));
1734
+ svg.setAttribute("viewBox", `0 0 ${WIDTH} ${initialSvgHeight}`);
1735
+ svg.setAttribute("aria-hidden", "true");
1736
+ const { g, defs } = createGooeyFilter(filterId, blur2);
1737
+ svg.appendChild(defs);
1738
+ const initialPillX = getPillAlign2(item.position) === "right" ? WIDTH - HEIGHT : getPillAlign2(item.position) === "center" ? (WIDTH - HEIGHT) / 2 : 0;
1739
+ const fill = item.fill ?? "var(--fluix-surface-contrast)";
1740
+ const pillEl = document.createElementNS(SVG_NS, "rect");
1741
+ pillEl.setAttribute("data-fluix-pill", "");
1742
+ for (const [k, v] of Object.entries({ x: initialPillX, y: 0, width: HEIGHT, height: HEIGHT, rx: roundness, ry: roundness })) {
1743
+ pillEl.setAttribute(k, String(v));
1946
1744
  }
1947
- function removeViewport(position) {
1948
- const vp = viewports.get(position);
1949
- if (vp) {
1950
- vp.remove();
1951
- viewports.delete(position);
1952
- }
1745
+ pillEl.setAttribute("fill", fill);
1746
+ const bodyEl = document.createElementNS(SVG_NS, "rect");
1747
+ bodyEl.setAttribute("data-fluix-body", "");
1748
+ for (const [k, v] of Object.entries({ x: 0, y: HEIGHT, width: WIDTH, height: 0, rx: roundness, ry: roundness })) {
1749
+ bodyEl.setAttribute(k, String(v));
1953
1750
  }
1954
- function sync() {
1955
- const next = machine2.store.getSnapshot();
1956
- const resolvedLayout = next.config?.layout ?? currentConfig?.layout ?? "stack";
1957
- const resolvedOffset = next.config?.offset ?? currentConfig?.offset;
1958
- const activePositions = /* @__PURE__ */ new Set();
1959
- const nextIds = new Set(next.toasts.map((t) => t.id));
1960
- for (const [id, inst] of instances) {
1961
- if (!nextIds.has(id)) {
1962
- destroyInstance(inst);
1963
- instances.delete(id);
1964
- }
1965
- }
1966
- for (const item of next.toasts) {
1967
- activePositions.add(item.position);
1968
- const vp = ensureViewport(item.position, resolvedLayout, resolvedOffset);
1969
- const existing = instances.get(item.id);
1970
- if (!existing) {
1971
- const inst = createInstance(item, machine2);
1972
- instances.set(item.id, inst);
1973
- vp.appendChild(inst.el);
1974
- } else if (existing.item.instanceId !== item.instanceId) {
1975
- destroyInstance(existing);
1976
- const inst = createInstance(item, machine2);
1977
- instances.set(item.id, inst);
1978
- vp.appendChild(inst.el);
1979
- } else {
1980
- applyUpdate(existing, item);
1981
- if (existing.el.parentElement !== vp) {
1982
- vp.appendChild(existing.el);
1751
+ bodyEl.setAttribute("fill", fill);
1752
+ bodyEl.setAttribute("opacity", "0");
1753
+ g.appendChild(pillEl);
1754
+ g.appendChild(bodyEl);
1755
+ svg.appendChild(g);
1756
+ return { svg, pillEl, bodyEl, initialPillX };
1757
+ }
1758
+ function buildInstanceContent(item) {
1759
+ let contentEl = null;
1760
+ let descriptionEl = null;
1761
+ const hasDesc = Boolean(item.description) || Boolean(item.button);
1762
+ if (!hasDesc) return { contentEl, descriptionEl };
1763
+ contentEl = document.createElement("div");
1764
+ descriptionEl = document.createElement("div");
1765
+ if (item.styles?.description) descriptionEl.className = item.styles.description;
1766
+ if (item.description != null) {
1767
+ if (typeof item.description === "string") descriptionEl.textContent = item.description;
1768
+ else if (item.description instanceof HTMLElement) descriptionEl.appendChild(item.description);
1769
+ }
1770
+ if (item.button) {
1771
+ const btn = document.createElement("button");
1772
+ btn.type = "button";
1773
+ btn.textContent = item.button.title;
1774
+ if (item.styles?.button) btn.className = item.styles.button;
1775
+ btn.addEventListener("click", (e) => {
1776
+ e.stopPropagation();
1777
+ item.button?.onClick();
1778
+ });
1779
+ descriptionEl.appendChild(btn);
1780
+ }
1781
+ contentEl.appendChild(descriptionEl);
1782
+ return { contentEl, descriptionEl };
1783
+ }
1784
+ function buildHeader(item) {
1785
+ const headerEl = document.createElement("div");
1786
+ const headerStackEl = document.createElement("div");
1787
+ headerStackEl.setAttribute("data-fluix-header-stack", "");
1788
+ const innerEl = document.createElement("div");
1789
+ innerEl.setAttribute("data-fluix-header-inner", "");
1790
+ innerEl.setAttribute("data-layer", "current");
1791
+ const badgeEl = document.createElement("div");
1792
+ renderIconInto(badgeEl, item.icon, item.state);
1793
+ const titleEl = document.createElement("span");
1794
+ titleEl.textContent = item.title ?? item.state;
1795
+ innerEl.appendChild(badgeEl);
1796
+ innerEl.appendChild(titleEl);
1797
+ headerStackEl.appendChild(innerEl);
1798
+ headerEl.appendChild(headerStackEl);
1799
+ return { headerEl, headerStackEl, innerEl, badgeEl, titleEl };
1800
+ }
1801
+ function createInstance(item, machine2) {
1802
+ const localState = { ready: false, expanded: false };
1803
+ const roundness = item.roundness ?? TOAST_DEFAULTS.roundness;
1804
+ const blur2 = Math.min(10, Math.max(6, roundness * 0.45));
1805
+ const el2 = document.createElement("button");
1806
+ el2.type = "button";
1807
+ const canvasDiv = document.createElement("div");
1808
+ const { svg, pillEl, bodyEl, initialPillX } = buildInstanceSvg(item, roundness, blur2);
1809
+ canvasDiv.appendChild(svg);
1810
+ el2.appendChild(canvasDiv);
1811
+ const { headerEl, headerStackEl, innerEl, badgeEl, titleEl } = buildHeader(item);
1812
+ el2.appendChild(headerEl);
1813
+ const { contentEl, descriptionEl } = buildInstanceContent(item);
1814
+ if (contentEl) el2.appendChild(contentEl);
1815
+ const attrs = Toaster.getAttrs(item, localState);
1816
+ applyAttrs(el2, attrs.root);
1817
+ applyAttrs(canvasDiv, attrs.canvas);
1818
+ applyAttrs(headerEl, attrs.header);
1819
+ applyAttrs(badgeEl, attrs.badge);
1820
+ if (item.styles?.badge) badgeEl.className = item.styles.badge;
1821
+ applyAttrs(titleEl, attrs.title);
1822
+ if (item.styles?.title) titleEl.className = item.styles.title;
1823
+ if (contentEl) applyAttrs(contentEl, attrs.content);
1824
+ if (descriptionEl) applyAttrs(descriptionEl, attrs.description);
1825
+ if (item.button && descriptionEl) {
1826
+ const btnEl = descriptionEl.querySelector("button");
1827
+ if (btnEl) applyAttrs(btnEl, attrs.button);
1828
+ }
1829
+ const inst = {
1830
+ el: el2,
1831
+ pillEl,
1832
+ bodyEl,
1833
+ svgEl: svg,
1834
+ headerEl,
1835
+ innerEl,
1836
+ headerStackEl,
1837
+ contentEl,
1838
+ descriptionEl,
1839
+ localState,
1840
+ pillWidth: 0,
1841
+ contentHeight: 0,
1842
+ frozenExpanded: HEIGHT * MIN_EXPAND_RATIO2,
1843
+ hovering: false,
1844
+ pendingDismiss: false,
1845
+ dismissRequested: false,
1846
+ pillRo: null,
1847
+ contentRo: null,
1848
+ pillAnim: null,
1849
+ prevPill: { x: initialPillX, width: HEIGHT, height: HEIGHT },
1850
+ pillFirst: true,
1851
+ headerKey: `${item.state}-${item.title ?? item.state}`,
1852
+ headerPad: null,
1853
+ connectHandle: null,
1854
+ timers: /* @__PURE__ */ new Set(),
1855
+ item
1856
+ };
1857
+ wireObservers(inst);
1858
+ wireConnect(inst, item, machine2);
1859
+ applyVars(inst, item);
1860
+ setTimer(inst, () => {
1861
+ inst.localState.ready = true;
1862
+ applyUpdate(inst, inst.item);
1863
+ setupAutopilot(inst, inst.item);
1864
+ }, 32);
1865
+ setupAutoDismiss(inst, item, machine2);
1866
+ measurePillWidth(inst);
1867
+ return inst;
1868
+ }
1869
+ function wireObservers(inst, _item) {
1870
+ inst.pillRo = new ResizeObserver(() => {
1871
+ requestAnimationFrame(() => {
1872
+ measurePillWidth(inst);
1873
+ applyVars(inst, inst.item);
1874
+ });
1875
+ });
1876
+ inst.pillRo.observe(inst.innerEl);
1877
+ if (inst.descriptionEl) {
1878
+ const descEl = inst.descriptionEl;
1879
+ inst.contentRo = new ResizeObserver(() => {
1880
+ requestAnimationFrame(() => {
1881
+ const h = descEl.scrollHeight;
1882
+ if (h !== inst.contentHeight) {
1883
+ inst.contentHeight = h;
1884
+ applyVars(inst, inst.item);
1983
1885
  }
1886
+ });
1887
+ });
1888
+ inst.contentRo.observe(descEl);
1889
+ }
1890
+ }
1891
+ function wireConnect(inst, item, machine2) {
1892
+ inst.connectHandle = Toaster.connect(inst.el, {
1893
+ onExpand: () => {
1894
+ if (inst.item.exiting || inst.dismissRequested) return;
1895
+ inst.localState.expanded = true;
1896
+ applyUpdate(inst, inst.item);
1897
+ },
1898
+ onCollapse: () => {
1899
+ if (inst.item.exiting || inst.dismissRequested) return;
1900
+ if (inst.item.autopilot !== false) return;
1901
+ inst.localState.expanded = false;
1902
+ applyUpdate(inst, inst.item);
1903
+ },
1904
+ onDismiss: () => {
1905
+ if (inst.dismissRequested) return;
1906
+ inst.dismissRequested = true;
1907
+ machine2.dismiss(item.id);
1908
+ },
1909
+ onHoverStart: () => {
1910
+ inst.hovering = true;
1911
+ },
1912
+ onHoverEnd: () => {
1913
+ inst.hovering = false;
1914
+ if (inst.pendingDismiss) {
1915
+ inst.pendingDismiss = false;
1916
+ if (inst.dismissRequested) return;
1917
+ inst.dismissRequested = true;
1918
+ machine2.dismiss(inst.item.id);
1984
1919
  }
1985
1920
  }
1986
- for (const [position, vp] of viewports) {
1987
- const vpAttrs = Toaster.getViewportAttrs(position, resolvedLayout);
1988
- applyAttrs(vp, vpAttrs);
1989
- applyViewportOffset(vp, resolvedOffset, position);
1921
+ }, item);
1922
+ }
1923
+
1924
+ // src/toast/index.ts
1925
+ function resolveOffsetValue(v) {
1926
+ return typeof v === "number" ? `${v}px` : v;
1927
+ }
1928
+ function applyViewportOffset(el2, offset, position) {
1929
+ el2.style.top = "";
1930
+ el2.style.right = "";
1931
+ el2.style.bottom = "";
1932
+ el2.style.left = "";
1933
+ el2.style.paddingLeft = "";
1934
+ el2.style.paddingRight = "";
1935
+ if (offset == null) return;
1936
+ let top, right, bottom, left;
1937
+ if (typeof offset === "number" || typeof offset === "string") {
1938
+ const v = resolveOffsetValue(offset);
1939
+ top = v;
1940
+ right = v;
1941
+ bottom = v;
1942
+ left = v;
1943
+ } else {
1944
+ if (offset.top != null) top = resolveOffsetValue(offset.top);
1945
+ if (offset.right != null) right = resolveOffsetValue(offset.right);
1946
+ if (offset.bottom != null) bottom = resolveOffsetValue(offset.bottom);
1947
+ if (offset.left != null) left = resolveOffsetValue(offset.left);
1948
+ }
1949
+ if (position.startsWith("top") && top) el2.style.top = top;
1950
+ if (position.startsWith("bottom") && bottom) el2.style.bottom = bottom;
1951
+ if (position.endsWith("right") && right) el2.style.right = right;
1952
+ if (position.endsWith("left") && left) el2.style.left = left;
1953
+ if (position.endsWith("center")) {
1954
+ if (left) el2.style.paddingLeft = left;
1955
+ if (right) el2.style.paddingRight = right;
1956
+ }
1957
+ }
1958
+ function ensureViewport(state, position, layout, offset) {
1959
+ let vp = state.viewports.get(position);
1960
+ if (!vp) {
1961
+ vp = document.createElement("section");
1962
+ applyAttrs(vp, Toaster.getViewportAttrs(position, layout));
1963
+ applyViewportOffset(vp, offset, position);
1964
+ document.body.appendChild(vp);
1965
+ state.viewports.set(position, vp);
1966
+ }
1967
+ return vp;
1968
+ }
1969
+ function sync(state) {
1970
+ const next = state.machine.store.getSnapshot();
1971
+ const layout = next.config?.layout ?? state.currentConfig?.layout ?? "stack";
1972
+ const offset = next.config?.offset ?? state.currentConfig?.offset;
1973
+ const activePositions = /* @__PURE__ */ new Set();
1974
+ const nextIds = new Set(next.toasts.map((t) => t.id));
1975
+ for (const [id, inst] of state.instances) {
1976
+ if (!nextIds.has(id)) {
1977
+ destroyInstance(inst);
1978
+ state.instances.delete(id);
1979
+ }
1980
+ }
1981
+ for (const item of next.toasts) {
1982
+ activePositions.add(item.position);
1983
+ const vp = ensureViewport(state, item.position, layout, offset);
1984
+ const existing = state.instances.get(item.id);
1985
+ if (!existing) {
1986
+ const inst = createInstance(item, state.machine);
1987
+ state.instances.set(item.id, inst);
1988
+ vp.appendChild(inst.el);
1989
+ } else if (existing.item.instanceId !== item.instanceId) {
1990
+ destroyInstance(existing);
1991
+ const inst = createInstance(item, state.machine);
1992
+ state.instances.set(item.id, inst);
1993
+ vp.appendChild(inst.el);
1994
+ } else {
1995
+ applyUpdate(existing, item);
1996
+ if (existing.el.parentElement !== vp) vp.appendChild(existing.el);
1990
1997
  }
1991
- for (const [position] of viewports) {
1992
- if (!activePositions.has(position)) {
1993
- removeViewport(position);
1994
- }
1998
+ }
1999
+ for (const [position, vp] of state.viewports) {
2000
+ applyAttrs(vp, Toaster.getViewportAttrs(position, layout));
2001
+ applyViewportOffset(vp, offset, position);
2002
+ }
2003
+ for (const [position] of state.viewports) {
2004
+ if (!activePositions.has(position)) {
2005
+ state.viewports.get(position)?.remove();
2006
+ state.viewports.delete(position);
1995
2007
  }
1996
2008
  }
1997
- sync();
1998
- const unsubscribe = machine2.store.subscribe(sync);
2009
+ }
2010
+ function createToaster(config) {
2011
+ const state = {
2012
+ machine: Toaster.getMachine(),
2013
+ instances: /* @__PURE__ */ new Map(),
2014
+ viewports: /* @__PURE__ */ new Map(),
2015
+ currentConfig: config
2016
+ };
2017
+ if (config) state.machine.configure(config);
2018
+ sync(state);
2019
+ const unsubscribe = state.machine.store.subscribe(() => sync(state));
1999
2020
  return {
2000
2021
  destroy() {
2001
2022
  unsubscribe();
2002
- for (const inst of instances.values()) {
2003
- destroyInstance(inst);
2004
- }
2005
- instances.clear();
2006
- for (const vp of viewports.values()) {
2007
- vp.remove();
2008
- }
2009
- viewports.clear();
2023
+ for (const inst of state.instances.values()) destroyInstance(inst);
2024
+ state.instances.clear();
2025
+ for (const vp of state.viewports.values()) vp.remove();
2026
+ state.viewports.clear();
2010
2027
  },
2011
2028
  update(newConfig) {
2012
- currentConfig = newConfig;
2013
- machine2.configure(newConfig);
2029
+ state.currentConfig = newConfig;
2030
+ state.machine.configure(newConfig);
2014
2031
  }
2015
2032
  };
2016
2033
  }
2017
2034
 
2018
- // src/notch.ts
2019
- var SVG_NS2 = "http://www.w3.org/2000/svg";
2020
- function applyAttrs2(el, attrs) {
2021
- for (const [key, value] of Object.entries(attrs)) {
2022
- el.setAttribute(key, value);
2035
+ // src/notch/highlight.ts
2036
+ function setBlobAttrs(el2, x, y, w, h, rx, opacity) {
2037
+ el2.setAttribute("x", String(x));
2038
+ el2.setAttribute("y", String(y));
2039
+ el2.setAttribute("width", String(w));
2040
+ el2.setAttribute("height", String(h));
2041
+ el2.setAttribute("rx", String(rx));
2042
+ el2.setAttribute("ry", String(rx));
2043
+ el2.setAttribute("opacity", opacity);
2044
+ }
2045
+ function springBlob(el2, from, to, sc) {
2046
+ return animateSpring(el2, {
2047
+ x: { from: from.x, to: to.x, unit: "px" },
2048
+ y: { from: from.y, to: to.y, unit: "px" },
2049
+ width: { from: from.w, to: to.w, unit: "px" },
2050
+ height: { from: from.h, to: to.h, unit: "px" },
2051
+ rx: { from: from.rx, to: to.rx, unit: "px" },
2052
+ ry: { from: from.rx, to: to.rx, unit: "px" }
2053
+ }, { ...sc, stiffness: (sc.stiffness ?? 300) * 1.2 });
2054
+ }
2055
+ function createHighlightTracker(el2, springConfig) {
2056
+ let anim = null;
2057
+ const prev = { x: 0, y: 0, w: 0, h: 0, visible: false };
2058
+ function finish(a, x, y, w, h, rx, opacity) {
2059
+ if (a) {
2060
+ anim = a;
2061
+ a.onfinish = () => {
2062
+ anim = null;
2063
+ setBlobAttrs(el2, x, y, w, h, rx, opacity);
2064
+ };
2065
+ } else {
2066
+ setBlobAttrs(el2, x, y, w, h, rx, opacity);
2067
+ }
2023
2068
  }
2069
+ return {
2070
+ onItemEnter(e, rootEl, isOpen, roundness) {
2071
+ const target = e.target.closest("a, button");
2072
+ if (!target || !isOpen) return;
2073
+ const rootRect = rootEl.getBoundingClientRect();
2074
+ const itemRect = target.getBoundingClientRect();
2075
+ const padX = 8, padY = 4;
2076
+ const overshoot = Math.max(6, roundness * 0.35);
2077
+ const toW = target.offsetWidth + padX * 2;
2078
+ const toH = Math.max(target.offsetHeight + padY * 2, rootRect.height + overshoot * 2);
2079
+ const toX = itemRect.left + itemRect.width / 2 - rootRect.left - toW / 2;
2080
+ const toY = itemRect.top + itemRect.height / 2 - rootRect.top - toH / 2;
2081
+ if (anim) {
2082
+ anim.cancel();
2083
+ anim = null;
2084
+ }
2085
+ const from = {
2086
+ x: prev.visible ? prev.x : toX + toW / 2,
2087
+ y: prev.visible ? prev.y : toY + toH / 2,
2088
+ w: prev.visible ? prev.w : 0,
2089
+ h: prev.visible ? prev.h : 0,
2090
+ rx: prev.visible ? prev.h / 2 : 0
2091
+ };
2092
+ finish(springBlob(el2, from, { x: toX, y: toY, w: toW, h: toH, rx: toH / 2 }, springConfig()), toX, toY, toW, toH, toH / 2, "1");
2093
+ el2.setAttribute("opacity", "1");
2094
+ prev.x = toX;
2095
+ prev.y = toY;
2096
+ prev.w = toW;
2097
+ prev.h = toH;
2098
+ prev.visible = true;
2099
+ },
2100
+ onItemLeave() {
2101
+ if (!prev.visible) return;
2102
+ const cx = prev.x + prev.w / 2, cy = prev.y + prev.h / 2;
2103
+ finish(springBlob(el2, { x: prev.x, y: prev.y, w: prev.w, h: prev.h, rx: prev.h / 2 }, { x: cx, y: cy, w: 0, h: 0, rx: 0 }, springConfig()), cx, cy, 0, 0, 0, "0");
2104
+ prev.visible = false;
2105
+ },
2106
+ reset(rootW, rootH) {
2107
+ if (anim) {
2108
+ anim.cancel();
2109
+ anim = null;
2110
+ }
2111
+ setBlobAttrs(el2, rootW / 2, rootH / 2, 0, 0, 0, "0");
2112
+ prev.visible = false;
2113
+ },
2114
+ cancelAnim() {
2115
+ anim?.cancel();
2116
+ }
2117
+ };
2024
2118
  }
2119
+
2120
+ // src/notch/dom.ts
2025
2121
  function resolveContent(source) {
2026
2122
  if (source instanceof HTMLElement) return source;
2027
2123
  const span = document.createElement("span");
2028
2124
  span.textContent = source;
2029
2125
  return span;
2030
2126
  }
2031
- function createNotch(container, options) {
2032
- let {
2033
- trigger = "click",
2034
- position = "top-center",
2035
- spring,
2036
- dotSize = 36,
2037
- roundness = NOTCH_DEFAULTS.roundness,
2038
- theme = "dark",
2039
- fill,
2040
- open: controlledOpen,
2041
- onOpenChange
2042
- } = options;
2043
- const springConfig = () => spring ?? FLUIX_SPRING;
2044
- const machine2 = createNotchMachine({
2045
- position,
2046
- trigger,
2047
- roundness,
2048
- fill,
2049
- spring
2050
- });
2051
- let snapshot = machine2.store.getSnapshot();
2052
- let prevOpenVal;
2053
- const blur = () => Math.min(10, Math.max(6, roundness * 0.45));
2054
- const collapsedW = () => dotSize;
2055
- const collapsedH = () => dotSize;
2056
- let contentSize = { w: 200, h: 44 };
2057
- const hlPad = 12;
2058
- const expandedW = () => contentSize.w + hlPad * 2;
2059
- const expandedH = () => Math.max(contentSize.h + hlPad, dotSize);
2060
- const targetW = () => snapshot.open ? expandedW() : collapsedW();
2061
- const targetH = () => snapshot.open ? expandedH() : collapsedH();
2062
- const rootW = () => Math.max(expandedW(), collapsedW());
2063
- const rootH = () => Math.max(expandedH(), collapsedH());
2064
- const prev = { w: 0, h: 0, initialized: false };
2065
- let currentAnim = null;
2066
- let highlightAnim = null;
2067
- const hlPrev = { x: 0, y: 0, w: 0, h: 0, visible: false };
2127
+ function buildNotchSvg(rW, rH, collW, collH, blurVal, fill) {
2128
+ const svg = document.createElementNS(SVG_NS, "svg");
2129
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2130
+ for (const [k, v] of Object.entries({ width: String(rW), height: String(rH), viewBox: `0 0 ${rW} ${rH}`, "aria-hidden": "true" })) svg.setAttribute(k, v);
2131
+ const { g, defs, feBlur } = createGooeyFilter("fluix-notch-goo", blurVal);
2132
+ svg.appendChild(defs);
2133
+ const effectiveFill = fill ?? "var(--fluix-notch-bg)";
2134
+ const svgRectEl = document.createElementNS(SVG_NS, "rect");
2135
+ const cx = (rW - collW) / 2, cy = (rH - collH) / 2;
2136
+ for (const [k, v] of Object.entries({ x: cx, y: cy, width: collW, height: collH, rx: collW / 2, ry: collH / 2 })) svgRectEl.setAttribute(k, String(v));
2137
+ svgRectEl.setAttribute("fill", effectiveFill);
2138
+ g.appendChild(svgRectEl);
2139
+ const hoverBlobEl = document.createElementNS(SVG_NS, "rect");
2140
+ for (const [k, v] of Object.entries({ x: cx, y: cy, width: 0, height: 0, rx: 0, ry: 0, opacity: "0" })) hoverBlobEl.setAttribute(k, String(v));
2141
+ hoverBlobEl.setAttribute("fill", effectiveFill);
2142
+ g.appendChild(hoverBlobEl);
2143
+ svg.appendChild(g);
2144
+ return { svg, svgRectEl, hoverBlobEl, feBlur };
2145
+ }
2146
+ var HL_PAD = 12;
2147
+ function computeDims(cfg) {
2148
+ const collW = cfg.dotSize, collH = cfg.dotSize;
2149
+ const expW = cfg.contentSize.w + HL_PAD * 2;
2150
+ const expH = Math.max(cfg.contentSize.h + HL_PAD, cfg.dotSize);
2151
+ const rW = Math.max(expW, collW);
2152
+ const rH = Math.max(expH, collH);
2153
+ return { collW, collH, rW, rH };
2154
+ }
2155
+ function computeBlur(roundness) {
2156
+ return Math.min(10, Math.max(6, roundness * 0.45));
2157
+ }
2158
+ function buildNotchDOM(cfg, pill, content, container, springCfg2) {
2068
2159
  const measureEl = document.createElement("div");
2069
2160
  measureEl.setAttribute("data-fluix-notch-measure", "");
2070
- measureEl.appendChild(resolveContent(options.content).cloneNode(true));
2161
+ measureEl.appendChild(resolveContent(content).cloneNode(true));
2071
2162
  container.appendChild(measureEl);
2163
+ const d = computeDims(cfg);
2072
2164
  const rootEl = document.createElement("div");
2073
- const attrs = getNotchAttrs({ open: snapshot.open, position, theme });
2074
- applyAttrs2(rootEl, attrs.root);
2075
- rootEl.style.width = `${rootW()}px`;
2076
- rootEl.style.height = `${rootH()}px`;
2165
+ const attrs = getNotchAttrs({ open: cfg.snapshot.open, position: cfg.position, theme: cfg.theme });
2166
+ applyAttrs(rootEl, attrs.root);
2167
+ rootEl.style.width = `${d.rW}px`;
2168
+ rootEl.style.height = `${d.rH}px`;
2077
2169
  const canvasDiv = document.createElement("div");
2078
- applyAttrs2(canvasDiv, attrs.canvas);
2079
- const svg = document.createElementNS(SVG_NS2, "svg");
2080
- svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2081
- svg.setAttribute("width", String(rootW()));
2082
- svg.setAttribute("height", String(rootH()));
2083
- svg.setAttribute("viewBox", `0 0 ${rootW()} ${rootH()}`);
2084
- svg.setAttribute("aria-hidden", "true");
2085
- const defs = document.createElementNS(SVG_NS2, "defs");
2086
- const filter = document.createElementNS(SVG_NS2, "filter");
2087
- filter.setAttribute("id", "fluix-notch-goo");
2088
- filter.setAttribute("x", "-20%");
2089
- filter.setAttribute("y", "-20%");
2090
- filter.setAttribute("width", "140%");
2091
- filter.setAttribute("height", "140%");
2092
- filter.setAttribute("color-interpolation-filters", "sRGB");
2093
- const feBlur = document.createElementNS(SVG_NS2, "feGaussianBlur");
2094
- feBlur.setAttribute("in", "SourceGraphic");
2095
- feBlur.setAttribute("stdDeviation", String(blur()));
2096
- feBlur.setAttribute("result", "blur");
2097
- const feCM = document.createElementNS(SVG_NS2, "feColorMatrix");
2098
- feCM.setAttribute("in", "blur");
2099
- feCM.setAttribute("type", "matrix");
2100
- feCM.setAttribute("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10");
2101
- feCM.setAttribute("result", "goo");
2102
- const feComp = document.createElementNS(SVG_NS2, "feComposite");
2103
- feComp.setAttribute("in", "SourceGraphic");
2104
- feComp.setAttribute("in2", "goo");
2105
- feComp.setAttribute("operator", "atop");
2106
- filter.appendChild(feBlur);
2107
- filter.appendChild(feCM);
2108
- filter.appendChild(feComp);
2109
- defs.appendChild(filter);
2110
- svg.appendChild(defs);
2111
- const gGroup = document.createElementNS(SVG_NS2, "g");
2112
- gGroup.setAttribute("filter", "url(#fluix-notch-goo)");
2113
- const svgRectEl = document.createElementNS(SVG_NS2, "rect");
2114
- const cw = collapsedW();
2115
- const ch = collapsedH();
2116
- svgRectEl.setAttribute("x", String((rootW() - cw) / 2));
2117
- svgRectEl.setAttribute("y", String((rootH() - ch) / 2));
2118
- svgRectEl.setAttribute("width", String(cw));
2119
- svgRectEl.setAttribute("height", String(ch));
2120
- svgRectEl.setAttribute("rx", String(cw / 2));
2121
- svgRectEl.setAttribute("ry", String(ch / 2));
2122
- svgRectEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
2123
- gGroup.appendChild(svgRectEl);
2124
- svg.appendChild(gGroup);
2125
- const hoverBlobEl = document.createElementNS(SVG_NS2, "rect");
2126
- hoverBlobEl.setAttribute("x", String((rootW() - cw) / 2));
2127
- hoverBlobEl.setAttribute("y", String((rootH() - ch) / 2));
2128
- hoverBlobEl.setAttribute("width", "0");
2129
- hoverBlobEl.setAttribute("height", "0");
2130
- hoverBlobEl.setAttribute("rx", "0");
2131
- hoverBlobEl.setAttribute("ry", "0");
2132
- hoverBlobEl.setAttribute("opacity", "0");
2133
- hoverBlobEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
2134
- gGroup.appendChild(hoverBlobEl);
2135
- canvasDiv.appendChild(svg);
2170
+ applyAttrs(canvasDiv, attrs.canvas);
2171
+ canvasDiv.style.cssText = "position:absolute;inset:0;pointer-events:none;overflow:visible;";
2172
+ const svgRefs = buildNotchSvg(d.rW, d.rH, d.collW, d.collH, computeBlur(cfg.roundness), cfg.fill);
2173
+ canvasDiv.appendChild(svgRefs.svg);
2136
2174
  rootEl.appendChild(canvasDiv);
2175
+ const highlight = createHighlightTracker(svgRefs.hoverBlobEl, springCfg2);
2137
2176
  const pillDiv = document.createElement("div");
2138
- applyAttrs2(pillDiv, attrs.pill);
2139
- pillDiv.style.width = `${dotSize}px`;
2140
- pillDiv.style.height = `${dotSize}px`;
2141
- pillDiv.appendChild(resolveContent(options.pill));
2177
+ applyAttrs(pillDiv, attrs.pill);
2178
+ pillDiv.style.cssText = `position:absolute;z-index:10;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;border-radius:50%;overflow:hidden;pointer-events:none;width:${cfg.dotSize}px;height:${cfg.dotSize}px;color:var(--fluix-notch-color);`;
2179
+ pillDiv.appendChild(resolveContent(pill));
2142
2180
  rootEl.appendChild(pillDiv);
2143
2181
  const contentDiv = document.createElement("div");
2144
- applyAttrs2(contentDiv, attrs.content);
2145
- contentDiv.appendChild(resolveContent(options.content));
2182
+ applyAttrs(contentDiv, attrs.content);
2183
+ contentDiv.style.cssText = "position:absolute;z-index:10;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;opacity:0;color:var(--fluix-notch-color);";
2184
+ contentDiv.appendChild(resolveContent(content));
2146
2185
  rootEl.appendChild(contentDiv);
2147
2186
  container.appendChild(rootEl);
2148
- prev.w = cw;
2149
- prev.h = ch;
2150
- prev.initialized = true;
2151
- let measureRaf = 0;
2187
+ rootEl.setAttribute("role", "button");
2188
+ rootEl.setAttribute("tabindex", "0");
2189
+ rootEl.setAttribute("aria-expanded", String(cfg.snapshot.open));
2190
+ return { rootEl, measureEl, pillDiv, contentDiv, highlight, ...svgRefs };
2191
+ }
2192
+
2193
+ // src/notch/index.ts
2194
+ var HL_PAD2 = 12;
2195
+ function dims(ctx) {
2196
+ const collW = ctx.dotSize, collH = ctx.dotSize;
2197
+ const expW = ctx.contentSize.w + HL_PAD2 * 2;
2198
+ const expH = Math.max(ctx.contentSize.h + HL_PAD2, ctx.dotSize);
2199
+ const tW = ctx.snapshot.open ? expW : collW;
2200
+ const tH = ctx.snapshot.open ? expH : collH;
2201
+ const rW = Math.max(expW, collW);
2202
+ const rH = Math.max(expH, collH);
2203
+ return { collW, collH, expW, expH, tW, tH, rW, rH };
2204
+ }
2205
+ function blur(ctx) {
2206
+ return Math.min(10, Math.max(6, ctx.roundness * 0.45));
2207
+ }
2208
+ function springCfg(ctx) {
2209
+ return ctx.spring ?? FLUIX_SPRING;
2210
+ }
2211
+ function animateRect(ctx) {
2212
+ const d = dims(ctx);
2213
+ const toX = (d.rW - d.tW) / 2, toY = (d.rH - d.tH) / 2;
2214
+ if (d.tW === ctx.prev.w && d.tH === ctx.prev.h) {
2215
+ const el3 = ctx.dom.svgRectEl;
2216
+ const rx = d.tW === d.collW && d.tH === d.collH ? d.collW / 2 : ctx.roundness;
2217
+ for (const [k, v] of Object.entries({ x: toX, y: toY, width: d.tW, height: d.tH, rx, ry: rx })) el3.setAttribute(k, String(v));
2218
+ return;
2219
+ }
2220
+ if (ctx.currentAnim) {
2221
+ ctx.currentAnim.cancel();
2222
+ ctx.currentAnim = null;
2223
+ }
2224
+ const fromW = ctx.prev.w, fromH = ctx.prev.h;
2225
+ const fromX = (d.rW - fromW) / 2, fromY = (d.rH - fromH) / 2;
2226
+ ctx.prev.w = d.tW;
2227
+ ctx.prev.h = d.tH;
2228
+ const wasCollapsed = fromW === d.collW && fromH === d.collH;
2229
+ const isCollapsing = d.tW === d.collW && d.tH === d.collH;
2230
+ const fromRx = wasCollapsed ? d.collW / 2 : ctx.roundness;
2231
+ const toRx = isCollapsing ? d.collW / 2 : ctx.roundness;
2232
+ const el2 = ctx.dom.svgRectEl;
2233
+ const a = animateSpring(el2, {
2234
+ width: { from: fromW, to: d.tW, unit: "px" },
2235
+ height: { from: fromH, to: d.tH, unit: "px" },
2236
+ x: { from: fromX, to: toX, unit: "px" },
2237
+ y: { from: fromY, to: toY, unit: "px" },
2238
+ rx: { from: fromRx, to: toRx, unit: "px" },
2239
+ ry: { from: fromRx, to: toRx, unit: "px" }
2240
+ }, springCfg(ctx));
2241
+ const applyFinal = () => {
2242
+ for (const [k, v] of Object.entries({ width: d.tW, height: d.tH, x: toX, y: toY, rx: toRx, ry: toRx })) el2.setAttribute(k, String(v));
2243
+ };
2244
+ if (a) {
2245
+ ctx.currentAnim = a;
2246
+ a.onfinish = () => {
2247
+ ctx.currentAnim = null;
2248
+ applyFinal();
2249
+ };
2250
+ } else applyFinal();
2251
+ }
2252
+ function updateLayout(ctx) {
2253
+ const d = dims(ctx);
2254
+ const isOpen = ctx.snapshot.open;
2255
+ const newAttrs = getNotchAttrs({ open: isOpen, position: ctx.position, theme: ctx.theme });
2256
+ applyAttrs(ctx.dom.rootEl, newAttrs.root);
2257
+ ctx.dom.rootEl.setAttribute("aria-expanded", String(isOpen));
2258
+ ctx.dom.rootEl.style.width = `${d.rW}px`;
2259
+ ctx.dom.rootEl.style.height = `${d.rH}px`;
2260
+ ctx.dom.svg.setAttribute("width", String(d.rW));
2261
+ ctx.dom.svg.setAttribute("height", String(d.rH));
2262
+ ctx.dom.svg.setAttribute("viewBox", `0 0 ${d.rW} ${d.rH}`);
2263
+ ctx.dom.feBlur.setAttribute("stdDeviation", String(blur(ctx)));
2264
+ const effectiveFill = ctx.fill ?? "var(--fluix-notch-bg)";
2265
+ ctx.dom.svgRectEl.setAttribute("fill", effectiveFill);
2266
+ ctx.dom.hoverBlobEl.setAttribute("fill", effectiveFill);
2267
+ applyAttrs(ctx.dom.contentDiv, newAttrs.content);
2268
+ ctx.dom.contentDiv.style.opacity = isOpen ? "1" : "0";
2269
+ ctx.dom.contentDiv.style.pointerEvents = isOpen ? "auto" : "none";
2270
+ ctx.dom.pillDiv.style.opacity = isOpen ? "0" : "1";
2271
+ animateRect(ctx);
2272
+ if (!isOpen) ctx.highlight.reset(d.rW, d.rH);
2273
+ document.documentElement.style.setProperty("--fluix-notch-offset", `${d.rH}px`);
2274
+ }
2275
+ function createListeners(ctx) {
2276
+ const handleOpen = () => {
2277
+ ctx.controlledOpen === void 0 ? ctx.machine.open() : ctx.onOpenChange?.(true);
2278
+ };
2279
+ const handleClose = () => {
2280
+ ctx.controlledOpen === void 0 ? ctx.machine.close() : ctx.onOpenChange?.(false);
2281
+ };
2282
+ const handleToggle = () => {
2283
+ ctx.controlledOpen === void 0 ? ctx.machine.toggle() : ctx.onOpenChange?.(!ctx.snapshot.open);
2284
+ };
2285
+ return {
2286
+ mouseenter: () => {
2287
+ if (ctx.trigger === "hover") handleOpen();
2288
+ },
2289
+ mouseleave: () => {
2290
+ if (ctx.trigger === "hover") {
2291
+ handleClose();
2292
+ ctx.highlight.reset(dims(ctx).rW, dims(ctx).rH);
2293
+ return;
2294
+ }
2295
+ ctx.highlight.onItemLeave();
2296
+ },
2297
+ mouseover: (e) => {
2298
+ ctx.highlight.onItemEnter(e, ctx.dom.rootEl, ctx.snapshot.open, ctx.roundness);
2299
+ },
2300
+ click: () => {
2301
+ if (ctx.trigger === "click") handleToggle();
2302
+ },
2303
+ keydown: (e) => {
2304
+ if (e.key === "Enter" || e.key === " ") {
2305
+ e.preventDefault();
2306
+ handleToggle();
2307
+ } else if (e.key === "Escape" && ctx.snapshot.open) {
2308
+ e.preventDefault();
2309
+ handleClose();
2310
+ }
2311
+ }
2312
+ };
2313
+ }
2314
+ function attachListeners(el2, ls) {
2315
+ el2.addEventListener("mouseenter", ls.mouseenter);
2316
+ el2.addEventListener("mouseleave", ls.mouseleave);
2317
+ el2.addEventListener("mouseover", ls.mouseover);
2318
+ el2.addEventListener("click", ls.click);
2319
+ el2.addEventListener("keydown", ls.keydown);
2320
+ }
2321
+ function detachListeners(el2, ls) {
2322
+ el2.removeEventListener("mouseenter", ls.mouseenter);
2323
+ el2.removeEventListener("mouseleave", ls.mouseleave);
2324
+ el2.removeEventListener("mouseover", ls.mouseover);
2325
+ el2.removeEventListener("click", ls.click);
2326
+ el2.removeEventListener("keydown", ls.keydown);
2327
+ }
2328
+ function notchUpdate(ctx, opts, listeners) {
2329
+ if (opts.trigger !== void 0) ctx.trigger = opts.trigger;
2330
+ if (opts.position !== void 0) ctx.position = opts.position;
2331
+ if (opts.spring !== void 0) ctx.spring = opts.spring;
2332
+ if (opts.dotSize !== void 0) ctx.dotSize = opts.dotSize;
2333
+ if (opts.roundness !== void 0) ctx.roundness = opts.roundness;
2334
+ if (opts.theme !== void 0) ctx.theme = opts.theme;
2335
+ if (opts.fill !== void 0) ctx.fill = opts.fill;
2336
+ if (opts.open !== void 0) ctx.controlledOpen = opts.open;
2337
+ if (opts.onOpenChange !== void 0) ctx.onOpenChange = opts.onOpenChange;
2338
+ if (opts.pill !== void 0) {
2339
+ ctx.dom.pillDiv.textContent = "";
2340
+ ctx.dom.pillDiv.appendChild(resolveContent(opts.pill));
2341
+ }
2342
+ if (opts.content !== void 0) {
2343
+ ctx.dom.contentDiv.textContent = "";
2344
+ ctx.dom.contentDiv.appendChild(resolveContent(opts.content));
2345
+ ctx.dom.measureEl.textContent = "";
2346
+ ctx.dom.measureEl.appendChild(resolveContent(opts.content).cloneNode(true));
2347
+ }
2348
+ ctx.dom.pillDiv.style.width = `${ctx.dotSize}px`;
2349
+ ctx.dom.pillDiv.style.height = `${ctx.dotSize}px`;
2350
+ ctx.machine.configure({ position: ctx.position, trigger: ctx.trigger, roundness: ctx.roundness, fill: ctx.fill, spring: ctx.spring });
2351
+ if (ctx.controlledOpen !== void 0) {
2352
+ if (ctx.controlledOpen && !ctx.snapshot.open) ctx.machine.open();
2353
+ else if (!ctx.controlledOpen && ctx.snapshot.open) ctx.machine.close();
2354
+ }
2355
+ detachListeners(ctx.dom.rootEl, listeners);
2356
+ const newListeners = createListeners(ctx);
2357
+ Object.assign(listeners, newListeners);
2358
+ attachListeners(ctx.dom.rootEl, listeners);
2359
+ updateLayout(ctx);
2360
+ return listeners;
2361
+ }
2362
+ function createNotch(container, options) {
2363
+ const machine2 = createNotchMachine({
2364
+ position: options.position ?? "top-center",
2365
+ trigger: options.trigger ?? "click",
2366
+ roundness: options.roundness ?? NOTCH_DEFAULTS.roundness,
2367
+ fill: options.fill,
2368
+ spring: options.spring
2369
+ });
2370
+ const dotSize = options.dotSize ?? 36;
2371
+ const roundness = options.roundness ?? NOTCH_DEFAULTS.roundness;
2372
+ const spring = options.spring;
2373
+ const domCfg = {
2374
+ snapshot: machine2.store.getSnapshot(),
2375
+ position: options.position ?? "top-center",
2376
+ theme: options.theme ?? "dark",
2377
+ dotSize,
2378
+ fill: options.fill,
2379
+ contentSize: { w: 200, h: 44 },
2380
+ roundness};
2381
+ const dom = buildNotchDOM(domCfg, options.pill, options.content, container, () => springCfg(ctx));
2382
+ const ctx = {
2383
+ trigger: options.trigger ?? "click",
2384
+ position: options.position ?? "top-center",
2385
+ spring,
2386
+ dotSize,
2387
+ roundness,
2388
+ theme: options.theme ?? "dark",
2389
+ fill: options.fill,
2390
+ controlledOpen: options.open,
2391
+ onOpenChange: options.onOpenChange,
2392
+ snapshot: machine2.store.getSnapshot(),
2393
+ prevOpenVal: void 0,
2394
+ contentSize: { w: 200, h: 44 },
2395
+ currentAnim: null,
2396
+ measureRaf: 0,
2397
+ machine: machine2,
2398
+ prev: { w: dotSize, h: dotSize },
2399
+ highlight: dom.highlight,
2400
+ dom
2401
+ };
2402
+ let listeners = createListeners(ctx);
2403
+ attachListeners(ctx.dom.rootEl, listeners);
2152
2404
  const measureObs = new ResizeObserver(() => {
2153
- cancelAnimationFrame(measureRaf);
2154
- measureRaf = requestAnimationFrame(() => {
2155
- const r = measureEl.getBoundingClientRect();
2405
+ cancelAnimationFrame(ctx.measureRaf);
2406
+ ctx.measureRaf = requestAnimationFrame(() => {
2407
+ const r = ctx.dom.measureEl.getBoundingClientRect();
2156
2408
  if (r.width > 0 && r.height > 0) {
2157
2409
  const newSize = { w: Math.ceil(r.width), h: Math.ceil(r.height) };
2158
- if (newSize.w !== contentSize.w || newSize.h !== contentSize.h) {
2159
- contentSize = newSize;
2160
- updateLayout();
2410
+ if (newSize.w !== ctx.contentSize.w || newSize.h !== ctx.contentSize.h) {
2411
+ ctx.contentSize = newSize;
2412
+ updateLayout(ctx);
2161
2413
  }
2162
2414
  }
2163
2415
  });
2164
2416
  });
2165
- measureObs.observe(measureEl);
2166
- function onItemEnter(e) {
2167
- const target = e.target.closest("a, button");
2168
- if (!target || !snapshot.open) return;
2169
- const rootRect = rootEl.getBoundingClientRect();
2170
- const itemW = target.offsetWidth;
2171
- const itemH = target.offsetHeight;
2172
- const itemRect = target.getBoundingClientRect();
2173
- const itemCenterX = itemRect.left + itemRect.width / 2;
2174
- const itemCenterY = itemRect.top + itemRect.height / 2;
2175
- const padX = 8;
2176
- const padY = 4;
2177
- const blobOvershoot = Math.max(6, roundness * 0.35);
2178
- const toW = itemW + padX * 2;
2179
- const toH = Math.max(itemH + padY * 2, rootRect.height + blobOvershoot * 2);
2180
- const toX = itemCenterX - rootRect.left - toW / 2;
2181
- const toY = itemCenterY - rootRect.top - toH / 2;
2182
- const toRx = toH / 2;
2183
- const fromX = hlPrev.visible ? hlPrev.x : toX + toW / 2;
2184
- const fromY = hlPrev.visible ? hlPrev.y : toY + toH / 2;
2185
- const fromW = hlPrev.visible ? hlPrev.w : 0;
2186
- const fromH = hlPrev.visible ? hlPrev.h : 0;
2187
- const fromR = hlPrev.visible ? hlPrev.h / 2 : 0;
2188
- if (highlightAnim) {
2189
- highlightAnim.cancel();
2190
- highlightAnim = null;
2191
- }
2192
- const sc = springConfig();
2193
- const a = animateSpring(
2194
- hoverBlobEl,
2195
- {
2196
- x: { from: fromX, to: toX, unit: "px" },
2197
- y: { from: fromY, to: toY, unit: "px" },
2198
- width: { from: fromW, to: toW, unit: "px" },
2199
- height: { from: fromH, to: toH, unit: "px" },
2200
- rx: { from: fromR, to: toRx, unit: "px" },
2201
- ry: { from: fromR, to: toRx, unit: "px" }
2202
- },
2203
- { ...sc, stiffness: (sc.stiffness ?? 300) * 1.2 }
2204
- );
2205
- hlPrev.x = toX;
2206
- hlPrev.y = toY;
2207
- hlPrev.w = toW;
2208
- hlPrev.h = toH;
2209
- if (a) {
2210
- highlightAnim = a;
2211
- a.onfinish = () => {
2212
- highlightAnim = null;
2213
- hoverBlobEl.setAttribute("x", String(toX));
2214
- hoverBlobEl.setAttribute("y", String(toY));
2215
- hoverBlobEl.setAttribute("width", String(toW));
2216
- hoverBlobEl.setAttribute("height", String(toH));
2217
- hoverBlobEl.setAttribute("rx", String(toRx));
2218
- hoverBlobEl.setAttribute("ry", String(toRx));
2219
- hoverBlobEl.setAttribute("opacity", "1");
2220
- };
2221
- } else {
2222
- hoverBlobEl.setAttribute("x", String(toX));
2223
- hoverBlobEl.setAttribute("y", String(toY));
2224
- hoverBlobEl.setAttribute("width", String(toW));
2225
- hoverBlobEl.setAttribute("height", String(toH));
2226
- hoverBlobEl.setAttribute("rx", String(toRx));
2227
- hoverBlobEl.setAttribute("ry", String(toRx));
2228
- hoverBlobEl.setAttribute("opacity", "1");
2229
- }
2230
- hoverBlobEl.setAttribute("opacity", "1");
2231
- hlPrev.visible = true;
2232
- }
2233
- function resetHoverBlobImmediate() {
2234
- if (highlightAnim) {
2235
- highlightAnim.cancel();
2236
- highlightAnim = null;
2237
- }
2238
- hoverBlobEl.setAttribute("x", String(rootW() / 2));
2239
- hoverBlobEl.setAttribute("y", String(rootH() / 2));
2240
- hoverBlobEl.setAttribute("width", "0");
2241
- hoverBlobEl.setAttribute("height", "0");
2242
- hoverBlobEl.setAttribute("rx", "0");
2243
- hoverBlobEl.setAttribute("ry", "0");
2244
- hoverBlobEl.setAttribute("opacity", "0");
2245
- hlPrev.visible = false;
2246
- }
2247
- function onItemLeave() {
2248
- if (!hlPrev.visible) return;
2249
- const cx = hlPrev.x + hlPrev.w / 2;
2250
- const cy = hlPrev.y + hlPrev.h / 2;
2251
- const sc = springConfig();
2252
- const a = animateSpring(
2253
- hoverBlobEl,
2254
- {
2255
- x: { from: hlPrev.x, to: cx, unit: "px" },
2256
- y: { from: hlPrev.y, to: cy, unit: "px" },
2257
- width: { from: hlPrev.w, to: 0, unit: "px" },
2258
- height: { from: hlPrev.h, to: 0, unit: "px" },
2259
- rx: { from: hlPrev.h / 2, to: 0, unit: "px" },
2260
- ry: { from: hlPrev.h / 2, to: 0, unit: "px" }
2261
- },
2262
- { ...sc, stiffness: (sc.stiffness ?? 300) * 1.2 }
2263
- );
2264
- if (a) {
2265
- highlightAnim = a;
2266
- a.onfinish = () => {
2267
- highlightAnim = null;
2268
- hoverBlobEl.setAttribute("x", String(cx));
2269
- hoverBlobEl.setAttribute("y", String(cy));
2270
- hoverBlobEl.setAttribute("width", "0");
2271
- hoverBlobEl.setAttribute("height", "0");
2272
- hoverBlobEl.setAttribute("rx", "0");
2273
- hoverBlobEl.setAttribute("ry", "0");
2274
- hoverBlobEl.setAttribute("opacity", "0");
2275
- };
2276
- } else {
2277
- hoverBlobEl.setAttribute("x", String(cx));
2278
- hoverBlobEl.setAttribute("y", String(cy));
2279
- hoverBlobEl.setAttribute("width", "0");
2280
- hoverBlobEl.setAttribute("height", "0");
2281
- hoverBlobEl.setAttribute("rx", "0");
2282
- hoverBlobEl.setAttribute("ry", "0");
2283
- hoverBlobEl.setAttribute("opacity", "0");
2284
- }
2285
- hlPrev.visible = false;
2286
- }
2287
- function handleOpen() {
2288
- if (controlledOpen === void 0) machine2.open();
2289
- else onOpenChange?.(true);
2290
- }
2291
- function handleClose() {
2292
- if (controlledOpen === void 0) machine2.close();
2293
- else onOpenChange?.(false);
2294
- }
2295
- function handleToggle() {
2296
- if (controlledOpen === void 0) machine2.toggle();
2297
- else onOpenChange?.(!snapshot.open);
2298
- }
2299
- function onMouseEnter() {
2300
- if (trigger === "hover") handleOpen();
2301
- }
2302
- function onMouseLeave() {
2303
- if (trigger === "hover") {
2304
- handleClose();
2305
- resetHoverBlobImmediate();
2306
- return;
2307
- }
2308
- onItemLeave();
2309
- }
2310
- function onClick() {
2311
- if (trigger === "click") handleToggle();
2312
- }
2313
- rootEl.addEventListener("mouseenter", onMouseEnter);
2314
- rootEl.addEventListener("mouseleave", onMouseLeave);
2315
- rootEl.addEventListener("mouseover", onItemEnter);
2316
- rootEl.addEventListener("click", onClick);
2317
- function animateRect() {
2318
- const tw = targetW();
2319
- const th = targetH();
2320
- if (tw === prev.w && th === prev.h) return;
2321
- if (currentAnim) {
2322
- currentAnim.cancel();
2323
- currentAnim = null;
2324
- }
2325
- const rw = rootW();
2326
- const rh = rootH();
2327
- const fromW = prev.w;
2328
- const fromH = prev.h;
2329
- const fromX = (rw - fromW) / 2;
2330
- const fromY = (rh - fromH) / 2;
2331
- const toX = (rw - tw) / 2;
2332
- const toY = (rh - th) / 2;
2333
- prev.w = tw;
2334
- prev.h = th;
2335
- const isCollapsing = tw === collapsedW() && th === collapsedH();
2336
- const wasCollapsed = fromW === collapsedW() && fromH === collapsedH();
2337
- const fromRx = wasCollapsed ? collapsedW() / 2 : roundness;
2338
- const toRx = isCollapsing ? collapsedW() / 2 : roundness;
2339
- const a = animateSpring(
2340
- svgRectEl,
2341
- {
2342
- width: { from: fromW, to: tw, unit: "px" },
2343
- height: { from: fromH, to: th, unit: "px" },
2344
- x: { from: fromX, to: toX, unit: "px" },
2345
- y: { from: fromY, to: toY, unit: "px" },
2346
- rx: { from: fromRx, to: toRx, unit: "px" },
2347
- ry: { from: fromRx, to: toRx, unit: "px" }
2348
- },
2349
- springConfig()
2350
- );
2351
- if (a) {
2352
- currentAnim = a;
2353
- a.onfinish = () => {
2354
- currentAnim = null;
2355
- svgRectEl.setAttribute("width", String(tw));
2356
- svgRectEl.setAttribute("height", String(th));
2357
- svgRectEl.setAttribute("x", String(toX));
2358
- svgRectEl.setAttribute("y", String(toY));
2359
- svgRectEl.setAttribute("rx", String(toRx));
2360
- svgRectEl.setAttribute("ry", String(toRx));
2361
- };
2362
- } else {
2363
- svgRectEl.setAttribute("width", String(tw));
2364
- svgRectEl.setAttribute("height", String(th));
2365
- svgRectEl.setAttribute("x", String(toX));
2366
- svgRectEl.setAttribute("y", String(toY));
2367
- svgRectEl.setAttribute("rx", String(toRx));
2368
- svgRectEl.setAttribute("ry", String(toRx));
2369
- }
2370
- }
2371
- function updateLayout() {
2372
- const isOpen = snapshot.open;
2373
- const newAttrs = getNotchAttrs({ open: isOpen, position, theme });
2374
- applyAttrs2(rootEl, newAttrs.root);
2375
- rootEl.style.width = `${rootW()}px`;
2376
- rootEl.style.height = `${rootH()}px`;
2377
- svg.setAttribute("width", String(rootW()));
2378
- svg.setAttribute("height", String(rootH()));
2379
- svg.setAttribute("viewBox", `0 0 ${rootW()} ${rootH()}`);
2380
- feBlur.setAttribute("stdDeviation", String(blur()));
2381
- svgRectEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
2382
- hoverBlobEl.setAttribute("fill", fill ?? "var(--fluix-notch-bg)");
2383
- applyAttrs2(contentDiv, newAttrs.content);
2384
- animateRect();
2385
- if (!isOpen) {
2386
- resetHoverBlobImmediate();
2387
- }
2388
- document.documentElement.style.setProperty(
2389
- "--fluix-notch-offset",
2390
- `${rootH()}px`
2391
- );
2392
- }
2417
+ measureObs.observe(ctx.dom.measureEl);
2393
2418
  const unsubscribe = machine2.store.subscribe(() => {
2394
- const next = machine2.store.getSnapshot();
2395
- snapshot = next;
2396
- if (controlledOpen !== void 0) {
2397
- if (controlledOpen && !next.open) machine2.open();
2398
- else if (!controlledOpen && next.open) machine2.close();
2399
- }
2400
- if (prevOpenVal !== void 0 && prevOpenVal !== next.open) {
2401
- onOpenChange?.(next.open);
2402
- }
2403
- prevOpenVal = next.open;
2404
- updateLayout();
2419
+ ctx.snapshot = machine2.store.getSnapshot();
2420
+ if (ctx.controlledOpen !== void 0) {
2421
+ if (ctx.controlledOpen && !ctx.snapshot.open) machine2.open();
2422
+ else if (!ctx.controlledOpen && ctx.snapshot.open) machine2.close();
2423
+ }
2424
+ if (ctx.prevOpenVal !== void 0 && ctx.prevOpenVal !== ctx.snapshot.open) ctx.onOpenChange?.(ctx.snapshot.open);
2425
+ ctx.prevOpenVal = ctx.snapshot.open;
2426
+ updateLayout(ctx);
2405
2427
  });
2406
- updateLayout();
2407
- document.documentElement.style.setProperty(
2408
- "--fluix-notch-offset",
2409
- `${rootH()}px`
2410
- );
2428
+ updateLayout(ctx);
2429
+ document.documentElement.style.setProperty("--fluix-notch-offset", `${dims(ctx).rH}px`);
2411
2430
  return {
2412
2431
  open() {
2413
- handleOpen();
2432
+ ctx.controlledOpen === void 0 ? machine2.open() : ctx.onOpenChange?.(true);
2414
2433
  },
2415
2434
  close() {
2416
- handleClose();
2435
+ ctx.controlledOpen === void 0 ? machine2.close() : ctx.onOpenChange?.(false);
2417
2436
  },
2418
2437
  toggle() {
2419
- handleToggle();
2438
+ ctx.controlledOpen === void 0 ? machine2.toggle() : ctx.onOpenChange?.(!ctx.snapshot.open);
2420
2439
  },
2421
2440
  destroy() {
2422
2441
  unsubscribe();
2423
- cancelAnimationFrame(measureRaf);
2442
+ cancelAnimationFrame(ctx.measureRaf);
2424
2443
  measureObs.disconnect();
2425
- currentAnim?.cancel();
2426
- highlightAnim?.cancel();
2427
- rootEl.removeEventListener("mouseenter", onMouseEnter);
2428
- rootEl.removeEventListener("mouseleave", onMouseLeave);
2429
- rootEl.removeEventListener("mouseover", onItemEnter);
2430
- rootEl.removeEventListener("click", onClick);
2431
- measureEl.remove();
2432
- rootEl.remove();
2444
+ ctx.currentAnim?.cancel();
2445
+ ctx.highlight.cancelAnim();
2446
+ detachListeners(ctx.dom.rootEl, listeners);
2447
+ machine2.destroy();
2448
+ ctx.dom.measureEl.remove();
2449
+ ctx.dom.rootEl.remove();
2433
2450
  document.documentElement.style.removeProperty("--fluix-notch-offset");
2434
2451
  },
2435
2452
  update(opts) {
2436
- if (opts.trigger !== void 0) trigger = opts.trigger;
2437
- if (opts.position !== void 0) position = opts.position;
2438
- if (opts.spring !== void 0) spring = opts.spring;
2439
- if (opts.dotSize !== void 0) dotSize = opts.dotSize;
2440
- if (opts.roundness !== void 0) roundness = opts.roundness;
2441
- if (opts.theme !== void 0) theme = opts.theme;
2442
- if (opts.fill !== void 0) fill = opts.fill;
2443
- if (opts.open !== void 0) controlledOpen = opts.open;
2444
- if (opts.onOpenChange !== void 0) onOpenChange = opts.onOpenChange;
2445
- if (opts.pill !== void 0) {
2446
- pillDiv.textContent = "";
2447
- pillDiv.appendChild(resolveContent(opts.pill));
2448
- }
2449
- if (opts.content !== void 0) {
2450
- contentDiv.textContent = "";
2451
- contentDiv.appendChild(resolveContent(opts.content));
2452
- measureEl.textContent = "";
2453
- measureEl.appendChild(resolveContent(opts.content).cloneNode(true));
2454
- }
2455
- pillDiv.style.width = `${dotSize}px`;
2456
- pillDiv.style.height = `${dotSize}px`;
2457
- machine2.configure({ position, trigger, roundness, fill, spring });
2458
- if (controlledOpen !== void 0) {
2459
- if (controlledOpen && !snapshot.open) machine2.open();
2460
- else if (!controlledOpen && snapshot.open) machine2.close();
2461
- }
2462
- rootEl.removeEventListener("mouseenter", onMouseEnter);
2463
- rootEl.removeEventListener("mouseleave", onMouseLeave);
2464
- rootEl.removeEventListener("click", onClick);
2465
- rootEl.addEventListener("mouseenter", onMouseEnter);
2466
- rootEl.addEventListener("mouseleave", onMouseLeave);
2467
- rootEl.addEventListener("click", onClick);
2468
- updateLayout();
2453
+ listeners = notchUpdate(ctx, opts, listeners);
2469
2454
  }
2470
2455
  };
2471
2456
  }
2472
2457
 
2473
- // src/menu.ts
2474
- var SVG_NS3 = "http://www.w3.org/2000/svg";
2475
- var GOO_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10";
2476
- function applyAttrs3(el, attrs) {
2477
- for (const [key, value] of Object.entries(attrs)) {
2478
- el.setAttribute(key, value);
2458
+ // src/menu/index.ts
2459
+ function buildSvgIndicator(svg, isTab, filterId, resolvedBlur, fill, indicatorAttrs) {
2460
+ clearChildren(svg);
2461
+ const effectiveFill = fill ?? "var(--fluix-menu-indicator)";
2462
+ if (isTab) {
2463
+ const indicatorEl2 = document.createElementNS(SVG_NS, "path");
2464
+ applyAttrs(indicatorEl2, indicatorAttrs);
2465
+ indicatorEl2.setAttribute("d", "");
2466
+ indicatorEl2.setAttribute("opacity", "0");
2467
+ indicatorEl2.style.fill = effectiveFill;
2468
+ svg.appendChild(indicatorEl2);
2469
+ return { indicatorEl: indicatorEl2, ghostIndicatorEl: null };
2470
+ }
2471
+ const { g, defs } = createGooeyFilter(filterId, resolvedBlur);
2472
+ svg.appendChild(defs);
2473
+ const ghostIndicatorEl = document.createElementNS(SVG_NS, "rect");
2474
+ zeroRect(ghostIndicatorEl);
2475
+ ghostIndicatorEl.setAttribute("opacity", "0");
2476
+ ghostIndicatorEl.style.fill = effectiveFill;
2477
+ const indicatorEl = document.createElementNS(SVG_NS, "rect");
2478
+ applyAttrs(indicatorEl, indicatorAttrs);
2479
+ zeroRect(indicatorEl);
2480
+ indicatorEl.setAttribute("opacity", "0");
2481
+ indicatorEl.style.fill = effectiveFill;
2482
+ g.appendChild(ghostIndicatorEl);
2483
+ g.appendChild(indicatorEl);
2484
+ svg.appendChild(g);
2485
+ return { indicatorEl, ghostIndicatorEl };
2486
+ }
2487
+ function buildMenuDOM(attrs, filterId, variant, resolvedBlur, fill) {
2488
+ const navEl = document.createElement("nav");
2489
+ applyAttrs(navEl, attrs.root);
2490
+ navEl.setAttribute("aria-label", "Fluix menu");
2491
+ const canvasDiv = document.createElement("div");
2492
+ applyAttrs(canvasDiv, attrs.canvas);
2493
+ const svg = document.createElementNS(SVG_NS, "svg");
2494
+ svg.setAttribute("xmlns", SVG_NS);
2495
+ for (const [k, v] of Object.entries({ width: "1", height: "1", viewBox: "0 0 1 1", "aria-hidden": "true" })) svg.setAttribute(k, v);
2496
+ const { indicatorEl, ghostIndicatorEl } = buildSvgIndicator(svg, variant === "tab", filterId, resolvedBlur, fill, attrs.indicator);
2497
+ canvasDiv.appendChild(svg);
2498
+ navEl.appendChild(canvasDiv);
2499
+ const listDiv = document.createElement("div");
2500
+ applyAttrs(listDiv, attrs.list);
2501
+ navEl.appendChild(listDiv);
2502
+ return { navEl, svg, listDiv, buttonMap: /* @__PURE__ */ new Map(), indicatorEl, ghostIndicatorEl };
2503
+ }
2504
+ function createItemButton(ctx, item) {
2505
+ const btn = document.createElement("button");
2506
+ btn.type = "button";
2507
+ const active = ctx.snapshot.activeId === item.id;
2508
+ applyAttrs(btn, ctx.attrs.item({ id: item.id, active, disabled: item.disabled }));
2509
+ if (item.disabled) btn.disabled = true;
2510
+ btn.textContent = item.label;
2511
+ btn.addEventListener("click", () => {
2512
+ if (item.disabled) return;
2513
+ if (ctx.controlledActiveId === void 0) ctx.machine.setActive(item.id);
2514
+ else ctx.onActiveChange?.(item.id);
2515
+ });
2516
+ ctx.buttonMap.set(item.id, btn);
2517
+ ctx.listDiv.appendChild(btn);
2518
+ }
2519
+ function makeConnection(ctx) {
2520
+ return connectMenu({
2521
+ root: ctx.navEl,
2522
+ indicator: ctx.indicatorEl,
2523
+ ghostIndicator: ctx.ghostIndicatorEl,
2524
+ getActiveId: () => ctx.snapshot.activeId,
2525
+ onSelect(id) {
2526
+ if (ctx.controlledActiveId === void 0) ctx.machine.setActive(id);
2527
+ else ctx.onActiveChange?.(id);
2528
+ },
2529
+ spring: ctx.spring ?? FLUIX_SPRING,
2530
+ variant: ctx.variant,
2531
+ orientation: ctx.orientation
2532
+ });
2533
+ }
2534
+ function measure(ctx) {
2535
+ const rect = ctx.navEl.getBoundingClientRect();
2536
+ const w = Math.ceil(rect.width);
2537
+ const h = Math.ceil(rect.height);
2538
+ if (ctx.size.width !== w || ctx.size.height !== h) {
2539
+ ctx.size = { width: w, height: h };
2540
+ const sw = Math.max(1, w);
2541
+ const sh = Math.max(1, h);
2542
+ ctx.svg.setAttribute("width", String(sw));
2543
+ ctx.svg.setAttribute("height", String(sh));
2544
+ ctx.svg.setAttribute("viewBox", `0 0 ${sw} ${sh}`);
2545
+ ctx.connection?.sync(false);
2479
2546
  }
2480
2547
  }
2548
+ function menuUpdate(ctx, opts) {
2549
+ const prevVariant = ctx.variant;
2550
+ if (opts.orientation !== void 0) ctx.orientation = opts.orientation;
2551
+ if (opts.variant !== void 0) ctx.variant = opts.variant;
2552
+ if (opts.theme !== void 0) ctx.theme = opts.theme;
2553
+ if (opts.activeId !== void 0) ctx.controlledActiveId = opts.activeId;
2554
+ if (opts.onActiveChange !== void 0) ctx.onActiveChange = opts.onActiveChange;
2555
+ if (opts.spring !== void 0) ctx.spring = opts.spring;
2556
+ if (opts.roundness !== void 0) ctx.roundness = opts.roundness;
2557
+ if (opts.blur !== void 0) ctx.blurProp = opts.blur;
2558
+ if (opts.fill !== void 0) ctx.fill = opts.fill;
2559
+ ctx.machine.configure({ orientation: ctx.orientation, variant: ctx.variant, spring: ctx.spring, roundness: ctx.roundness, blur: ctx.blurProp, fill: ctx.fill });
2560
+ if (ctx.controlledActiveId !== void 0) ctx.machine.setActive(ctx.controlledActiveId ?? null);
2561
+ ctx.attrs = getMenuAttrs({ orientation: ctx.orientation, theme: ctx.theme, variant: ctx.variant });
2562
+ applyAttrs(ctx.navEl, ctx.attrs.root);
2563
+ if (prevVariant !== ctx.variant) {
2564
+ const resolvedBlur = ctx.blurProp ?? Math.min(10, Math.max(6, ctx.roundness * 0.45));
2565
+ const refs = buildSvgIndicator(ctx.svg, ctx.variant === "tab", ctx.filterId, resolvedBlur, ctx.fill, ctx.attrs.indicator);
2566
+ ctx.indicatorEl = refs.indicatorEl;
2567
+ ctx.ghostIndicatorEl = refs.ghostIndicatorEl;
2568
+ }
2569
+ if (opts.items !== void 0) {
2570
+ ctx.items = opts.items;
2571
+ clearChildren(ctx.listDiv);
2572
+ ctx.buttonMap.clear();
2573
+ for (const item of ctx.items) createItemButton(ctx, item);
2574
+ }
2575
+ ctx.connection.destroy();
2576
+ ctx.connection = makeConnection(ctx);
2577
+ requestAnimationFrame(() => {
2578
+ measure(ctx);
2579
+ ctx.connection.sync(false);
2580
+ });
2581
+ }
2481
2582
  function createMenu(container, options) {
2482
- let {
2583
+ const {
2483
2584
  orientation = MENU_DEFAULTS.orientation,
2484
2585
  variant = "pill",
2485
2586
  theme = "dark",
@@ -2491,8 +2592,6 @@ var Fluix = (function (exports) {
2491
2592
  fill,
2492
2593
  items
2493
2594
  } = options;
2494
- const springConfig = () => spring ?? FLUIX_SPRING;
2495
- const resolvedBlur = () => blurProp ?? Math.min(10, Math.max(6, roundness * 0.45));
2496
2595
  const machine2 = createMenuMachine({
2497
2596
  orientation,
2498
2597
  variant,
@@ -2502,216 +2601,69 @@ var Fluix = (function (exports) {
2502
2601
  fill,
2503
2602
  initialActiveId: controlledActiveId ?? null
2504
2603
  });
2505
- let snapshot = machine2.store.getSnapshot();
2506
- let lastActiveNotified = snapshot.activeId;
2507
2604
  const attrs = getMenuAttrs({ orientation, theme, variant });
2508
2605
  const filterId = `fluix-menu-goo-${Math.random().toString(36).slice(2, 8)}`;
2509
- const isTab = variant === "tab";
2510
- const navEl = document.createElement("nav");
2511
- applyAttrs3(navEl, attrs.root);
2512
- navEl.setAttribute("aria-label", "Fluix menu");
2513
- const canvasDiv = document.createElement("div");
2514
- applyAttrs3(canvasDiv, attrs.canvas);
2515
- const svg = document.createElementNS(SVG_NS3, "svg");
2516
- svg.setAttribute("xmlns", SVG_NS3);
2517
- svg.setAttribute("width", "1");
2518
- svg.setAttribute("height", "1");
2519
- svg.setAttribute("viewBox", "0 0 1 1");
2520
- svg.setAttribute("aria-hidden", "true");
2521
- let indicatorEl;
2522
- if (isTab) {
2523
- indicatorEl = document.createElementNS(SVG_NS3, "path");
2524
- applyAttrs3(indicatorEl, attrs.indicator);
2525
- indicatorEl.setAttribute("d", "");
2526
- indicatorEl.setAttribute("opacity", "0");
2527
- indicatorEl.style.fill = fill ?? "var(--fluix-menu-indicator)";
2528
- svg.appendChild(indicatorEl);
2529
- } else {
2530
- const defs = document.createElementNS(SVG_NS3, "defs");
2531
- const filter = document.createElementNS(SVG_NS3, "filter");
2532
- filter.setAttribute("id", filterId);
2533
- filter.setAttribute("x", "-20%");
2534
- filter.setAttribute("y", "-20%");
2535
- filter.setAttribute("width", "140%");
2536
- filter.setAttribute("height", "140%");
2537
- filter.setAttribute("color-interpolation-filters", "sRGB");
2538
- const feBlur = document.createElementNS(SVG_NS3, "feGaussianBlur");
2539
- feBlur.setAttribute("in", "SourceGraphic");
2540
- feBlur.setAttribute("stdDeviation", String(resolvedBlur()));
2541
- feBlur.setAttribute("result", "blur");
2542
- const feCM = document.createElementNS(SVG_NS3, "feColorMatrix");
2543
- feCM.setAttribute("in", "blur");
2544
- feCM.setAttribute("type", "matrix");
2545
- feCM.setAttribute("values", GOO_MATRIX);
2546
- feCM.setAttribute("result", "goo");
2547
- const feComp = document.createElementNS(SVG_NS3, "feComposite");
2548
- feComp.setAttribute("in", "SourceGraphic");
2549
- feComp.setAttribute("in2", "goo");
2550
- feComp.setAttribute("operator", "atop");
2551
- filter.appendChild(feBlur);
2552
- filter.appendChild(feCM);
2553
- filter.appendChild(feComp);
2554
- defs.appendChild(filter);
2555
- svg.appendChild(defs);
2556
- const gGroup = document.createElementNS(SVG_NS3, "g");
2557
- gGroup.setAttribute("filter", `url(#${filterId})`);
2558
- indicatorEl = document.createElementNS(SVG_NS3, "rect");
2559
- applyAttrs3(indicatorEl, attrs.indicator);
2560
- indicatorEl.setAttribute("x", "0");
2561
- indicatorEl.setAttribute("y", "0");
2562
- indicatorEl.setAttribute("width", "0");
2563
- indicatorEl.setAttribute("height", "0");
2564
- indicatorEl.setAttribute("rx", "0");
2565
- indicatorEl.setAttribute("ry", "0");
2566
- indicatorEl.setAttribute("opacity", "0");
2567
- indicatorEl.style.fill = fill ?? "var(--fluix-menu-indicator)";
2568
- gGroup.appendChild(indicatorEl);
2569
- svg.appendChild(gGroup);
2570
- }
2571
- canvasDiv.appendChild(svg);
2572
- navEl.appendChild(canvasDiv);
2573
- const listDiv = document.createElement("div");
2574
- applyAttrs3(listDiv, attrs.list);
2575
- const buttonMap = /* @__PURE__ */ new Map();
2576
- function createItemButton(item) {
2577
- const btn = document.createElement("button");
2578
- btn.type = "button";
2579
- const active = snapshot.activeId === item.id;
2580
- const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
2581
- applyAttrs3(btn, itemAttrs);
2582
- if (item.disabled) btn.disabled = true;
2583
- btn.textContent = item.label;
2584
- btn.addEventListener("click", () => {
2585
- if (item.disabled) return;
2586
- if (controlledActiveId === void 0) {
2587
- machine2.setActive(item.id);
2588
- } else {
2589
- onActiveChange?.(item.id);
2590
- }
2591
- });
2592
- buttonMap.set(item.id, btn);
2593
- listDiv.appendChild(btn);
2594
- }
2595
- for (const item of items) {
2596
- createItemButton(item);
2597
- }
2598
- navEl.appendChild(listDiv);
2599
- container.appendChild(navEl);
2600
- let size = { width: 0, height: 0 };
2601
- let measureRaf = 0;
2602
- const measure = () => {
2603
- const rect = navEl.getBoundingClientRect();
2604
- const w = Math.ceil(rect.width);
2605
- const h = Math.ceil(rect.height);
2606
- if (size.width !== w || size.height !== h) {
2607
- size = { width: w, height: h };
2608
- updateSvgSize();
2609
- connection?.sync(false);
2610
- }
2606
+ const resolvedBlur = blurProp ?? Math.min(10, Math.max(6, roundness * 0.45));
2607
+ const dom = buildMenuDOM(attrs, filterId, variant, resolvedBlur, fill);
2608
+ const ctx = {
2609
+ orientation,
2610
+ variant,
2611
+ theme,
2612
+ controlledActiveId,
2613
+ onActiveChange,
2614
+ spring,
2615
+ roundness,
2616
+ blurProp,
2617
+ fill,
2618
+ items,
2619
+ attrs,
2620
+ filterId,
2621
+ machine: machine2,
2622
+ snapshot: machine2.store.getSnapshot(),
2623
+ lastActiveNotified: machine2.store.getSnapshot().activeId,
2624
+ ...dom,
2625
+ connection: null,
2626
+ size: { width: 0, height: 0 },
2627
+ measureRaf: 0
2611
2628
  };
2629
+ for (const item of items) createItemButton(ctx, item);
2630
+ container.appendChild(ctx.navEl);
2612
2631
  const resizeObs = new ResizeObserver(() => {
2613
- cancelAnimationFrame(measureRaf);
2614
- measureRaf = requestAnimationFrame(measure);
2615
- });
2616
- resizeObs.observe(navEl);
2617
- function updateSvgSize() {
2618
- const w = Math.max(1, size.width);
2619
- const h = Math.max(1, size.height);
2620
- svg.setAttribute("width", String(w));
2621
- svg.setAttribute("height", String(h));
2622
- svg.setAttribute("viewBox", `0 0 ${w} ${h}`);
2623
- }
2624
- let connection = connectMenu({
2625
- root: navEl,
2626
- indicator: indicatorEl,
2627
- getActiveId: () => snapshot.activeId,
2628
- onSelect(id) {
2629
- if (controlledActiveId === void 0) {
2630
- machine2.setActive(id);
2631
- } else {
2632
- onActiveChange?.(id);
2633
- }
2634
- },
2635
- spring: springConfig(),
2636
- variant,
2637
- orientation
2632
+ cancelAnimationFrame(ctx.measureRaf);
2633
+ ctx.measureRaf = requestAnimationFrame(() => measure(ctx));
2638
2634
  });
2635
+ resizeObs.observe(ctx.navEl);
2636
+ ctx.connection = makeConnection(ctx);
2639
2637
  requestAnimationFrame(() => {
2640
- measure();
2641
- connection.sync(false);
2638
+ measure(ctx);
2639
+ ctx.connection.sync(false);
2642
2640
  });
2643
2641
  const unsubscribe = machine2.store.subscribe(() => {
2644
- const next = machine2.store.getSnapshot();
2645
- snapshot = next;
2646
- for (const item of items) {
2647
- const btn = buttonMap.get(item.id);
2648
- if (btn) {
2649
- const active = next.activeId === item.id;
2650
- const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
2651
- applyAttrs3(btn, itemAttrs);
2652
- }
2642
+ ctx.snapshot = machine2.store.getSnapshot();
2643
+ for (const item of ctx.items) {
2644
+ const btn = ctx.buttonMap.get(item.id);
2645
+ if (btn) applyAttrs(btn, ctx.attrs.item({ id: item.id, active: ctx.snapshot.activeId === item.id, disabled: item.disabled }));
2653
2646
  }
2654
- if (next.activeId && lastActiveNotified !== next.activeId && onActiveChange) {
2655
- onActiveChange(next.activeId);
2647
+ if (ctx.snapshot.activeId && ctx.lastActiveNotified !== ctx.snapshot.activeId && ctx.onActiveChange) {
2648
+ ctx.onActiveChange(ctx.snapshot.activeId);
2656
2649
  }
2657
- lastActiveNotified = next.activeId;
2658
- connection.sync(false);
2650
+ ctx.lastActiveNotified = ctx.snapshot.activeId;
2651
+ ctx.connection.sync(false);
2659
2652
  });
2660
2653
  return {
2661
2654
  setActive(id) {
2662
2655
  machine2.setActive(id);
2663
2656
  },
2664
2657
  update(opts) {
2665
- if (opts.orientation !== void 0) orientation = opts.orientation;
2666
- if (opts.variant !== void 0) variant = opts.variant;
2667
- if (opts.theme !== void 0) theme = opts.theme;
2668
- if (opts.activeId !== void 0) controlledActiveId = opts.activeId;
2669
- if (opts.onActiveChange !== void 0) onActiveChange = opts.onActiveChange;
2670
- if (opts.spring !== void 0) spring = opts.spring;
2671
- if (opts.roundness !== void 0) roundness = opts.roundness;
2672
- if (opts.blur !== void 0) blurProp = opts.blur;
2673
- if (opts.fill !== void 0) fill = opts.fill;
2674
- machine2.configure({ orientation, variant, spring, roundness, blur: blurProp, fill });
2675
- if (controlledActiveId !== void 0) {
2676
- machine2.setActive(controlledActiveId ?? null);
2677
- }
2678
- const newAttrs = getMenuAttrs({ orientation, theme, variant });
2679
- applyAttrs3(navEl, newAttrs.root);
2680
- if (opts.items !== void 0) {
2681
- items = opts.items;
2682
- listDiv.innerHTML = "";
2683
- buttonMap.clear();
2684
- for (const item of items) {
2685
- createItemButton(item);
2686
- }
2687
- }
2688
- connection.destroy();
2689
- connection = connectMenu({
2690
- root: navEl,
2691
- indicator: indicatorEl,
2692
- getActiveId: () => snapshot.activeId,
2693
- onSelect(id) {
2694
- if (controlledActiveId === void 0) {
2695
- machine2.setActive(id);
2696
- } else {
2697
- onActiveChange?.(id);
2698
- }
2699
- },
2700
- spring: springConfig(),
2701
- variant,
2702
- orientation
2703
- });
2704
- requestAnimationFrame(() => {
2705
- measure();
2706
- connection.sync(false);
2707
- });
2658
+ menuUpdate(ctx, opts);
2708
2659
  },
2709
2660
  destroy() {
2710
2661
  unsubscribe();
2711
- cancelAnimationFrame(measureRaf);
2662
+ cancelAnimationFrame(ctx.measureRaf);
2712
2663
  resizeObs.disconnect();
2713
- connection.destroy();
2714
- navEl.remove();
2664
+ ctx.connection.destroy();
2665
+ machine2.destroy();
2666
+ ctx.navEl.remove();
2715
2667
  }
2716
2668
  };
2717
2669
  }