@kywi-software/js 0.6.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -22,4 +22,5 @@ export declare const Kywi: {
22
22
  export { bootAudienceEngine } from './audience/index.js';
23
23
  export { openTransparencyPanel, renderTransparencyPanel, type TransparencyState, type TransparencyAudience, } from './audience/transparency.js';
24
24
  export { bootExperiments, type ExperimentRuntime, type ConversionSignal } from './experiments/index.js';
25
+ export { initNavMenus, enhanceNav, NAV_BREAKPOINT } from './nav/menu.js';
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AASnE,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,KAAK,IAAI,CAAA;AAIlD,eAAO,MAAM,IAAI;;aAEa,WAAW;YACzB,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC;gBACtC,eAAe,GAAG,IAAI;;2BAIb,oBAAoB,GAAG,oBAAoB;;;;oBAM9C,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;4BAC7C,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,IAAI;;;eAK5C,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG;aACvB,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG;gBAClB,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI;YAC7C,IAAI;CAeb,CAAA;AAWD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAUnE,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,KAAK,IAAI,CAAA;AAIlD,eAAO,MAAM,IAAI;;aAEa,WAAW;YACzB,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC;gBACtC,eAAe,GAAG,IAAI;;2BAIb,oBAAoB,GAAG,oBAAoB;;;;oBAM9C,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;4BAC7C,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,IAAI;;;eAK5C,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG;aACvB,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG;gBAClB,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI;YAC7C,IAAI;CAkBb,CAAA;AAWD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACvG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
package/dist/kywi.esm.js CHANGED
@@ -289,6 +289,188 @@ function initFormHooks() {
289
289
  }
290
290
  }
291
291
 
292
+ // src/nav/menu.ts
293
+ var NAV_BREAKPOINT = 860;
294
+ var CLOSE_DELAY_MS = 120;
295
+ var ITEM_CLASS = "kywi-nav-item--has-children";
296
+ var OPEN_CLASS = "kywi-nav-item--open";
297
+ var TOGGLE_CLASS = "kywi-nav-toggle";
298
+ var PANEL_CLASS = "kywi-nav-panel";
299
+ var FLIP_CLASS = "kywi-nav-panel--flip";
300
+ var READY_ATTRIBUTE = "data-kywi-nav-ready";
301
+ function directChild(parent, className) {
302
+ for (const child of Array.from(parent.children)) {
303
+ if (child instanceof HTMLElement && child.classList.contains(className)) return child;
304
+ }
305
+ return null;
306
+ }
307
+ function isDesktop() {
308
+ if (typeof window === "undefined") return false;
309
+ if (typeof window.matchMedia !== "function") return true;
310
+ return window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`).matches;
311
+ }
312
+ function panelLinks(panel) {
313
+ return Array.from(panel.querySelectorAll("a[href], button:not([disabled])"));
314
+ }
315
+ function enhanceNav(nav) {
316
+ const cleanups = [];
317
+ const entries = [];
318
+ for (const item of Array.from(nav.querySelectorAll(`.${ITEM_CLASS}`))) {
319
+ const toggle = directChild(item, TOGGLE_CLASS);
320
+ const panel = directChild(item, PANEL_CLASS);
321
+ if (toggle && panel) entries.push({ item, toggle, panel });
322
+ }
323
+ if (entries.length === 0) return () => {
324
+ };
325
+ let closeTimer;
326
+ const cancelClose = () => {
327
+ if (closeTimer !== void 0) {
328
+ clearTimeout(closeTimer);
329
+ closeTimer = void 0;
330
+ }
331
+ };
332
+ function isOpen(entry) {
333
+ return entry.item.classList.contains(OPEN_CLASS);
334
+ }
335
+ function close(entry) {
336
+ entry.item.classList.remove(OPEN_CLASS);
337
+ entry.toggle.setAttribute("aria-expanded", "false");
338
+ entry.panel.classList.remove(FLIP_CLASS);
339
+ }
340
+ function closeAll(except) {
341
+ for (const entry of entries) {
342
+ if (except && (entry === except || entry.item.contains(except.item))) continue;
343
+ close(entry);
344
+ }
345
+ }
346
+ function applyEdgeFlip(entry) {
347
+ if (typeof entry.panel.getBoundingClientRect !== "function") return;
348
+ entry.panel.classList.remove(FLIP_CLASS);
349
+ const rect = entry.panel.getBoundingClientRect();
350
+ const viewport = window.innerWidth || document.documentElement.clientWidth || 0;
351
+ if (viewport > 0 && rect.width > 0 && rect.right > viewport - 4) {
352
+ entry.panel.classList.add(FLIP_CLASS);
353
+ }
354
+ }
355
+ function open(entry) {
356
+ cancelClose();
357
+ closeAll(entry);
358
+ entry.item.classList.add(OPEN_CLASS);
359
+ entry.toggle.setAttribute("aria-expanded", "true");
360
+ applyEdgeFlip(entry);
361
+ }
362
+ function on(target, type, handler, options) {
363
+ target.addEventListener(type, handler, options);
364
+ cleanups.push(() => target.removeEventListener(type, handler, options));
365
+ }
366
+ for (const entry of entries) {
367
+ on(entry.toggle, "click", (event) => {
368
+ event.preventDefault();
369
+ if (isOpen(entry)) {
370
+ close(entry);
371
+ } else {
372
+ open(entry);
373
+ }
374
+ });
375
+ on(entry.item, "mouseenter", () => {
376
+ if (!isDesktop()) return;
377
+ open(entry);
378
+ });
379
+ on(entry.item, "mouseleave", () => {
380
+ if (!isDesktop()) return;
381
+ cancelClose();
382
+ closeTimer = setTimeout(() => {
383
+ close(entry);
384
+ closeTimer = void 0;
385
+ }, CLOSE_DELAY_MS);
386
+ });
387
+ }
388
+ on(nav, "focusin", (event) => {
389
+ const target = event.target;
390
+ if (!target) return;
391
+ for (const entry of entries) {
392
+ if (entry.panel.contains(target)) {
393
+ open(entry);
394
+ return;
395
+ }
396
+ }
397
+ for (const entry of entries) {
398
+ if (!entry.item.contains(target)) close(entry);
399
+ }
400
+ });
401
+ on(nav, "keydown", (event) => {
402
+ const target = event.target;
403
+ if (!target) return;
404
+ const entry = entries.find((e) => e.item.contains(target));
405
+ if (!entry) return;
406
+ if (event.key === "Escape") {
407
+ const openEntry = entries.filter((e) => e.item.contains(target) && isOpen(e)).pop();
408
+ if (!openEntry) return;
409
+ event.preventDefault();
410
+ close(openEntry);
411
+ openEntry.toggle.focus();
412
+ return;
413
+ }
414
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
415
+ if (target === entry.toggle) {
416
+ if (event.key === "ArrowDown") {
417
+ event.preventDefault();
418
+ if (!isOpen(entry)) open(entry);
419
+ panelLinks(entry.panel)[0]?.focus();
420
+ } else if (isOpen(entry)) {
421
+ event.preventDefault();
422
+ close(entry);
423
+ }
424
+ return;
425
+ }
426
+ const owner = entries.find((e) => e.panel.contains(target));
427
+ if (!owner) return;
428
+ const links = panelLinks(owner.panel);
429
+ const index = links.indexOf(target);
430
+ if (index === -1) return;
431
+ event.preventDefault();
432
+ const next = event.key === "ArrowDown" ? links[(index + 1) % links.length] : links[(index - 1 + links.length) % links.length];
433
+ next?.focus();
434
+ });
435
+ on(document, "click", (event) => {
436
+ const target = event.target;
437
+ if (target && nav.contains(target)) return;
438
+ cancelClose();
439
+ closeAll();
440
+ });
441
+ if (typeof window.matchMedia === "function") {
442
+ const query = window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`);
443
+ const onChange = () => {
444
+ cancelClose();
445
+ closeAll();
446
+ };
447
+ if (typeof query.addEventListener === "function") {
448
+ query.addEventListener("change", onChange);
449
+ cleanups.push(() => query.removeEventListener("change", onChange));
450
+ }
451
+ }
452
+ nav.setAttribute(READY_ATTRIBUTE, "");
453
+ return () => {
454
+ cancelClose();
455
+ for (const undo of cleanups) undo();
456
+ cleanups.length = 0;
457
+ closeAll();
458
+ nav.removeAttribute(READY_ATTRIBUTE);
459
+ };
460
+ }
461
+ function initNavMenus(root = document) {
462
+ if (typeof document === "undefined") return () => {
463
+ };
464
+ const teardowns = [];
465
+ for (const nav of Array.from(root.querySelectorAll("[data-kywi-nav]"))) {
466
+ if (nav.hasAttribute(READY_ATTRIBUTE)) continue;
467
+ teardowns.push(enhanceNav(nav));
468
+ }
469
+ return () => {
470
+ for (const undo of teardowns) undo();
471
+ };
472
+ }
473
+
292
474
  // src/experiments/index.ts
293
475
  function apiBaseFrom(ctx) {
294
476
  return ctx.basePath && ctx.basePath !== "/" ? `${ctx.basePath}/api/v1` : "/api/v1";
@@ -1496,6 +1678,7 @@ var Kywi = {
1496
1678
  Kywi.renderFeed = renderFeed;
1497
1679
  initModules();
1498
1680
  initFormHooks();
1681
+ initNavMenus();
1499
1682
  bootExperiments({ context: Kywi.context });
1500
1683
  booted = true;
1501
1684
  }
@@ -1512,8 +1695,11 @@ if (typeof document !== "undefined") {
1512
1695
  }
1513
1696
  export {
1514
1697
  Kywi,
1698
+ NAV_BREAKPOINT,
1515
1699
  bootAudienceEngine,
1516
1700
  bootExperiments,
1701
+ enhanceNav,
1702
+ initNavMenus,
1517
1703
  openTransparencyPanel,
1518
1704
  renderTransparencyPanel
1519
1705
  };