From dedc9d4a6619187235cd11b3ce0dbafdccab1618 Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Mon, 13 Jan 2025 12:03:10 +0200 Subject: [PATCH 1/6] refactor(Dialog): allow reusing of observeContentResize modifier --- packages/core/src/components/Dialog/Dialog.tsx | 18 ++---------------- .../modifiers/observeContentResizeModifier.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 packages/core/src/components/Dialog/modifiers/observeContentResizeModifier.ts diff --git a/packages/core/src/components/Dialog/Dialog.tsx b/packages/core/src/components/Dialog/Dialog.tsx index 47b3e92ee8..e91248dde2 100644 --- a/packages/core/src/components/Dialog/Dialog.tsx +++ b/packages/core/src/components/Dialog/Dialog.tsx @@ -19,6 +19,7 @@ import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; import { DialogAnimationType, DialogPosition, DialogTriggerEvent } from "./Dialog.types"; import LayerContext from "../LayerProvider/LayerContext"; import { isClient } from "../../utils/ssr-utils"; +import { createObserveContentResizeModifier } from "./modifiers/observeContentResizeModifier"; export interface DialogProps extends VibeComponentProps { /** @@ -584,22 +585,7 @@ export default class Dialog extends PureComponent { return state; } }, - { - name: "observeContentResize", - enabled: observeContentResize, - phase: "beforeWrite", - fn() {}, - effect({ state, instance }) { - const observer = new ResizeObserver(() => { - instance.update(); - }); - observer.observe(state.elements.popper); - - return () => { - observer.disconnect(); - }; - } - }, + createObserveContentResizeModifier(observeContentResize), ...modifiers ]} > diff --git a/packages/core/src/components/Dialog/modifiers/observeContentResizeModifier.ts b/packages/core/src/components/Dialog/modifiers/observeContentResizeModifier.ts new file mode 100644 index 0000000000..e6d90ff5be --- /dev/null +++ b/packages/core/src/components/Dialog/modifiers/observeContentResizeModifier.ts @@ -0,0 +1,18 @@ +import { Modifier } from "react-popper"; + +export const createObserveContentResizeModifier = (isEnabled = false): Modifier<"observeContentResize"> => ({ + name: "observeContentResize", + enabled: isEnabled, + phase: "beforeWrite", + fn() {}, + effect({ state, instance }) { + const observer = new ResizeObserver(() => { + instance.update(); + }); + observer.observe(state.elements.popper); + + return () => { + observer.disconnect(); + }; + } +}); From 22f8dd85ee6a1d8599dda42be0f5d3b68b789a08 Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Mon, 13 Jan 2025 12:04:10 +0200 Subject: [PATCH 2/6] feat(MenuItem): allow observing submenu content resize when a re-render isn't triggered --- .../core/src/components/Menu/MenuItem/MenuItem.tsx | 8 ++++++++ .../components/MenuItemSubMenu/MenuItemSubMenu.tsx | 6 ++++-- .../MenuItemSubMenu/MenuItemSubMenu.types.ts | 3 ++- packages/core/src/hooks/usePopover.ts | 10 +++++++--- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/core/src/components/Menu/MenuItem/MenuItem.tsx b/packages/core/src/components/Menu/MenuItem/MenuItem.tsx index 7fdc05cc7f..08eeec8418 100644 --- a/packages/core/src/components/Menu/MenuItem/MenuItem.tsx +++ b/packages/core/src/components/Menu/MenuItem/MenuItem.tsx @@ -56,6 +56,14 @@ export interface MenuItemProps extends VibeComponentProps { splitMenuItem?: boolean; "aria-label"?: AriaAttributes["aria-label"]; submenuPosition?: SubmenuPosition; + /** + * Enables the observation of content resize for the menu item's submenu. + * When set to `true`, a ResizeObserver is attached to the popper content, + * automatically triggering repositioning when the size of the submenu's content changes. + * + * This is useful for when submenu's content may grow or shrink without a re-render being triggered. + */ + observeSubMenuContentResize?: boolean; } export interface MenuItemTitleComponentProps extends Omit { diff --git a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx index abfd1859ce..da240a6436 100644 --- a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx +++ b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx @@ -12,7 +12,8 @@ const MenuItemSubMenu = ({ autoFocusOnMount, onClose, children, - submenuPosition + submenuPosition, + observeSubMenuContentResize }: MenuItemSubMenuProps) => { const childRef = useRef(null); const popperElementRef = useRef(null); @@ -36,7 +37,8 @@ const MenuItemSubMenu = ({ popperElementRef?.current, { isOpen: open, - placement: submenuPlacement + placement: submenuPlacement, + observeContentResize: observeSubMenuContentResize } ); diff --git a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts index 8eac7274de..855059b843 100644 --- a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts +++ b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts @@ -1,8 +1,9 @@ import React from "react"; import { CloseMenuOption, MenuChild } from "../../../Menu/MenuConstants"; import { SubmenuPosition } from "../../MenuItem.types"; +import { MenuItemProps } from "../../MenuItem"; -export interface MenuItemSubMenuProps { +export interface MenuItemSubMenuProps extends Pick { /** * Reference to the anchor element that the submenu is related to. This is used to position the submenu correctly relative to the parent menu item. */ diff --git a/packages/core/src/hooks/usePopover.ts b/packages/core/src/hooks/usePopover.ts index 6cf3159f50..10e36606c6 100644 --- a/packages/core/src/hooks/usePopover.ts +++ b/packages/core/src/hooks/usePopover.ts @@ -4,6 +4,7 @@ import { Placement } from "./popoverConstants"; import useIsomorphicLayoutEffect from "./ssr/useIsomorphicLayoutEffect"; import useForceUpdate from "./useForceUpdate"; import type { Options, State } from "@popperjs/core"; +import { createObserveContentResizeModifier } from "../components/Dialog/modifiers/observeContentResizeModifier"; const { RIGHT_START, RIGHT_END, LEFT_START, LEFT_END } = Placement; @@ -19,10 +20,12 @@ export default function usePopover( popperElement: HTMLElement, { isOpen, - placement = RIGHT_START + placement = RIGHT_START, + observeContentResize }: { isOpen?: boolean; placement?: Placement; + observeContentResize?: boolean; } ) { const forceUpdate = useForceUpdate(); @@ -46,10 +49,11 @@ export default function usePopover( state.styles.popper.visibility = isOpen ? "visible" : "hidden"; return state; } - } + }, + createObserveContentResizeModifier(observeContentResize) ] }; - }, [isOpen, placement]); + }, [isOpen, placement, observeContentResize]); const { styles, attributes } = usePopper(referenceElement, popperElement, popperOptions); From 02eba3d443fa52511f878514676d7cb0b1fc1303 Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Mon, 13 Jan 2025 12:13:27 +0200 Subject: [PATCH 3/6] trigger [prerelease] From ab569606b4d760868835fc9405101601c7551735 Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Wed, 15 Jan 2025 11:50:12 +0200 Subject: [PATCH 4/6] refactor(MenuItem): rename prop --- packages/core/src/components/Menu/MenuItem/MenuItem.tsx | 6 ++---- .../MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx | 4 ++-- .../components/MenuItemSubMenu/MenuItemSubMenu.types.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/core/src/components/Menu/MenuItem/MenuItem.tsx b/packages/core/src/components/Menu/MenuItem/MenuItem.tsx index 08eeec8418..280cfd1520 100644 --- a/packages/core/src/components/Menu/MenuItem/MenuItem.tsx +++ b/packages/core/src/components/Menu/MenuItem/MenuItem.tsx @@ -57,13 +57,11 @@ export interface MenuItemProps extends VibeComponentProps { "aria-label"?: AriaAttributes["aria-label"]; submenuPosition?: SubmenuPosition; /** - * Enables the observation of content resize for the menu item's submenu. - * When set to `true`, a ResizeObserver is attached to the popper content, - * automatically triggering repositioning when the size of the submenu's content changes. + * When set to `true`, submenu's content and size changes would automatically trigger repositioning. * * This is useful for when submenu's content may grow or shrink without a re-render being triggered. */ - observeSubMenuContentResize?: boolean; + autoAdjustOnSubMenuContentResize?: boolean; } export interface MenuItemTitleComponentProps extends Omit { diff --git a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx index da240a6436..a58fae2fb5 100644 --- a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx +++ b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.tsx @@ -13,7 +13,7 @@ const MenuItemSubMenu = ({ onClose, children, submenuPosition, - observeSubMenuContentResize + autoAdjustOnSubMenuContentResize }: MenuItemSubMenuProps) => { const childRef = useRef(null); const popperElementRef = useRef(null); @@ -38,7 +38,7 @@ const MenuItemSubMenu = ({ { isOpen: open, placement: submenuPlacement, - observeContentResize: observeSubMenuContentResize + observeContentResize: autoAdjustOnSubMenuContentResize } ); diff --git a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts index 855059b843..7c6cc29ad7 100644 --- a/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts +++ b/packages/core/src/components/Menu/MenuItem/components/MenuItemSubMenu/MenuItemSubMenu.types.ts @@ -3,7 +3,7 @@ import { CloseMenuOption, MenuChild } from "../../../Menu/MenuConstants"; import { SubmenuPosition } from "../../MenuItem.types"; import { MenuItemProps } from "../../MenuItem"; -export interface MenuItemSubMenuProps extends Pick { +export interface MenuItemSubMenuProps extends Pick { /** * Reference to the anchor element that the submenu is related to. This is used to position the submenu correctly relative to the parent menu item. */ From 354a426e6e7b7ead322d95695e70da722250ce8c Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Mon, 27 Jan 2025 15:28:24 +0200 Subject: [PATCH 5/6] fix(MenuItem): pass autoAdjustOnSubMenuContentResize prop to SubMenu internal component --- .../Menu/MenuItem/components/BaseMenuItem/BaseMenuItem.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/components/Menu/MenuItem/components/BaseMenuItem/BaseMenuItem.tsx b/packages/core/src/components/Menu/MenuItem/components/BaseMenuItem/BaseMenuItem.tsx index 9949310da3..ef9703b27d 100644 --- a/packages/core/src/components/Menu/MenuItem/components/BaseMenuItem/BaseMenuItem.tsx +++ b/packages/core/src/components/Menu/MenuItem/components/BaseMenuItem/BaseMenuItem.tsx @@ -39,7 +39,8 @@ const BaseMenuItem = forwardRef( "data-testid": dataTestId, splitMenuItem = false, children, - submenuPosition = "right" + submenuPosition = "right", + autoAdjustOnSubMenuContentResize }: BaseMenuItemProps, ref: React.ForwardedRef ) => { @@ -153,6 +154,7 @@ const BaseMenuItem = forwardRef( onClose={closeSubMenu} autoFocusOnMount={!useDocumentEventListeners} submenuPosition={submenuPosition} + autoAdjustOnSubMenuContentResize={autoAdjustOnSubMenuContentResize} > {subMenu} From 5e4f6c6941c6a89bedd8e3bdf3d232625eae769f Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Mon, 27 Jan 2025 15:28:35 +0200 Subject: [PATCH 6/6] trigger [prerelease]