[internal] add demo mode to Menu and Popover components (#2448)
* add demo mode to `Menu` and `Popover` * update changelog
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ type MenuItemDataRef = MutableRefObject<{
|
||||
}>
|
||||
|
||||
interface StateDefinition {
|
||||
__demoMode: boolean
|
||||
menuState: MenuStates
|
||||
buttonRef: MutableRefObject<HTMLButtonElement | null>
|
||||
itemsRef: MutableRefObject<HTMLDivElement | null>
|
||||
@@ -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<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: [],
|
||||
@@ -279,7 +294,6 @@ function MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(
|
||||
[menuState, close]
|
||||
)
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: menuRef }
|
||||
|
||||
return (
|
||||
@@ -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
|
||||
@@ -613,6 +628,7 @@ function ItemFn<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(
|
||||
})
|
||||
return d.dispose
|
||||
}, [
|
||||
state.__demoMode,
|
||||
internalItemRef,
|
||||
active,
|
||||
state.menuState,
|
||||
|
||||
@@ -63,6 +63,7 @@ enum PopoverStates {
|
||||
}
|
||||
|
||||
interface StateDefinition {
|
||||
__demoMode: boolean
|
||||
popoverState: PopoverStates
|
||||
|
||||
buttons: MutableRefObject<Symbol[]>
|
||||
@@ -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 }
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -354,7 +373,6 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
|
||||
[popoverState, close]
|
||||
)
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: popoverRef }
|
||||
|
||||
return (
|
||||
@@ -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
|
||||
@@ -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 }),
|
||||
|
||||
Reference in New Issue
Block a user