phoenix_duskmoon 10.0.10 → 10.0.11

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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # [10.0.11](https://github.com/duskmoon-dev/phoenix-duskmoon-ui/compare/v10.0.10...v10.0.11) (2026-10-22)
2
+
3
+ ### Fixed
4
+
5
+ - Synchronize native dropdown triggers' explicit `aria-expanded` state on open,
6
+ Escape, light dismiss and LiveView patches through the existing CSP-safe
7
+ `DuskmoonPopover` hook. Preserve native-only behavior and accessibility when
8
+ the hook is absent, resolving
9
+ [#209](https://github.com/duskmoon-dev/phoenix-duskmoon-ui/issues/209).
10
+
1
11
  # [10.0.10](https://github.com/duskmoon-dev/phoenix-duskmoon-ui/compare/v10.0.9...v10.0.10) (2026-10-22)
2
12
 
3
13
  ### Fixed
@@ -10,12 +10,55 @@ export function syncPopoverState(element) {
10
10
  if (controlledOpen === "false" && open) element.hidePopover();
11
11
  }
12
12
 
13
+ // Dropdown hooks live on the wrapper so patches to either the trigger or panel
14
+ // resynchronize state. Existing popover hooks still mount directly on the panel.
15
+ function hookPopover(element) {
16
+ return element.dataset.popoverId
17
+ ? element.ownerDocument.getElementById(element.dataset.popoverId)
18
+ : element;
19
+ }
20
+
21
+ function syncExpandedState(element, popover) {
22
+ if (element === popover || !("showPopover" in popover)) return;
23
+
24
+ const expanded = String(popover.matches(":popover-open"));
25
+
26
+ for (const trigger of element.querySelectorAll("[aria-controls]")) {
27
+ if (trigger.getAttribute("aria-controls") === popover.id) {
28
+ trigger.setAttribute("aria-expanded", expanded);
29
+ }
30
+ }
31
+ }
32
+
33
+ function syncHookState(hook) {
34
+ const popover = hookPopover(hook.el);
35
+
36
+ if (hook._popover !== popover) {
37
+ hook._popover?.removeEventListener("toggle", hook._onPopoverToggle);
38
+ hook._popover = popover;
39
+ popover?.addEventListener("toggle", hook._onPopoverToggle);
40
+ }
41
+
42
+ if (!popover) return;
43
+
44
+ syncPopoverState(popover);
45
+ syncExpandedState(hook.el, popover);
46
+ }
47
+
13
48
  export const DuskmoonPopover = {
14
49
  mounted() {
15
- syncPopoverState(this.el);
50
+ this._onPopoverToggle = () => {
51
+ if (this._popover) syncExpandedState(this.el, this._popover);
52
+ };
53
+ syncHookState(this);
16
54
  },
17
55
 
18
56
  updated() {
19
- syncPopoverState(this.el);
57
+ syncHookState(this);
58
+ },
59
+
60
+ destroyed() {
61
+ this._popover?.removeEventListener("toggle", this._onPopoverToggle);
62
+ this._popover = null;
20
63
  },
21
64
  };
@@ -48,7 +48,7 @@ export declare const Spotlight: LiveViewHook<HTMLDialogElement>;
48
48
  /** Page-header visibility observer. */
49
49
  export declare const PageHeader: LiveViewHook;
50
50
 
51
- /** Synchronizes an explicitly controlled native popover with LiveView state. */
51
+ /** Synchronizes native dropdown expanded state and optional server-controlled popover visibility. */
52
52
  export declare const DuskmoonPopover: LiveViewHook<HTMLElement>;
53
53
 
54
54
  /** Preserves left-menu collapse, group, and scroll state across LiveView patches. */
@@ -22,7 +22,7 @@
22
22
  * - ThemeSwitcher: Theme toggle with localStorage persistence
23
23
  * - Spotlight: Keyboard shortcut (Cmd/Ctrl+K) for spotlight search
24
24
  * - PageHeader: Intersection observer for scroll-based effects
25
- * - DuskmoonPopover: Optional server-controlled native popover state
25
+ * - DuskmoonPopover: Native dropdown expanded state and optional server-controlled popovers
26
26
  * - DuskmoonLeftMenu: Collapsible sidebar with persistent group and scroll state
27
27
  * - DuskmoonLeftMenuNavigation: Active-page synchronization for sticky left menus
28
28
  * - DuskmoonSplitPane: Accessible client-owned split-pane resizing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phoenix_duskmoon",
3
- "version": "10.0.10",
3
+ "version": "10.0.11",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./priv/static/assets/js/phoenix_duskmoon.js",
@@ -2,5 +2,5 @@
2
2
  "!comment!":"This file was auto-generated by `mix phx.digest`. Remove it and all generated artefacts with `mix phx.digest.clean --all`",
3
3
  "version":1,
4
4
  "latest":{"assets/css/app.css":"assets/css/app-8305fbeb48a74c180d2d40e5e2a038c9.css","assets/css/manifest.json":"assets/css/manifest-771fd8eaae673c76a85c29e7b1106af0.json","assets/js/manifest.json":"assets/js/manifest-b1cb13d0ed943a03c54da6dd727c4ea5.json","assets/js/phoenix_duskmoon.js":"assets/js/phoenix_duskmoon-c22eb67823f82d7b0d27eba5dbd7be12.js"},
5
- "digests":{"assets/css/app-8305fbeb48a74c180d2d40e5e2a038c9.css":{"size":807121,"sha512":"HhFYdMA1G3Em4WqiiVn3PVZtn/ycbYaXBI6s76lgSEOuHqdqHTppaSdvdyjo7cP9GOriidcrRt+fYpbhkH3t5w==","mtime":63959824722,"digest":"8305fbeb48a74c180d2d40e5e2a038c9","logical_path":"assets/css/app.css"},"assets/css/manifest-771fd8eaae673c76a85c29e7b1106af0.json":{"size":100,"sha512":"MNvnqDa/IgJZPGOqVMw/HbfH7xdXVKmJCtAxN2+C/85JfkoFIhxgJ/V+gUJcvHhBnBVTB1M9fU87FJzyAXPhyQ==","mtime":63959824722,"digest":"771fd8eaae673c76a85c29e7b1106af0","logical_path":"assets/css/manifest.json"},"assets/js/manifest-b1cb13d0ed943a03c54da6dd727c4ea5.json":{"size":184,"sha512":"BTfsrl88Ai6yWI57UhqJvF3SqXrV1dN60XQFdeHHv+kTdGuYz5jp6xBcK9STo9jdaW9JPVGAI8BjAImgxkGJdw==","mtime":63959824722,"digest":"b1cb13d0ed943a03c54da6dd727c4ea5","logical_path":"assets/js/manifest.json"},"assets/js/phoenix_duskmoon-c22eb67823f82d7b0d27eba5dbd7be12.js":{"size":3120,"sha512":"ulzAj2MAGVqSYL/QkVOqiAKj4psHe7hN6xx7DScYNIwg3g5AdSlh3onS3b6GxsBvZTolvfG3kqS2J6jRj6uTfg==","mtime":63959824722,"digest":"c22eb67823f82d7b0d27eba5dbd7be12","logical_path":"assets/js/phoenix_duskmoon.js"}}
5
+ "digests":{"assets/css/app-8305fbeb48a74c180d2d40e5e2a038c9.css":{"size":807121,"sha512":"HhFYdMA1G3Em4WqiiVn3PVZtn/ycbYaXBI6s76lgSEOuHqdqHTppaSdvdyjo7cP9GOriidcrRt+fYpbhkH3t5w==","mtime":63959864862,"digest":"8305fbeb48a74c180d2d40e5e2a038c9","logical_path":"assets/css/app.css"},"assets/css/manifest-771fd8eaae673c76a85c29e7b1106af0.json":{"size":100,"sha512":"MNvnqDa/IgJZPGOqVMw/HbfH7xdXVKmJCtAxN2+C/85JfkoFIhxgJ/V+gUJcvHhBnBVTB1M9fU87FJzyAXPhyQ==","mtime":63959864862,"digest":"771fd8eaae673c76a85c29e7b1106af0","logical_path":"assets/css/manifest.json"},"assets/js/manifest-b1cb13d0ed943a03c54da6dd727c4ea5.json":{"size":184,"sha512":"BTfsrl88Ai6yWI57UhqJvF3SqXrV1dN60XQFdeHHv+kTdGuYz5jp6xBcK9STo9jdaW9JPVGAI8BjAImgxkGJdw==","mtime":63959864862,"digest":"b1cb13d0ed943a03c54da6dd727c4ea5","logical_path":"assets/js/manifest.json"},"assets/js/phoenix_duskmoon-c22eb67823f82d7b0d27eba5dbd7be12.js":{"size":3120,"sha512":"ulzAj2MAGVqSYL/QkVOqiAKj4psHe7hN6xx7DScYNIwg3g5AdSlh3onS3b6GxsBvZTolvfG3kqS2J6jRj6uTfg==","mtime":63959864862,"digest":"c22eb67823f82d7b0d27eba5dbd7be12","logical_path":"assets/js/phoenix_duskmoon.js"}}
6
6
  }