@react-hive/honey-layout 18.0.0 → 18.2.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.
@@ -5026,7 +5026,7 @@ __webpack_require__.r(__webpack_exports__);
5026
5026
 
5027
5027
 
5028
5028
 
5029
- const HoneyPopupContent = ({ children, referenceProps, content, contentProps, focusManagerProps, arrowProps, portalProps, adjustStyles, context, ...popupOptions }) => {
5029
+ const HoneyPopupContent = ({ children, referenceUserProps, referenceProps, content, contentProps, focusManagerProps, arrowProps, portalProps, adjustStyles, context, ...popupOptions }) => {
5030
5030
  const { useArrow, onClose } = popupOptions;
5031
5031
  const onCloseRef = (0,_react_hive_honey_hooks__WEBPACK_IMPORTED_MODULE_4__.useHoneyLatest)(onClose);
5032
5032
  const { nodeId, floating, isOpen, arrowRef, interactions, transition } = (0,_hooks__WEBPACK_IMPORTED_MODULE_8__.useHoneyPopup)(popupOptions);
@@ -5037,9 +5037,7 @@ const HoneyPopupContent = ({ children, referenceProps, content, contentProps, fo
5037
5037
  context,
5038
5038
  floatingContext: floating.context,
5039
5039
  }), [context, floating.context]);
5040
- return ((0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_HoneyPopupStyled__WEBPACK_IMPORTED_MODULE_6__.HoneyPopupStyled, { ref: floating.refs.setReference, ...interactions.getReferenceProps({
5041
- onClick: e => e.stopPropagation(),
5042
- }), ...referenceProps, "data-testid": "honey-popup", children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components__WEBPACK_IMPORTED_MODULE_5__.HoneyPopupContext, { value: popupContext, children: [(0,_react_hive_honey_utils__WEBPACK_IMPORTED_MODULE_3__.invokeIfFunction)(children, popupContext), (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_floating_ui_react__WEBPACK_IMPORTED_MODULE_2__.FloatingNode, { id: nodeId, children: transition.isMounted && ((0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_HoneyPopupPortal__WEBPACK_IMPORTED_MODULE_7__.HoneyPopupPortal, { ...portalProps, children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_floating_ui_react__WEBPACK_IMPORTED_MODULE_2__.FloatingFocusManager, { context: floating.context, disabled: !isOpen, ...focusManagerProps, children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components__WEBPACK_IMPORTED_MODULE_5__.HoneyOverlay, { ref: floating.refs.setFloating, active: isOpen, style: adjustStyles?.({
5040
+ return ((0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_HoneyPopupStyled__WEBPACK_IMPORTED_MODULE_6__.HoneyPopupStyled, { ref: floating.refs.setReference, ...interactions.getReferenceProps(referenceUserProps), ...referenceProps, "data-testid": "honey-popup", children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components__WEBPACK_IMPORTED_MODULE_5__.HoneyPopupContext, { value: popupContext, children: [(0,_react_hive_honey_utils__WEBPACK_IMPORTED_MODULE_3__.invokeIfFunction)(children, popupContext), (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_floating_ui_react__WEBPACK_IMPORTED_MODULE_2__.FloatingNode, { id: nodeId, children: transition.isMounted && ((0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_HoneyPopupPortal__WEBPACK_IMPORTED_MODULE_7__.HoneyPopupPortal, { ...portalProps, children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_floating_ui_react__WEBPACK_IMPORTED_MODULE_2__.FloatingFocusManager, { context: floating.context, disabled: !isOpen, ...focusManagerProps, children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components__WEBPACK_IMPORTED_MODULE_5__.HoneyOverlay, { ref: floating.refs.setFloating, active: isOpen, style: adjustStyles?.({
5043
5041
  ...floating.floatingStyles,
5044
5042
  ...transition.styles,
5045
5043
  }, {
@@ -6022,41 +6020,49 @@ __webpack_require__.r(__webpack_exports__);
6022
6020
  /* harmony import */ var _hooks__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../hooks */ "./src/hooks/index.ts");
6023
6021
 
6024
6022
 
6023
+ const subscribeToNothing = () => () => undefined;
6025
6024
  /**
6026
- * Returns an active overlay by ID and optionally attaches overlay event listeners.
6025
+ * Returns an active overlay by ID and optionally attaches a keyup event listener.
6027
6026
  *
6028
- * This hook looks up an overlay registered in `HoneyLayoutProvider` by its ID.
6029
- * It can also subscribe to overlay-level events, such as `keyup`, for the matched overlay.
6027
+ * The hook reads the overlay through the external overlay store exposed by
6028
+ * `HoneyLayoutProvider`. It subscribes to stack changes with `useSyncExternalStore`, but
6029
+ * selects only the requested overlay. Consequently, unrelated overlay stack changes do not
6030
+ * re-render the consuming component when the selected overlay instance remains unchanged.
6030
6031
  *
6031
6032
  * @param targetOverlayId - The ID of the active overlay to find.
6032
- * @param options - Optional configuration for resolving the overlay and attaching event handlers.
6033
+ * @param options - Optional configuration for resolving the overlay and handling keyup events.
6033
6034
  *
6034
- * @returns The matching active overlay instance, or `null` when the hook is disabled
6035
- * or the overlay is not currently registered.
6035
+ * @returns The matching active overlay, or `null` when the hook is disabled or the overlay is
6036
+ * not currently registered.
6036
6037
  *
6037
6038
  * @remarks
6038
- * - The hook only works with overlays that are currently active.
6039
- * - If `enabled` is `false`, the hook returns `null` and does not attach listeners.
6040
- * - If the overlay is inactive, unregistered, or already removed from the stack, `null` is returned.
6041
- * - The `onKeyUp` listener is attached only to the matched overlay while the hook is enabled.
6042
- * - Event listeners are automatically cleaned up when the overlay, handler, enabled state,
6043
- * or component lifecycle changes.
6039
+ * - The hook resolves only overlays that are currently registered.
6040
+ * - If `enabled` is `false`, the snapshot is `null` and no keyup listener is attached.
6041
+ * - If the target overlay is registered or unregistered, the consuming component is updated.
6042
+ * - Changes to unrelated overlays do not cause a re-render when the selected snapshot is equal.
6043
+ * - The `onKeyUp` listener is automatically cleaned up when its dependencies change.
6044
6044
  *
6045
6045
  * @example
6046
6046
  * ```tsx
6047
6047
  * const overlay = useHoneyOverlay('my-overlay-id', {
6048
6048
  * enabled: isOpen,
6049
- * onKeyUp: (keyCode, overlay, e) => {
6049
+ * onKeyUp: keyCode => {
6050
6050
  * if (keyCode === 'Escape') {
6051
- * // Handle Escape key.
6051
+ * closeOverlay();
6052
6052
  * }
6053
6053
  * },
6054
6054
  * });
6055
6055
  * ```
6056
6056
  */
6057
6057
  const useHoneyOverlay = (targetOverlayId, { enabled = true, onKeyUp } = {}) => {
6058
- const { overlays } = (0,_hooks__WEBPACK_IMPORTED_MODULE_1__.useHoneyLayout)();
6059
- const overlay = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => (enabled ? (overlays.find(overlay => overlay.id === targetOverlayId) ?? null) : null), [enabled, overlays, targetOverlayId]);
6058
+ const { getOverlaysSnapshot, subscribeOverlays } = (0,_hooks__WEBPACK_IMPORTED_MODULE_1__.useHoneyLayout)();
6059
+ const getOverlaySnapshot = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
6060
+ if (!enabled) {
6061
+ return null;
6062
+ }
6063
+ return getOverlaysSnapshot().find(overlay => overlay.id === targetOverlayId) ?? null;
6064
+ }, [enabled, targetOverlayId]);
6065
+ const overlay = (0,react__WEBPACK_IMPORTED_MODULE_0__.useSyncExternalStore)(enabled ? subscribeOverlays : subscribeToNothing, getOverlaySnapshot, getOverlaySnapshot);
6060
6066
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
6061
6067
  if (!overlay || !onKeyUp) {
6062
6068
  return;
@@ -6141,16 +6147,39 @@ __webpack_require__.r(__webpack_exports__);
6141
6147
 
6142
6148
 
6143
6149
 
6150
+ /**
6151
+ * Provides Honey styling, responsive screen state, and overlay management to its descendants.
6152
+ *
6153
+ * Overlay state is held in a ref-backed external store. Registering or unregistering an overlay
6154
+ * notifies subscribed overlay consumers without updating this provider's React state or context
6155
+ * value. This prevents overlay stack changes from re-rendering the full layout subtree.
6156
+ *
6157
+ * The context value changes only when the theme or responsive screen state changes. Overlay
6158
+ * consumers should read the store through `useHoneyOverlay` or `useSyncExternalStore` rather
6159
+ * than attempting to read the overlay ref directly.
6160
+ *
6161
+ * @param props - The provider props, including the theme, children, style-provider options,
6162
+ * and optional media-query configuration.
6163
+ * @returns The configured Honey style and layout providers.
6164
+ *
6165
+ * @example
6166
+ * ```tsx
6167
+ * <HoneyLayoutProvider theme={theme} mediaQueryOptions={mediaQueryOptions}>
6168
+ * <App />
6169
+ * </HoneyLayoutProvider>
6170
+ * ```
6171
+ */
6144
6172
  const HoneyLayoutProvider = ({ children, theme, mediaQueryOptions, ...props }) => {
6145
6173
  const screenState = (0,_hooks__WEBPACK_IMPORTED_MODULE_3__.useHoneyMediaQuery)(theme, mediaQueryOptions);
6146
- const { overlays, registerOverlay, unregisterOverlay } = (0,_hooks__WEBPACK_IMPORTED_MODULE_5__.useHoneyOverlays)();
6174
+ const { getOverlaysSnapshot, registerOverlay, subscribeOverlays, unregisterOverlay } = (0,_hooks__WEBPACK_IMPORTED_MODULE_5__.useHoneyOverlays)();
6147
6175
  const contextValue = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => ({
6148
6176
  theme,
6149
6177
  screenState,
6150
- overlays,
6178
+ getOverlaysSnapshot,
6151
6179
  registerOverlay,
6180
+ subscribeOverlays,
6152
6181
  unregisterOverlay,
6153
- }), [theme, screenState, overlays]);
6182
+ }), [theme, screenState]);
6154
6183
  return ((0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_react_hive_honey_style__WEBPACK_IMPORTED_MODULE_2__.HoneyStyleProvider, { theme: theme, ...props, children: (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_contexts__WEBPACK_IMPORTED_MODULE_4__.HoneyLayoutContext, { value: contextValue, children: children }) }));
6155
6184
  };
6156
6185
 
@@ -6232,17 +6261,51 @@ __webpack_require__.r(__webpack_exports__);
6232
6261
 
6233
6262
 
6234
6263
  /**
6235
- * Manages the active overlay stack and global keyboard event dispatching.
6264
+ * Manages the active overlay stack and dispatches global keyboard events.
6236
6265
  *
6237
- * The hook keeps registered overlays in stack order, where the latest registered overlay
6238
- * is treated as the top-level overlay. Keyboard events are forwarded only to the top-level
6239
- * overlay, allowing nested or overlapping overlays to handle key interactions predictably.
6266
+ * Registered overlays are stored in a ref so adding or removing an overlay does not re-render
6267
+ * the component that owns this hook. The hook exposes a snapshot getter and subscription
6268
+ * function that consumers can use with `useSyncExternalStore` to react to stack changes.
6240
6269
  *
6241
- * @returns An object containing the active overlays stack and helper methods for registering
6242
- * and unregistering overlays.
6270
+ * Overlays are kept in stack order. The most recently registered overlay is treated as the
6271
+ * top-level overlay and is the only overlay that receives global keyboard events.
6272
+ *
6273
+ * @returns A stable overlay store containing methods for reading and subscribing to the stack,
6274
+ * together with helpers for registering and unregistering overlays.
6243
6275
  */
6244
6276
  const useHoneyOverlays = () => {
6245
6277
  const overlaysRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)([]);
6278
+ const subscribersRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(new Set());
6279
+ /**
6280
+ * Returns the current overlay stack snapshot.
6281
+ *
6282
+ * The snapshot keeps the same array identity until the stack changes, making this getter
6283
+ * compatible with `useSyncExternalStore`.
6284
+ *
6285
+ * @returns The current active overlays in registration order.
6286
+ */
6287
+ const getOverlaysSnapshot = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => overlaysRef.current, []);
6288
+ /**
6289
+ * Subscribes to overlay stack changes.
6290
+ *
6291
+ * The subscriber is notified after an overlay is registered or successfully unregistered.
6292
+ * Subscribing does not itself cause the component that owns this hook to re-render.
6293
+ *
6294
+ * @param subscriber - Callback invoked whenever the overlay stack snapshot changes.
6295
+ * @returns A cleanup function that removes the subscriber.
6296
+ */
6297
+ const subscribeOverlays = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)((subscriber) => {
6298
+ subscribersRef.current.add(subscriber);
6299
+ return () => {
6300
+ subscribersRef.current.delete(subscriber);
6301
+ };
6302
+ }, []);
6303
+ /**
6304
+ * Notifies every overlay stack subscriber that a new snapshot is available.
6305
+ */
6306
+ const notifyOverlaySubscribers = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
6307
+ subscribersRef.current.forEach(subscriber => subscriber());
6308
+ }, []);
6246
6309
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
6247
6310
  /**
6248
6311
  * Handles global keyup events and forwards them to the top-level overlay.
@@ -6273,6 +6336,9 @@ const useHoneyOverlays = () => {
6273
6336
  * subscribing to overlay events, removing event listeners, and notifying registered
6274
6337
  * listeners when matching events occur.
6275
6338
  *
6339
+ * Registering creates a new stack snapshot and notifies overlay stack subscribers without
6340
+ * re-rendering the component that owns this hook.
6341
+ *
6276
6342
  * @param overlayConfig - The overlay configuration, including an optional ID, optional keyup
6277
6343
  * handler, and optional list of keyboard codes the overlay should listen to.
6278
6344
  *
@@ -6348,23 +6414,30 @@ const useHoneyOverlays = () => {
6348
6414
  },
6349
6415
  };
6350
6416
  overlaysRef.current = [...overlaysRef.current, overlay];
6417
+ notifyOverlaySubscribers();
6351
6418
  return overlay;
6352
6419
  }, []);
6353
6420
  /**
6354
6421
  * Unregisters an overlay by ID and removes it from the overlay stack.
6355
6422
  *
6356
6423
  * This should usually be called when an overlay is deactivated or unmounted.
6424
+ * Subscribers are notified only when an overlay with the supplied ID was present.
6357
6425
  *
6358
6426
  * @param targetOverlayId - The ID of the overlay to remove.
6359
6427
  */
6360
6428
  const unregisterOverlay = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(targetOverlayId => {
6361
- overlaysRef.current = overlaysRef.current.filter(overlay => overlay.id !== targetOverlayId);
6429
+ const nextOverlays = overlaysRef.current.filter(overlay => overlay.id !== targetOverlayId);
6430
+ if (nextOverlays.length !== overlaysRef.current.length) {
6431
+ overlaysRef.current = nextOverlays;
6432
+ notifyOverlaySubscribers();
6433
+ }
6362
6434
  }, []);
6363
- return {
6364
- overlays: overlaysRef.current,
6435
+ return (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => ({
6436
+ getOverlaysSnapshot,
6365
6437
  registerOverlay,
6438
+ subscribeOverlays,
6366
6439
  unregisterOverlay,
6367
- };
6440
+ }), []);
6368
6441
  };
6369
6442
 
6370
6443