From e8b7edf57dc8476dd0275b5a2e752a8bfedb44d5 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 21 Apr 2023 13:45:13 +0200 Subject: [PATCH 1/2] add demo mode to `Menu` and `Popover` --- .../src/components/menu/menu.tsx | 24 +++++++++-- .../src/components/popover/popover.tsx | 41 ++++++++++++++----- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index 00077db0c..0ed50af19 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -69,6 +69,7 @@ type MenuItemDataRef = MutableRefObject<{ }> interface StateDefinition { + __demoMode: boolean menuState: MenuStates buttonRef: MutableRefObject itemsRef: MutableRefObject @@ -141,7 +142,12 @@ let reducers: { }, [ActionTypes.OpenMenu](state) { if (state.menuState === MenuStates.Open) return state - return { ...state, menuState: MenuStates.Open } + return { + ...state, + /* We can turn off demo mode once we re-open the `Menu` */ + __demoMode: false, + menuState: MenuStates.Open, + } }, [ActionTypes.GoToItem]: (state, action) => { let adjustedState = adjustOrderedState(state) @@ -238,14 +244,23 @@ interface MenuRenderPropArg { close: () => void } -export type MenuProps = Props +export type MenuProps = Props< + TTag, + MenuRenderPropArg, + never, + { + __demoMode?: boolean + } +> function MenuFn( props: MenuProps, ref: Ref ) { + let { __demoMode = false, ...theirProps } = props let reducerBag = useReducer(stateReducer, { - menuState: MenuStates.Closed, + __demoMode, + menuState: __demoMode ? MenuStates.Open : MenuStates.Closed, buttonRef: createRef(), itemsRef: createRef(), items: [], @@ -279,7 +294,6 @@ function MenuFn( [menuState, close] ) - let theirProps = props let ourProps = { ref: menuRef } return ( @@ -604,6 +618,7 @@ function ItemFn( let itemRef = useSyncRefs(ref, internalItemRef) useIsoMorphicEffect(() => { + if (state.__demoMode) return if (state.menuState !== MenuStates.Open) return if (!active) return if (state.activationTrigger === ActivationTrigger.Pointer) return @@ -613,6 +628,7 @@ function ItemFn( }) return d.dispose }, [ + state.__demoMode, internalItemRef, active, state.menuState, diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index f626789e2..b9b3e589f 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -63,6 +63,7 @@ enum PopoverStates { } interface StateDefinition { + __demoMode: boolean popoverState: PopoverStates buttons: MutableRefObject @@ -100,13 +101,22 @@ let reducers: { action: Extract ) => StateDefinition } = { - [ActionTypes.TogglePopover]: (state) => ({ - ...state, - popoverState: match(state.popoverState, { - [PopoverStates.Open]: PopoverStates.Closed, - [PopoverStates.Closed]: PopoverStates.Open, - }), - }), + [ActionTypes.TogglePopover]: (state) => { + let nextState = { + ...state, + popoverState: match(state.popoverState, { + [PopoverStates.Open]: PopoverStates.Closed, + [PopoverStates.Closed]: PopoverStates.Open, + }), + } + + /* We can turn off demo mode once we re-open the `Popover` */ + if (nextState.popoverState === PopoverStates.Open) { + nextState.__demoMode = false + } + + return nextState + }, [ActionTypes.ClosePopover](state) { if (state.popoverState === PopoverStates.Closed) return state return { ...state, popoverState: PopoverStates.Closed } @@ -198,12 +208,20 @@ interface PopoverRenderPropArg { ): void } -export type PopoverProps = Props +export type PopoverProps = Props< + TTag, + PopoverRenderPropArg, + never, + { + __demoMode?: boolean + } +> function PopoverFn( props: PopoverProps, ref: Ref ) { + let { __demoMode = false, ...theirProps } = props let internalPopoverRef = useRef(null) let popoverRef = useSyncRefs( ref, @@ -214,7 +232,8 @@ function PopoverFn( let buttons = useRef([]) let reducerBag = useReducer(stateReducer, { - popoverState: PopoverStates.Closed, + __demoMode, + popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed, buttons, button: null, buttonId: null, @@ -354,7 +373,6 @@ function PopoverFn( [popoverState, close] ) - let theirProps = props let ourProps = { ref: popoverRef } return ( @@ -771,6 +789,7 @@ function PanelFn( // Move focus within panel useEffect(() => { + if (state.__demoMode) return if (!focus) return if (state.popoverState !== PopoverStates.Open) return if (!internalPanelRef.current) return @@ -779,7 +798,7 @@ function PanelFn( if (internalPanelRef.current.contains(activeElement)) return // Already focused within Dialog focusIn(internalPanelRef.current, Focus.First) - }, [focus, internalPanelRef, state.popoverState]) + }, [state.__demoMode, focus, internalPanelRef, state.popoverState]) let slot = useMemo( () => ({ open: state.popoverState === PopoverStates.Open, close }), From 6a94e11b0ca2a8e378b88ea4cdd3d585efecb552 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 21 Apr 2023 13:46:45 +0200 Subject: [PATCH 2/2] update changelog --- packages/@headlessui-react/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index a27e3fc72..207ac6b73 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Added + +- [internal] add demo mode to `Menu` and `Popover` components ([#2448](https://github.com/tailwindlabs/headlessui/pull/2448)) ## [1.7.14] - 2023-04-12