@neovici/cosmoz-dropdown 7.4.0 → 7.4.2

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.
@@ -1,4 +1,4 @@
1
- import { component, css, useEffect, useProperty, useRef } from '@pionjs/pion';
1
+ import { component, css, useCallback, useEffect, useProperty, useRef, } from '@pionjs/pion';
2
2
  import { html } from 'lit-html';
3
3
  import { ref } from 'lit-html/directives/ref.js';
4
4
  import { useAutoOpen } from './use-auto-open.js';
@@ -88,43 +88,57 @@ const CosmozDropdownNext = (host) => {
88
88
  const { placement = 'bottom span-right', openOnHover, openOnFocus } = host;
89
89
  const popoverRef = useRef();
90
90
  const [opened, setOpened] = useProperty('opened', false);
91
- const open = () => setOpened(true);
92
- const close = () => setOpened(false);
93
- const toggle = () => setOpened((v) => !v);
94
- // Drive the native popover from the `opened` property
91
+ // Call showPopover/hidePopover synchronously so the browser associates
92
+ // the popover with the current user-gesture. Deferring to a microtask
93
+ // (useEffect) causes light-dismiss to immediately close the popover.
94
+ const open = useCallback(() => {
95
+ setOpened(true);
96
+ popoverRef.current?.showPopover();
97
+ }, []);
98
+ const close = useCallback(() => {
99
+ setOpened(false);
100
+ popoverRef.current?.hidePopover();
101
+ }, []);
102
+ const toggle = useCallback(() => {
103
+ const popover = popoverRef.current;
104
+ if (popover?.matches(':popover-open'))
105
+ close();
106
+ else
107
+ open();
108
+ }, []);
109
+ // Sync native popover when `opened` is set externally via property binding
95
110
  useEffect(() => {
96
111
  const popover = popoverRef.current;
97
112
  if (!popover)
98
113
  return;
99
- if (opened && !popover.matches(':popover-open'))
114
+ if (opened)
100
115
  popover.showPopover();
101
- if (!opened && popover.matches(':popover-open'))
116
+ else
102
117
  popover.hidePopover();
103
118
  }, [opened]);
104
- // Attribute reflection — sync property → attribute for CSS selectors.
105
119
  useEffect(() => {
106
120
  host.toggleAttribute('opened', !!opened);
107
121
  }, [opened]);
108
- useAutoOpen({ host, popoverRef, openOnHover, openOnFocus, open, close });
109
- // When open-on-focus is active, clicking the button should only open
110
- // (not toggle), since focusin already handles opening and toggle would
111
- // race with the focusin handler (focusin opens, then click toggles closed).
122
+ const { scheduleClose, cancelClose } = useAutoOpen({
123
+ host,
124
+ popoverRef,
125
+ openOnHover,
126
+ openOnFocus,
127
+ open,
128
+ close,
129
+ });
130
+ // With open-on-focus, only open (not toggle) on click to avoid racing
131
+ // with the focusin handler
112
132
  const handleClick = openOnFocus ? open : toggle;
113
- const onToggle = (e) => {
133
+ const onToggle = useCallback((e) => {
114
134
  autofocus(e);
115
- // Sync browser-initiated state changes (light-dismiss, Escape)
116
- // back to the property. The useEffect guards against redundant
117
- // showPopover/hidePopover calls.
118
135
  setOpened(e.newState === 'open');
119
- // Re-dispatch as a composed event so parent components across
120
- // shadow boundaries can observe popover state changes.
121
- // The native ToggleEvent is composed: false, bubbles: false.
122
136
  host.dispatchEvent(new ToggleEvent('dropdown-toggle', {
123
137
  newState: e.newState,
124
138
  oldState: e.oldState,
125
139
  composed: true,
126
140
  }));
127
- };
141
+ }, []);
128
142
  return html `
129
143
  <slot name="button" @click=${handleClick}></slot>
130
144
  <div
@@ -132,6 +146,8 @@ const CosmozDropdownNext = (host) => {
132
146
  style="position-area: ${placement}"
133
147
  @toggle=${onToggle}
134
148
  @select=${close}
149
+ @focusout=${scheduleClose}
150
+ @focusin=${cancelClose}
135
151
  ${ref((el) => el && (popoverRef.current = el))}
136
152
  >
137
153
  <slot></slot>
@@ -8,5 +8,8 @@ interface UseAutoOpenOptions {
8
8
  open: () => void;
9
9
  close: () => void;
10
10
  }
11
- export declare const useAutoOpen: ({ host, popoverRef, openOnHover, openOnFocus, open, close, }: UseAutoOpenOptions) => void;
11
+ export declare const useAutoOpen: ({ host, popoverRef, openOnHover, openOnFocus, open, close, }: UseAutoOpenOptions) => {
12
+ scheduleClose: () => void;
13
+ cancelClose: () => void;
14
+ };
12
15
  export {};
@@ -10,8 +10,7 @@ export const useAutoOpen = ({ host, popoverRef, openOnHover, openOnFocus, open,
10
10
  (host.matches(':hover') || popover?.matches(':hover'))) {
11
11
  return;
12
12
  }
13
- if (openOnFocus &&
14
- (host.matches(':focus-within') || popover?.matches(':focus-within'))) {
13
+ if (host.matches(':focus-within') || popover?.matches(':focus-within')) {
15
14
  return;
16
15
  }
17
16
  close();
@@ -45,4 +44,5 @@ export const useAutoOpen = ({ host, popoverRef, openOnHover, openOnFocus, open,
45
44
  host.removeEventListener('focusout', scheduleClose);
46
45
  };
47
46
  }, [openOnFocus, host]);
47
+ return { scheduleClose, cancelClose };
48
48
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-dropdown",
3
- "version": "7.4.0",
3
+ "version": "7.4.2",
4
4
  "description": "A simple dropdown web component",
5
5
  "keywords": [
6
6
  "lit-html",