@fluix-ui/vanilla 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,14 +8,104 @@ Vanilla JS adapter for Fluix UI components.
8
8
  npm install @fluix-ui/vanilla @fluix-ui/css
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## How imports work in Vanilla JS
12
+
13
+ ### Option A: npm + bundler (Vite/Webpack/Rollup)
14
+
15
+ Use standard ESM imports in your source files:
16
+
17
+ ```ts
18
+ import { createToaster, fluix } from "@fluix-ui/vanilla";
19
+ import "@fluix-ui/css";
20
+ ```
21
+
22
+ ### Option B: browser without bundler (CDN + global)
23
+
24
+ The IIFE build exposes a global `window.Fluix`.
25
+
26
+ ```html
27
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fluix-ui/css/dist/fluix.css" />
28
+ <script src="https://cdn.jsdelivr.net/npm/@fluix-ui/vanilla/dist/index.global.js"></script>
29
+ <script>
30
+ const { createToaster, fluix, createNotch, createMenu } = window.Fluix;
31
+ createToaster({ position: "top-right" });
32
+ fluix.success({ title: "Loaded" });
33
+ </script>
34
+ ```
35
+
36
+ ## Quick start (Toasts)
12
37
 
13
38
  ```ts
14
39
  import { createToaster, fluix } from "@fluix-ui/vanilla";
15
40
  import "@fluix-ui/css";
41
+
42
+ const toaster = createToaster({ position: "top-right", layout: "stack" });
43
+
44
+ document.querySelector("#save-btn")?.addEventListener("click", () => {
45
+ fluix.success({
46
+ title: "Saved",
47
+ description: "Your changes were stored.",
48
+ });
49
+ });
50
+ ```
51
+
52
+ ### Promise toasts
53
+
54
+ ```ts
55
+ await fluix.promise(saveUser(), {
56
+ loading: { title: "Saving..." },
57
+ success: (data) => ({
58
+ title: "Saved",
59
+ description: `User ${data.name} updated`,
60
+ }),
61
+ error: (err) => ({
62
+ title: "Failed",
63
+ description: err instanceof Error ? err.message : "Unexpected error",
64
+ }),
65
+ });
16
66
  ```
17
67
 
18
- ### Custom themes
68
+ ## Notch
69
+
70
+ ```ts
71
+ import { createNotch } from "@fluix-ui/vanilla";
72
+
73
+ const container = document.querySelector("#notch-root");
74
+ if (!container) throw new Error("Missing #notch-root");
75
+
76
+ const notch = createNotch(container, {
77
+ position: "top-center",
78
+ trigger: "click",
79
+ theme: "dark",
80
+ pill: "Now",
81
+ content: "Now playing: Fluix Theme",
82
+ });
83
+
84
+ notch.open();
85
+ ```
86
+
87
+ ## Menu
88
+
89
+ ```ts
90
+ import { createMenu } from "@fluix-ui/vanilla";
91
+
92
+ const container = document.querySelector("#menu-root");
93
+ if (!container) throw new Error("Missing #menu-root");
94
+
95
+ const menu = createMenu(container, {
96
+ activeId: "home",
97
+ orientation: "horizontal",
98
+ variant: "pill",
99
+ theme: "dark",
100
+ items: [
101
+ { id: "home", label: "Home" },
102
+ { id: "projects", label: "Projects" },
103
+ { id: "settings", label: "Settings" },
104
+ ],
105
+ });
106
+ ```
107
+
108
+ ## Theming
19
109
 
20
110
  Pass any theme name — themes are pure CSS. See `@fluix-ui/css` for details.
21
111
 
@@ -23,6 +113,20 @@ Pass any theme name — themes are pure CSS. See `@fluix-ui/css` for details.
23
113
  fluix.success({ title: "Done", theme: "midnight" });
24
114
  ```
25
115
 
116
+ ## Exports
117
+
118
+ ```ts
119
+ import { createToaster, createNotch, createMenu, fluix } from "@fluix-ui/vanilla";
120
+ import type {
121
+ NotchOptions,
122
+ MenuOptions,
123
+ MenuInstance,
124
+ MenuItemConfig,
125
+ FluixToastOptions,
126
+ FluixToasterConfig,
127
+ } from "@fluix-ui/vanilla";
128
+ ```
129
+
26
130
  ## Docs
27
131
 
28
132
  - Official docs: https://fluix.ivanlopezdev.es
package/dist/index.cjs CHANGED
@@ -1236,11 +1236,257 @@ function createNotch(container, options) {
1236
1236
  }
1237
1237
  };
1238
1238
  }
1239
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
1240
+ var GOO_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10";
1241
+ function applyAttrs3(el, attrs) {
1242
+ for (const [key, value] of Object.entries(attrs)) {
1243
+ el.setAttribute(key, value);
1244
+ }
1245
+ }
1246
+ function createMenu(container, options) {
1247
+ let {
1248
+ orientation = core.MENU_DEFAULTS.orientation,
1249
+ variant = "pill",
1250
+ theme = "dark",
1251
+ activeId: controlledActiveId,
1252
+ onActiveChange,
1253
+ spring,
1254
+ roundness = core.MENU_DEFAULTS.roundness,
1255
+ blur: blurProp,
1256
+ fill,
1257
+ items
1258
+ } = options;
1259
+ const springConfig = () => spring ?? core.FLUIX_SPRING;
1260
+ const resolvedBlur = () => blurProp ?? Math.min(10, Math.max(6, roundness * 0.45));
1261
+ const machine = core.createMenuMachine({
1262
+ orientation,
1263
+ variant,
1264
+ spring,
1265
+ roundness,
1266
+ blur: blurProp,
1267
+ fill,
1268
+ initialActiveId: controlledActiveId ?? null
1269
+ });
1270
+ let snapshot = machine.store.getSnapshot();
1271
+ let lastActiveNotified = snapshot.activeId;
1272
+ const attrs = core.getMenuAttrs({ orientation, theme, variant });
1273
+ const filterId = `fluix-menu-goo-${Math.random().toString(36).slice(2, 8)}`;
1274
+ const isTab = variant === "tab";
1275
+ const navEl = document.createElement("nav");
1276
+ applyAttrs3(navEl, attrs.root);
1277
+ navEl.setAttribute("aria-label", "Fluix menu");
1278
+ const canvasDiv = document.createElement("div");
1279
+ applyAttrs3(canvasDiv, attrs.canvas);
1280
+ const svg = document.createElementNS(SVG_NS3, "svg");
1281
+ svg.setAttribute("xmlns", SVG_NS3);
1282
+ svg.setAttribute("width", "1");
1283
+ svg.setAttribute("height", "1");
1284
+ svg.setAttribute("viewBox", "0 0 1 1");
1285
+ svg.setAttribute("aria-hidden", "true");
1286
+ let indicatorEl;
1287
+ if (isTab) {
1288
+ indicatorEl = document.createElementNS(SVG_NS3, "path");
1289
+ applyAttrs3(indicatorEl, attrs.indicator);
1290
+ indicatorEl.setAttribute("d", "");
1291
+ indicatorEl.setAttribute("opacity", "0");
1292
+ indicatorEl.setAttribute("fill", fill ?? "var(--fluix-menu-indicator)");
1293
+ svg.appendChild(indicatorEl);
1294
+ } else {
1295
+ const defs = document.createElementNS(SVG_NS3, "defs");
1296
+ const filter = document.createElementNS(SVG_NS3, "filter");
1297
+ filter.setAttribute("id", filterId);
1298
+ filter.setAttribute("x", "-20%");
1299
+ filter.setAttribute("y", "-20%");
1300
+ filter.setAttribute("width", "140%");
1301
+ filter.setAttribute("height", "140%");
1302
+ filter.setAttribute("color-interpolation-filters", "sRGB");
1303
+ const feBlur = document.createElementNS(SVG_NS3, "feGaussianBlur");
1304
+ feBlur.setAttribute("in", "SourceGraphic");
1305
+ feBlur.setAttribute("stdDeviation", String(resolvedBlur()));
1306
+ feBlur.setAttribute("result", "blur");
1307
+ const feCM = document.createElementNS(SVG_NS3, "feColorMatrix");
1308
+ feCM.setAttribute("in", "blur");
1309
+ feCM.setAttribute("type", "matrix");
1310
+ feCM.setAttribute("values", GOO_MATRIX);
1311
+ feCM.setAttribute("result", "goo");
1312
+ const feComp = document.createElementNS(SVG_NS3, "feComposite");
1313
+ feComp.setAttribute("in", "SourceGraphic");
1314
+ feComp.setAttribute("in2", "goo");
1315
+ feComp.setAttribute("operator", "atop");
1316
+ filter.appendChild(feBlur);
1317
+ filter.appendChild(feCM);
1318
+ filter.appendChild(feComp);
1319
+ defs.appendChild(filter);
1320
+ svg.appendChild(defs);
1321
+ const gGroup = document.createElementNS(SVG_NS3, "g");
1322
+ gGroup.setAttribute("filter", `url(#${filterId})`);
1323
+ indicatorEl = document.createElementNS(SVG_NS3, "rect");
1324
+ applyAttrs3(indicatorEl, attrs.indicator);
1325
+ indicatorEl.setAttribute("x", "0");
1326
+ indicatorEl.setAttribute("y", "0");
1327
+ indicatorEl.setAttribute("width", "0");
1328
+ indicatorEl.setAttribute("height", "0");
1329
+ indicatorEl.setAttribute("rx", "0");
1330
+ indicatorEl.setAttribute("ry", "0");
1331
+ indicatorEl.setAttribute("opacity", "0");
1332
+ indicatorEl.setAttribute("fill", fill ?? "var(--fluix-menu-indicator)");
1333
+ gGroup.appendChild(indicatorEl);
1334
+ svg.appendChild(gGroup);
1335
+ }
1336
+ canvasDiv.appendChild(svg);
1337
+ navEl.appendChild(canvasDiv);
1338
+ const listDiv = document.createElement("div");
1339
+ applyAttrs3(listDiv, attrs.list);
1340
+ const buttonMap = /* @__PURE__ */ new Map();
1341
+ function createItemButton(item) {
1342
+ const btn = document.createElement("button");
1343
+ btn.type = "button";
1344
+ const active = snapshot.activeId === item.id;
1345
+ const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
1346
+ applyAttrs3(btn, itemAttrs);
1347
+ if (item.disabled) btn.disabled = true;
1348
+ btn.textContent = item.label;
1349
+ btn.addEventListener("click", () => {
1350
+ if (item.disabled) return;
1351
+ if (controlledActiveId === void 0) {
1352
+ machine.setActive(item.id);
1353
+ } else {
1354
+ onActiveChange?.(item.id);
1355
+ }
1356
+ });
1357
+ buttonMap.set(item.id, btn);
1358
+ listDiv.appendChild(btn);
1359
+ }
1360
+ for (const item of items) {
1361
+ createItemButton(item);
1362
+ }
1363
+ navEl.appendChild(listDiv);
1364
+ container.appendChild(navEl);
1365
+ let size = { width: 0, height: 0 };
1366
+ let measureRaf = 0;
1367
+ const measure = () => {
1368
+ const rect = navEl.getBoundingClientRect();
1369
+ const w = Math.ceil(rect.width);
1370
+ const h = Math.ceil(rect.height);
1371
+ if (size.width !== w || size.height !== h) {
1372
+ size = { width: w, height: h };
1373
+ updateSvgSize();
1374
+ connection?.sync(false);
1375
+ }
1376
+ };
1377
+ const resizeObs = new ResizeObserver(() => {
1378
+ cancelAnimationFrame(measureRaf);
1379
+ measureRaf = requestAnimationFrame(measure);
1380
+ });
1381
+ resizeObs.observe(navEl);
1382
+ function updateSvgSize() {
1383
+ const w = Math.max(1, size.width);
1384
+ const h = Math.max(1, size.height);
1385
+ svg.setAttribute("width", String(w));
1386
+ svg.setAttribute("height", String(h));
1387
+ svg.setAttribute("viewBox", `0 0 ${w} ${h}`);
1388
+ }
1389
+ let connection = core.connectMenu({
1390
+ root: navEl,
1391
+ indicator: indicatorEl,
1392
+ getActiveId: () => snapshot.activeId,
1393
+ onSelect(id) {
1394
+ if (controlledActiveId === void 0) {
1395
+ machine.setActive(id);
1396
+ } else {
1397
+ onActiveChange?.(id);
1398
+ }
1399
+ },
1400
+ spring: springConfig(),
1401
+ variant,
1402
+ orientation
1403
+ });
1404
+ requestAnimationFrame(() => {
1405
+ measure();
1406
+ connection.sync(false);
1407
+ });
1408
+ const unsubscribe = machine.store.subscribe(() => {
1409
+ const next = machine.store.getSnapshot();
1410
+ snapshot = next;
1411
+ for (const item of items) {
1412
+ const btn = buttonMap.get(item.id);
1413
+ if (btn) {
1414
+ const active = next.activeId === item.id;
1415
+ const itemAttrs = attrs.item({ id: item.id, active, disabled: item.disabled });
1416
+ applyAttrs3(btn, itemAttrs);
1417
+ }
1418
+ }
1419
+ if (next.activeId && lastActiveNotified !== next.activeId && onActiveChange) {
1420
+ onActiveChange(next.activeId);
1421
+ }
1422
+ lastActiveNotified = next.activeId;
1423
+ connection.sync(false);
1424
+ });
1425
+ return {
1426
+ setActive(id) {
1427
+ machine.setActive(id);
1428
+ },
1429
+ update(opts) {
1430
+ if (opts.orientation !== void 0) orientation = opts.orientation;
1431
+ if (opts.variant !== void 0) variant = opts.variant;
1432
+ if (opts.theme !== void 0) theme = opts.theme;
1433
+ if (opts.activeId !== void 0) controlledActiveId = opts.activeId;
1434
+ if (opts.onActiveChange !== void 0) onActiveChange = opts.onActiveChange;
1435
+ if (opts.spring !== void 0) spring = opts.spring;
1436
+ if (opts.roundness !== void 0) roundness = opts.roundness;
1437
+ if (opts.blur !== void 0) blurProp = opts.blur;
1438
+ if (opts.fill !== void 0) fill = opts.fill;
1439
+ machine.configure({ orientation, variant, spring, roundness, blur: blurProp, fill });
1440
+ if (controlledActiveId !== void 0) {
1441
+ machine.setActive(controlledActiveId ?? null);
1442
+ }
1443
+ const newAttrs = core.getMenuAttrs({ orientation, theme, variant });
1444
+ applyAttrs3(navEl, newAttrs.root);
1445
+ if (opts.items !== void 0) {
1446
+ items = opts.items;
1447
+ listDiv.innerHTML = "";
1448
+ buttonMap.clear();
1449
+ for (const item of items) {
1450
+ createItemButton(item);
1451
+ }
1452
+ }
1453
+ connection.destroy();
1454
+ connection = core.connectMenu({
1455
+ root: navEl,
1456
+ indicator: indicatorEl,
1457
+ getActiveId: () => snapshot.activeId,
1458
+ onSelect(id) {
1459
+ if (controlledActiveId === void 0) {
1460
+ machine.setActive(id);
1461
+ } else {
1462
+ onActiveChange?.(id);
1463
+ }
1464
+ },
1465
+ spring: springConfig(),
1466
+ variant,
1467
+ orientation
1468
+ });
1469
+ requestAnimationFrame(() => {
1470
+ measure();
1471
+ connection.sync(false);
1472
+ });
1473
+ },
1474
+ destroy() {
1475
+ unsubscribe();
1476
+ cancelAnimationFrame(measureRaf);
1477
+ resizeObs.disconnect();
1478
+ connection.destroy();
1479
+ machine.destroy();
1480
+ navEl.remove();
1481
+ }
1482
+ };
1483
+ }
1239
1484
 
1240
1485
  Object.defineProperty(exports, "fluix", {
1241
1486
  enumerable: true,
1242
1487
  get: function () { return core.fluix; }
1243
1488
  });
1489
+ exports.createMenu = createMenu;
1244
1490
  exports.createNotch = createNotch;
1245
1491
  exports.createToaster = createToaster;
1246
1492
  //# sourceMappingURL=index.cjs.map