Skip to content

Commit

Permalink
[internal] add demo mode to Menu and Popover components (#2448)
Browse files Browse the repository at this point in the history
* add demo mode to `Menu` and `Popover`

* update changelog
  • Loading branch information
RobinMalfait committed Apr 21, 2023
1 parent b98e642 commit 08127dd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 16 deletions.
4 changes: 3 additions & 1 deletion packages/@headlessui-react/CHANGELOG.md
Expand Up @@ -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

Expand Down
24 changes: 20 additions & 4 deletions packages/@headlessui-react/src/components/menu/menu.tsx
Expand Up @@ -69,6 +69,7 @@ type MenuItemDataRef = MutableRefObject<{
}>

interface StateDefinition {
__demoMode: boolean
menuState: MenuStates
buttonRef: MutableRefObject<HTMLButtonElement | null>
itemsRef: MutableRefObject<HTMLDivElement | null>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -238,14 +244,23 @@ interface MenuRenderPropArg {
close: () => void
}

export type MenuProps<TTag extends ElementType> = Props<TTag, MenuRenderPropArg>
export type MenuProps<TTag extends ElementType> = Props<
TTag,
MenuRenderPropArg,
never,
{
__demoMode?: boolean
}
>

function MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(
props: MenuProps<TTag>,
ref: Ref<HTMLElement>
) {
let { __demoMode = false, ...theirProps } = props
let reducerBag = useReducer(stateReducer, {
menuState: MenuStates.Closed,
__demoMode,
menuState: __demoMode ? MenuStates.Open : MenuStates.Closed,
buttonRef: createRef(),
itemsRef: createRef(),
items: [],
Expand Down Expand Up @@ -279,7 +294,6 @@ function MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(
[menuState, close]
)

let theirProps = props
let ourProps = { ref: menuRef }

return (
Expand Down Expand Up @@ -604,6 +618,7 @@ function ItemFn<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(
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
Expand All @@ -613,6 +628,7 @@ function ItemFn<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(
})
return d.dispose
}, [
state.__demoMode,
internalItemRef,
active,
state.menuState,
Expand Down
41 changes: 30 additions & 11 deletions packages/@headlessui-react/src/components/popover/popover.tsx
Expand Up @@ -63,6 +63,7 @@ enum PopoverStates {
}

interface StateDefinition {
__demoMode: boolean
popoverState: PopoverStates

buttons: MutableRefObject<Symbol[]>
Expand Down Expand Up @@ -100,13 +101,22 @@ let reducers: {
action: Extract<Actions, { type: P }>
) => 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 }
Expand Down Expand Up @@ -198,12 +208,20 @@ interface PopoverRenderPropArg {
): void
}

export type PopoverProps<TTag extends ElementType> = Props<TTag, PopoverRenderPropArg>
export type PopoverProps<TTag extends ElementType> = Props<
TTag,
PopoverRenderPropArg,
never,
{
__demoMode?: boolean
}
>

function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
props: PopoverProps<TTag>,
ref: Ref<HTMLElement>
) {
let { __demoMode = false, ...theirProps } = props
let internalPopoverRef = useRef<HTMLElement | null>(null)
let popoverRef = useSyncRefs(
ref,
Expand All @@ -214,7 +232,8 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(

let buttons = useRef([])
let reducerBag = useReducer(stateReducer, {
popoverState: PopoverStates.Closed,
__demoMode,
popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,
buttons,
button: null,
buttonId: null,
Expand Down Expand Up @@ -354,7 +373,6 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
[popoverState, close]
)

let theirProps = props
let ourProps = { ref: popoverRef }

return (
Expand Down Expand Up @@ -771,6 +789,7 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(

// Move focus within panel
useEffect(() => {
if (state.__demoMode) return
if (!focus) return
if (state.popoverState !== PopoverStates.Open) return
if (!internalPanelRef.current) return
Expand All @@ -779,7 +798,7 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
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<PanelRenderPropArg>(
() => ({ open: state.popoverState === PopoverStates.Open, close }),
Expand Down

0 comments on commit 08127dd

Please sign in to comment.