Render <MainTreeNode /> indicators in Popover.Group only (#2634)
* only render `<MainTreeNode />` in `Popover.Group` instead of after every `Popover` * make Vue Popover consistent * apply same `MainTreeNode` logic to Vue version * update changelog
This commit is contained in:
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Fixed
|
||||
|
||||
- Use correct value when resetting `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))
|
||||
- Render `<MainTreeNode />` in `Popover.Group` component only ([#2634](https://github.com/tailwindlabs/headlessui/pull/2634))
|
||||
|
||||
## [1.7.16] - 2023-07-27
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ import { useTabDirection, Direction as TabDirection } from '../../hooks/use-tab-
|
||||
import { microTask } from '../../utils/micro-task'
|
||||
import { useLatestValue } from '../../hooks/use-latest-value'
|
||||
import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'
|
||||
import { useRootContainers } from '../../hooks/use-root-containers'
|
||||
import { useMainTreeNode, useRootContainers } from '../../hooks/use-root-containers'
|
||||
import { useNestedPortals } from '../../components/portal/portal'
|
||||
|
||||
type MouseEvent<T> = Parameters<MouseEventHandler<T>>[0]
|
||||
@@ -177,6 +177,7 @@ let PopoverGroupContext = createContext<{
|
||||
unregisterPopover(registerbag: PopoverRegisterBag): void
|
||||
isFocusWithinPopoverGroup(): boolean
|
||||
closeOthers(buttonId: string): void
|
||||
mainTreeNodeRef: MutableRefObject<HTMLElement | null>
|
||||
} | null>(null)
|
||||
PopoverGroupContext.displayName = 'PopoverGroupContext'
|
||||
|
||||
@@ -313,6 +314,7 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
|
||||
|
||||
let [portals, PortalWrapper] = useNestedPortals()
|
||||
let root = useRootContainers({
|
||||
mainTreeNodeRef: groupContext?.mainTreeNodeRef,
|
||||
portals,
|
||||
defaultContainers: [button, panel],
|
||||
})
|
||||
@@ -971,6 +973,7 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
let internalGroupRef = useRef<HTMLElement | null>(null)
|
||||
let groupRef = useSyncRefs(internalGroupRef, ref)
|
||||
let [popovers, setPopovers] = useState<PopoverRegisterBag[]>([])
|
||||
let root = useMainTreeNode()
|
||||
|
||||
let unregisterPopover = useEvent((registerbag: PopoverRegisterBag) => {
|
||||
setPopovers((existing) => {
|
||||
@@ -1017,8 +1020,15 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
unregisterPopover: unregisterPopover,
|
||||
isFocusWithinPopoverGroup,
|
||||
closeOthers,
|
||||
mainTreeNodeRef: root.mainTreeNodeRef,
|
||||
}),
|
||||
[registerPopover, unregisterPopover, isFocusWithinPopoverGroup, closeOthers]
|
||||
[
|
||||
registerPopover,
|
||||
unregisterPopover,
|
||||
isFocusWithinPopoverGroup,
|
||||
closeOthers,
|
||||
root.mainTreeNodeRef,
|
||||
]
|
||||
)
|
||||
|
||||
let slot = useMemo<GroupRenderPropArg>(() => ({}), [])
|
||||
@@ -1035,6 +1045,7 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
defaultTag: DEFAULT_GROUP_TAG,
|
||||
name: 'Popover.Group',
|
||||
})}
|
||||
<root.MainTreeNode />
|
||||
</PopoverGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import React, { useRef, useMemo, MutableRefObject } from 'react'
|
||||
import { Hidden, Features as HiddenFeatures } from '../internal/hidden'
|
||||
import { useComputed } from './use-computed'
|
||||
import { useEvent } from './use-event'
|
||||
import { useOwnerDocument } from './use-owner'
|
||||
|
||||
export function useRootContainers({
|
||||
defaultContainers = [],
|
||||
portals,
|
||||
mainTreeNodeRef: _mainTreeNodeRef,
|
||||
}: {
|
||||
defaultContainers?: (HTMLElement | null | MutableRefObject<HTMLElement | null>)[]
|
||||
portals?: MutableRefObject<HTMLElement[]>
|
||||
mainTreeNodeRef?: MutableRefObject<HTMLElement | null>
|
||||
} = {}) {
|
||||
// Reference to a node in the "main" tree, not in the portalled Dialog tree.
|
||||
let mainTreeNodeRef = useRef<HTMLDivElement | null>(null)
|
||||
let mainTreeNodeRef = useRef<HTMLElement | null>(_mainTreeNodeRef?.current ?? null)
|
||||
let ownerDocument = useOwnerDocument(mainTreeNodeRef)
|
||||
|
||||
let resolveContainers = useEvent(() => {
|
||||
@@ -54,6 +57,25 @@ export function useRootContainers({
|
||||
contains: useEvent((element: HTMLElement) =>
|
||||
resolveContainers().some((container) => container.contains(element))
|
||||
),
|
||||
mainTreeNodeRef,
|
||||
MainTreeNode: useMemo(() => {
|
||||
return function MainTreeNode() {
|
||||
let hasPassedInMainTreeNode = useComputed(
|
||||
() => (_mainTreeNodeRef?.current ?? null) !== null,
|
||||
[_mainTreeNodeRef]
|
||||
)
|
||||
if (hasPassedInMainTreeNode) return null
|
||||
|
||||
return <Hidden features={HiddenFeatures.Hidden} ref={mainTreeNodeRef} />
|
||||
}
|
||||
}, [mainTreeNodeRef]),
|
||||
}
|
||||
}
|
||||
|
||||
export function useMainTreeNode() {
|
||||
let mainTreeNodeRef = useRef<HTMLElement | null>(null)
|
||||
|
||||
return {
|
||||
mainTreeNodeRef,
|
||||
MainTreeNode: useMemo(() => {
|
||||
return function MainTreeNode() {
|
||||
|
||||
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Fix form elements for uncontrolled `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))
|
||||
- Use correct value when resetting `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))
|
||||
- Render `<MainTreeNode />` in `PopoverGroup` component only ([#2634](https://github.com/tailwindlabs/headlessui/pull/2634))
|
||||
|
||||
## [1.7.15] - 2023-07-27
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import { useEventListener } from '../../hooks/use-event-listener'
|
||||
import { Hidden, Features as HiddenFeatures } from '../../internal/hidden'
|
||||
import { useTabDirection, Direction as TabDirection } from '../../hooks/use-tab-direction'
|
||||
import { microTask } from '../../utils/micro-task'
|
||||
import { useRootContainers } from '../../hooks/use-root-containers'
|
||||
import { useMainTreeNode, useRootContainers } from '../../hooks/use-root-containers'
|
||||
import { useNestedPortals } from '../../components/portal/portal'
|
||||
|
||||
enum PopoverStates {
|
||||
@@ -82,6 +82,7 @@ let PopoverGroupContext = Symbol('PopoverGroupContext') as InjectionKey<{
|
||||
unregisterPopover(registerbag: PopoverRegisterBag): void
|
||||
isFocusWithinPopoverGroup(): boolean
|
||||
closeOthers(buttonId: string): void
|
||||
mainTreeNodeRef: Ref<HTMLElement | null>
|
||||
} | null>
|
||||
|
||||
function usePopoverGroupContext() {
|
||||
@@ -103,6 +104,7 @@ interface PopoverRegisterBag {
|
||||
|
||||
export let Popover = defineComponent({
|
||||
name: 'Popover',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
as: { type: [Object, String], default: 'div' },
|
||||
},
|
||||
@@ -208,6 +210,7 @@ export let Popover = defineComponent({
|
||||
|
||||
let [portals, PortalWrapper] = useNestedPortals()
|
||||
let root = useRootContainers({
|
||||
mainTreeNodeRef: groupContext?.mainTreeNodeRef,
|
||||
portals,
|
||||
defaultContainers: [button, panel],
|
||||
})
|
||||
@@ -259,16 +262,19 @@ export let Popover = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: popoverState.value === PopoverStates.Open, close: api.close }
|
||||
return h(PortalWrapper, {}, () =>
|
||||
render({
|
||||
theirProps: { ...props, ...attrs },
|
||||
ourProps: { ref: internalPopoverRef },
|
||||
slot,
|
||||
slots,
|
||||
attrs,
|
||||
name: 'Popover',
|
||||
})
|
||||
)
|
||||
return h(Fragment, [
|
||||
h(PortalWrapper, {}, () =>
|
||||
render({
|
||||
theirProps: { ...props, ...attrs },
|
||||
ourProps: { ref: internalPopoverRef },
|
||||
slot,
|
||||
slots,
|
||||
attrs,
|
||||
name: 'Popover',
|
||||
})
|
||||
),
|
||||
h(root.MainTreeNode),
|
||||
])
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -745,6 +751,7 @@ export let PopoverPanel = defineComponent({
|
||||
|
||||
export let PopoverGroup = defineComponent({
|
||||
name: 'PopoverGroup',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
as: { type: [Object, String], default: 'div' },
|
||||
},
|
||||
@@ -752,6 +759,7 @@ export let PopoverGroup = defineComponent({
|
||||
let groupRef = ref<HTMLElement | null>(null)
|
||||
let popovers = shallowRef<PopoverRegisterBag[]>([])
|
||||
let ownerDocument = computed(() => getOwnerDocument(groupRef))
|
||||
let root = useMainTreeNode()
|
||||
|
||||
expose({ el: groupRef, $el: groupRef })
|
||||
|
||||
@@ -794,19 +802,23 @@ export let PopoverGroup = defineComponent({
|
||||
unregisterPopover,
|
||||
isFocusWithinPopoverGroup,
|
||||
closeOthers,
|
||||
mainTreeNodeRef: root.mainTreeNodeRef,
|
||||
})
|
||||
|
||||
return () => {
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps: props,
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
name: 'PopoverGroup',
|
||||
})
|
||||
return h(Fragment, [
|
||||
render({
|
||||
ourProps,
|
||||
theirProps: { ...props, ...attrs },
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
name: 'PopoverGroup',
|
||||
}),
|
||||
h(root.MainTreeNode),
|
||||
])
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ref, h, Ref } from 'vue'
|
||||
import { ref, h, Ref, computed } from 'vue'
|
||||
import { Hidden, Features as HiddenFeatures } from '../internal/hidden'
|
||||
import { getOwnerDocument } from '../utils/owner'
|
||||
import { dom } from '../utils/dom'
|
||||
@@ -6,12 +6,14 @@ import { dom } from '../utils/dom'
|
||||
export function useRootContainers({
|
||||
defaultContainers = [],
|
||||
portals,
|
||||
mainTreeNodeRef: _mainTreeNodeRef,
|
||||
}: {
|
||||
defaultContainers?: (HTMLElement | null | Ref<HTMLElement | null>)[]
|
||||
portals?: Ref<HTMLElement[]>
|
||||
mainTreeNodeRef?: Ref<HTMLElement | null>
|
||||
} = {}) {
|
||||
// Reference to a node in the "main" tree, not in the portalled Dialog tree.
|
||||
let mainTreeNodeRef = ref<HTMLDivElement | null>(null)
|
||||
let mainTreeNodeRef = ref<HTMLElement | null>(null)
|
||||
let ownerDocument = getOwnerDocument(mainTreeNodeRef)
|
||||
|
||||
function resolveContainers() {
|
||||
@@ -54,6 +56,19 @@ export function useRootContainers({
|
||||
contains(element: HTMLElement) {
|
||||
return resolveContainers().some((container) => container.contains(element))
|
||||
},
|
||||
mainTreeNodeRef,
|
||||
MainTreeNode() {
|
||||
let hasPassedInMainTreeNode = (_mainTreeNodeRef?.value ?? null) !== null
|
||||
if (hasPassedInMainTreeNode) return null
|
||||
return h(Hidden, { features: HiddenFeatures.Hidden, ref: mainTreeNodeRef })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function useMainTreeNode() {
|
||||
let mainTreeNodeRef = ref<HTMLElement | null>(null)
|
||||
|
||||
return {
|
||||
mainTreeNodeRef,
|
||||
MainTreeNode() {
|
||||
return h(Hidden, { features: HiddenFeatures.Hidden, ref: mainTreeNodeRef })
|
||||
|
||||
Reference in New Issue
Block a user