@lumx/react 4.25.0 → 4.25.1-next.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/index.d.ts CHANGED
@@ -1898,6 +1898,12 @@ interface ComboboxEventMap {
1898
1898
  * Used to control the loading message text in the live region (ComboboxState).
1899
1899
  */
1900
1900
  loadingAnnouncement: boolean;
1901
+ /**
1902
+ * Fired only when this option becomes the active descendant (true)
1903
+ * or stops being the active descendant (false).
1904
+ * Build the key with optionActiveEvent(optionId).
1905
+ */
1906
+ [event: OptionActiveEvent]: boolean;
1901
1907
  }
1902
1908
  /** Callbacks provided by the consumer (React/Vue) to react to combobox state changes. */
1903
1909
  interface ComboboxCallbacks {
package/index.js CHANGED
@@ -6514,6 +6514,11 @@ function setupListbox(handle, signal, notify, options) {
6514
6514
  return focusNav;
6515
6515
  }
6516
6516
 
6517
+ const DEFAULT_COMBOBOX_POPOVER_MAX_HEIGHT = '80vh';
6518
+ const OPTION_ACTIVE_EVENT_PREFIX = 'optionActive:';
6519
+ const optionActiveEvent = optionId => `${OPTION_ACTIVE_EVENT_PREFIX}${optionId}`;
6520
+ const isOptionActiveEvent = event => event.startsWith(OPTION_ACTIVE_EVENT_PREFIX);
6521
+
6517
6522
  /** Options for configuring the shared combobox behavior. */
6518
6523
 
6519
6524
  /**
@@ -6574,6 +6579,9 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
6574
6579
  loadingAnnouncement: new Set()
6575
6580
  };
6576
6581
 
6582
+ /** Per-option subscribers, keyed by optionActive:<id>. Sets are created on demand. */
6583
+ const optionActiveSubscribers = new Map();
6584
+
6577
6585
  /**
6578
6586
  * Last value dispatched for each event. Kept in sync so pull-based subscribers — React's
6579
6587
  * `useSyncExternalStore` via {@link ComboboxHandle.getSnapshot} — can read the current value
@@ -6585,8 +6593,15 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
6585
6593
 
6586
6594
  /** Notify all subscribers for a given event. */
6587
6595
  function notify(event, value) {
6596
+ const previous = latestValues.activeDescendantChange ?? null;
6588
6597
  latestValues[event] = value;
6589
6598
  subscribers[event].forEach(cb => cb(value));
6599
+
6600
+ // Fan out to the 2 affected options only: first the previous one, then the new one.
6601
+ if (event === 'activeDescendantChange' && previous !== value) {
6602
+ if (previous) optionActiveSubscribers.get(optionActiveEvent(previous))?.forEach(cb => cb(false));
6603
+ if (value) optionActiveSubscribers.get(optionActiveEvent(value))?.forEach(cb => cb(true));
6604
+ }
6590
6605
  }
6591
6606
 
6592
6607
  /** Count visible (non-filtered) options. */
@@ -7003,7 +7018,20 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
7003
7018
  };
7004
7019
  },
7005
7020
  subscribe(event, callback) {
7006
- subscribers[event].add(callback);
7021
+ if (isOptionActiveEvent(event)) {
7022
+ const optionCallback = callback;
7023
+ const set = optionActiveSubscribers.get(event) ?? new Set();
7024
+ optionActiveSubscribers.set(event, set);
7025
+ set.add(optionCallback);
7026
+ // Replay (same as open): Vue subscribes after mount and can miss the activation.
7027
+ if (latestValues.activeDescendantChange === event.slice(OPTION_ACTIVE_EVENT_PREFIX.length)) optionCallback(true);
7028
+ return () => {
7029
+ set.delete(optionCallback);
7030
+ if (!set.size) optionActiveSubscribers.delete(event);
7031
+ };
7032
+ }
7033
+ const subs = subscribers[event];
7034
+ subs.add(callback);
7007
7035
  // Replay current loading state to late subscribers so that framework wrappers
7008
7036
  // that subscribe after initial mount (e.g. async Vue watchers) don't miss the
7009
7037
  // initial events fired during mount.
@@ -7016,10 +7044,14 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
7016
7044
 
7017
7045
  // Cleanup function
7018
7046
  return () => {
7019
- subscribers[event].delete(callback);
7047
+ subs.delete(callback);
7020
7048
  };
7021
7049
  },
7022
7050
  getSnapshot(event) {
7051
+ if (isOptionActiveEvent(event)) {
7052
+ // Derived value. No per-id value is stored.
7053
+ return latestValues.activeDescendantChange === event.slice(OPTION_ACTIVE_EVENT_PREFIX.length);
7054
+ }
7023
7055
  return latestValues[event];
7024
7056
  },
7025
7057
  destroy() {
@@ -7039,6 +7071,7 @@ function setupCombobox(callbacks, options, onTriggerAttach) {
7039
7071
  skeletonCount = 0;
7040
7072
  clearTimeout(loadingTimer);
7041
7073
  announcementSent = false;
7074
+ optionActiveSubscribers.clear();
7042
7075
  // Clear all subscribers
7043
7076
  for (const set of Object.values(subscribers)) {
7044
7077
  set.clear();
@@ -9022,6 +9055,9 @@ const ComboboxOption = forwardRef((props, ref) => {
9022
9055
  const internalRef = useRef(null);
9023
9056
  const mergedRef = useMergeRefs(ref, internalRef);
9024
9057
  const [isFiltered, setIsFiltered] = useState(false);
9058
+
9059
+ // Force the tooltip open while the option is the active descendant (keyboard highlight).
9060
+ const isActive = useComboboxEvent(optionActiveEvent(optionId), false);
9025
9061
  useEffect(() => {
9026
9062
  const element = internalRef.current;
9027
9063
  if (!element || !handle) return undefined;
@@ -9060,7 +9096,10 @@ const ComboboxOption = forwardRef((props, ref) => {
9060
9096
  handleClick: onClick,
9061
9097
  id: optionId,
9062
9098
  descriptionId,
9063
- tooltipProps
9099
+ tooltipProps: tooltipProps && {
9100
+ ...tooltipProps,
9101
+ forceOpen: tooltipProps.forceOpen || isActive
9102
+ }
9064
9103
  }, {
9065
9104
  Tooltip
9066
9105
  });
@@ -10251,8 +10290,6 @@ const ComboboxOptionSkeleton = props => {
10251
10290
  ComboboxOptionSkeleton.displayName = COMPONENT_NAME$1c;
10252
10291
  ComboboxOptionSkeleton.className = CLASSNAME$19;
10253
10292
 
10254
- const DEFAULT_COMBOBOX_POPOVER_MAX_HEIGHT = '80vh';
10255
-
10256
10293
  const COMPONENT_NAME$1b = 'ComboboxPopover';
10257
10294
 
10258
10295
  /**