@codbex/harmonia 1.11.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/harmonia.js CHANGED
@@ -4864,13 +4864,13 @@
4864
4864
  // src/components/menu.js
4865
4865
  function menu_default(Alpine) {
4866
4866
  Alpine.directive("h-menu-trigger", (el, { modifiers }) => {
4867
- el._menu_trigger = {
4867
+ el._h_menu_trigger = {
4868
4868
  isDropdown: modifiers.includes("dropdown"),
4869
4869
  setOpen(open) {
4870
4870
  el.setAttribute("aria-expanded", open);
4871
4871
  }
4872
4872
  };
4873
- if (el._menu_trigger.isDropdown) {
4873
+ if (el._h_menu_trigger.isDropdown) {
4874
4874
  el.setAttribute("aria-haspopup", "true");
4875
4875
  el.setAttribute("aria-expanded", "false");
4876
4876
  if (!el.hasAttribute("id")) {
@@ -4912,10 +4912,10 @@
4912
4912
  const menuTrigger = (() => {
4913
4913
  if (isSubmenu) return;
4914
4914
  let sibling = el.previousElementSibling;
4915
- while (sibling && !Object.prototype.hasOwnProperty.call(sibling, "_menu_trigger")) {
4915
+ while (sibling && !Object.prototype.hasOwnProperty.call(sibling, "_h_menu_trigger")) {
4916
4916
  sibling = sibling.previousElementSibling;
4917
4917
  }
4918
- if (!Object.prototype.hasOwnProperty.call(sibling, "_menu_trigger")) {
4918
+ if (!Object.prototype.hasOwnProperty.call(sibling, "_h_menu_trigger")) {
4919
4919
  throw new Error(`${original} menu must be placed after a menu trigger element`);
4920
4920
  }
4921
4921
  return sibling;
@@ -4937,16 +4937,22 @@
4937
4937
  }
4938
4938
  setAriaAttrubutes(menuSubItem);
4939
4939
  } else if (menuTrigger) {
4940
- setAriaAttrubutes(menuTrigger._menu_trigger.isDropdown ? menuTrigger : void 0);
4940
+ if (menuTrigger._h_menu_trigger.isDropdown) {
4941
+ if (!el.hasAttribute("id")) {
4942
+ el.setAttribute("id", `m${uuid_default()}`);
4943
+ }
4944
+ menuTrigger.setAttribute("aria-controls", el.getAttribute("id"));
4945
+ setAriaAttrubutes(menuTrigger);
4946
+ } else setAriaAttrubutes();
4941
4947
  } else {
4942
4948
  setAriaAttrubutes();
4943
4949
  }
4944
4950
  function listenForTrigger(listen) {
4945
4951
  if (listen) {
4946
- if (menuTrigger._menu_trigger.isDropdown) menuTrigger.addEventListener("click", openDropdown);
4952
+ if (menuTrigger._h_menu_trigger.isDropdown) menuTrigger.addEventListener("click", openDropdown);
4947
4953
  else menuTrigger.addEventListener("contextmenu", onContextmenu);
4948
4954
  } else {
4949
- if (menuTrigger._menu_trigger.isDropdown) menuTrigger.removeEventListener("click", openDropdown);
4955
+ if (menuTrigger._h_menu_trigger.isDropdown) menuTrigger.removeEventListener("click", openDropdown);
4950
4956
  else menuTrigger.removeEventListener("contextmenu", onContextmenu);
4951
4957
  }
4952
4958
  }
@@ -4975,8 +4981,8 @@
4975
4981
  } else {
4976
4982
  listenForTrigger(true);
4977
4983
  if (focusTrigger) menuTrigger.focus();
4978
- if (menuTrigger._menu_trigger.isDropdown) {
4979
- menuTrigger._menu_trigger.setOpen(false);
4984
+ if (menuTrigger._h_menu_trigger.isDropdown) {
4985
+ menuTrigger._h_menu_trigger.setOpen(false);
4980
4986
  }
4981
4987
  }
4982
4988
  }
@@ -5091,7 +5097,7 @@
5091
5097
  let getPlacement = function() {
5092
5098
  if (isSubmenu) {
5093
5099
  return "right-start";
5094
- } else if (menuTrigger._menu_trigger.isDropdown) {
5100
+ } else if (menuTrigger._h_menu_trigger.isDropdown) {
5095
5101
  return el.getAttribute("data-align") || "bottom-start";
5096
5102
  }
5097
5103
  return "right-start";
@@ -5136,7 +5142,7 @@
5136
5142
  el.classList.remove("hidden");
5137
5143
  el.pauseKeyEvents = false;
5138
5144
  let firstOpen = true;
5139
- if (!isSubmenu && menuTrigger._menu_trigger.isDropdown) {
5145
+ if (!isSubmenu && menuTrigger._h_menu_trigger.isDropdown) {
5140
5146
  autoUpdateCleanup = autoUpdate(parent, el, updatePosition);
5141
5147
  } else {
5142
5148
  updatePosition();
@@ -5144,8 +5150,8 @@
5144
5150
  }
5145
5151
  }
5146
5152
  function openDropdown() {
5147
- if (menuTrigger._menu_trigger.isDropdown) {
5148
- menuTrigger._menu_trigger.setOpen(true);
5153
+ if (menuTrigger._h_menu_trigger.isDropdown) {
5154
+ menuTrigger._h_menu_trigger.setOpen(true);
5149
5155
  }
5150
5156
  open(menuTrigger);
5151
5157
  }
@@ -5171,6 +5177,10 @@
5171
5177
  menuSubItem._menu_sub.open = open;
5172
5178
  menuSubItem._menu_sub.close = close;
5173
5179
  } else {
5180
+ if (menuTrigger._h_menu_trigger.navItem) {
5181
+ menuTrigger._h_menu_trigger.openMenu = openDropdown;
5182
+ menuTrigger._h_menu_trigger.closeMenu = close;
5183
+ }
5174
5184
  listenForTrigger(true);
5175
5185
  }
5176
5186
  function onTransitionEnd(event) {
@@ -5539,6 +5549,154 @@
5539
5549
  });
5540
5550
  }
5541
5551
 
5552
+ // src/components/navigation-menu.js
5553
+ var navItemTriggerClasses = [
5554
+ "inline-flex",
5555
+ "h-9",
5556
+ "w-max",
5557
+ "items-center",
5558
+ "justify-center",
5559
+ "rounded-control",
5560
+ "px-3",
5561
+ "py-2",
5562
+ "text-sm",
5563
+ "font-medium",
5564
+ "whitespace-nowrap",
5565
+ "gap-1.5",
5566
+ "transition-colors",
5567
+ "duration-100",
5568
+ "motion-reduce:transition-none",
5569
+ "hover:bg-secondary-hover",
5570
+ "hover:text-secondary-foreground",
5571
+ "focus:bg-secondary-hover",
5572
+ "focus:text-secondary-foreground",
5573
+ "outline-ring/50",
5574
+ "focus-visible:outline-[calc(var(--spacing)*0.75)]",
5575
+ "focus-visible:outline",
5576
+ "cursor-pointer",
5577
+ "shrink-0",
5578
+ "[&_svg]:shrink-0",
5579
+ "[&_svg:not([class*='size-'])]:size-4",
5580
+ "has-[>svg]:px-2.5"
5581
+ ];
5582
+ function navigation_menu_default(Alpine) {
5583
+ Alpine.directive("h-nav", (el, { original }) => {
5584
+ if (el.tagName !== "NAV") {
5585
+ throw new Error(`${original} must be a nav element`);
5586
+ }
5587
+ if (!el.hasAttribute("aria-label")) {
5588
+ throw new Error(`${original} must have an "aria-label" attribute`);
5589
+ }
5590
+ el.classList.add("relative", "z-10", "flex", "items-center");
5591
+ el.setAttribute("data-slot", "nav");
5592
+ });
5593
+ Alpine.directive("h-nav-list", (el, { original }, { Alpine: Alpine2 }) => {
5594
+ if (el.tagName !== "UL") {
5595
+ throw new Error(`${original} must be a ul element`);
5596
+ }
5597
+ const nav = Alpine2.findClosest(el.parentElement, (parent) => parent.getAttribute("data-slot") === "nav");
5598
+ if (!nav) {
5599
+ throw new Error(`${original} must be inside a ${Alpine2.prefixed("h-nav")} element`);
5600
+ }
5601
+ el.classList.add("flex", "flex-1", "list-none", "items-center", "gap-1");
5602
+ el.setAttribute("data-slot", "nav-list");
5603
+ });
5604
+ Alpine.directive("h-nav-item", (el, { original }) => {
5605
+ if (el.tagName !== "LI") {
5606
+ throw new Error(`${original} must be a li element`);
5607
+ }
5608
+ if (el.parentElement?.getAttribute("data-slot") !== "nav-list") {
5609
+ throw new Error(`${original} must be a direct child of a x-h-nav-list element`);
5610
+ }
5611
+ el.classList.add("relative");
5612
+ el.setAttribute("data-slot", "nav-item");
5613
+ });
5614
+ Alpine.directive("h-nav-trigger", (el, { original }, { cleanup, Alpine: Alpine2 }) => {
5615
+ if (el.tagName !== "BUTTON") {
5616
+ throw new Error(`${original} must be a button element`);
5617
+ }
5618
+ const navItem = Alpine2.findClosest(el.parentElement, (parent) => parent.getAttribute("data-slot") === "nav-item");
5619
+ if (!navItem) {
5620
+ throw new Error(`${original} must be inside a ${Alpine2.prefixed("h-nav-item")} element`);
5621
+ }
5622
+ if (!el.hasAttribute("id")) {
5623
+ el.setAttribute("id", `nnt${uuid_default()}`);
5624
+ }
5625
+ const chevron = createSvg({
5626
+ icon: ChevronDown,
5627
+ classes: "size-4 shrink-0 transition-transform duration-200 motion-reduce:transition-none [[data-state=open]_&]:rotate-180",
5628
+ attrs: { "aria-hidden": "true", role: "presentation" }
5629
+ });
5630
+ el._h_menu_trigger = {
5631
+ isDropdown: true,
5632
+ navItem: true,
5633
+ openMenu: void 0,
5634
+ closeMenu: void 0,
5635
+ setOpen(open) {
5636
+ el.setAttribute("aria-expanded", String(open));
5637
+ el.setAttribute("data-state", open ? "open" : "closed");
5638
+ }
5639
+ };
5640
+ el.setAttribute("aria-haspopup", "menu");
5641
+ el.setAttribute("aria-expanded", "false");
5642
+ el.setAttribute("data-state", "closed");
5643
+ el.classList.add("bg-transparent", "data-[state=open]:bg-secondary-hover", "disabled:opacity-50", "disabled:pointer-events-none", ...navItemTriggerClasses);
5644
+ el.appendChild(chevron);
5645
+ el.setAttribute("data-slot", "nav-trigger");
5646
+ const nav = Alpine2.findClosest(el.parentElement, (p) => p.getAttribute("data-slot") === "nav");
5647
+ let cancelHoverCleanup = null;
5648
+ if (nav?.hasAttribute("data-open-on-hover")) {
5649
+ let scheduleClose = function() {
5650
+ closeTimer = setTimeout(() => {
5651
+ el._h_menu_trigger.closeMenu?.();
5652
+ closeTimer = null;
5653
+ }, 100);
5654
+ }, cancelClose = function() {
5655
+ if (closeTimer) {
5656
+ clearTimeout(closeTimer);
5657
+ closeTimer = null;
5658
+ }
5659
+ }, onNavItemEnter = function() {
5660
+ cancelClose();
5661
+ el._h_menu_trigger.openMenu?.();
5662
+ };
5663
+ el.classList.remove("cursor-pointer");
5664
+ let closeTimer = null;
5665
+ navItem.addEventListener("mouseenter", onNavItemEnter);
5666
+ navItem.addEventListener("mouseleave", scheduleClose);
5667
+ cancelHoverCleanup = () => {
5668
+ cancelClose();
5669
+ navItem.removeEventListener("mouseenter", onNavItemEnter);
5670
+ navItem.removeEventListener("mouseleave", scheduleClose);
5671
+ };
5672
+ }
5673
+ cleanup(() => {
5674
+ if (cancelHoverCleanup) cancelHoverCleanup();
5675
+ if (chevron.parentElement === el) el.removeChild(chevron);
5676
+ });
5677
+ });
5678
+ Alpine.directive("h-nav-link", (el, { original }, { cleanup }) => {
5679
+ if (el.tagName !== "A" && el.tagName !== "BUTTON") {
5680
+ throw new Error(`${original} must be an anchor or button element`);
5681
+ } else if (el.tagName === "BUTTON") {
5682
+ el.setAttribute("type", "button");
5683
+ }
5684
+ el.classList.add(...navItemTriggerClasses, "no-underline", "text-inherit", "data-[active]:bg-secondary-hover", "data-[active]:font-semibold");
5685
+ function syncActive() {
5686
+ if (el.hasAttribute("data-active")) {
5687
+ el.setAttribute("aria-current", "page");
5688
+ } else {
5689
+ el.removeAttribute("aria-current");
5690
+ }
5691
+ }
5692
+ syncActive();
5693
+ const observer = new MutationObserver(syncActive);
5694
+ observer.observe(el, { attributes: true, attributeFilter: ["data-active"] });
5695
+ el.setAttribute("data-slot", "nav-link");
5696
+ cleanup(() => observer.disconnect());
5697
+ });
5698
+ }
5699
+
5542
5700
  // src/utils/breakpoint-listener.js
5543
5701
  function getBreakpointListener(handler, breakpoint = 768, frame = false) {
5544
5702
  let bps = Number.isFinite(breakpoint) ? `${breakpoint}px` : breakpoint;
@@ -11766,7 +11924,7 @@
11766
11924
  }
11767
11925
 
11768
11926
  // package.json
11769
- var version = "1.11.1";
11927
+ var version = "1.12.0";
11770
11928
 
11771
11929
  // src/index.js
11772
11930
  window.Harmonia = { getBreakpointListener, addColorSchemeListener, getColorScheme, removeColorSchemeListener, setColorScheme, getSystemColorScheme, version };
@@ -11791,6 +11949,7 @@
11791
11949
  window.Alpine.plugin(label_default);
11792
11950
  window.Alpine.plugin(list_default);
11793
11951
  window.Alpine.plugin(menu_default);
11952
+ window.Alpine.plugin(navigation_menu_default);
11794
11953
  window.Alpine.plugin(notifications_default);
11795
11954
  window.Alpine.plugin(pagination_default);
11796
11955
  window.Alpine.plugin(popover_default);