Add string shorthand for the anchor prop (#3133)
* allow to define `anchor` as a string. E.g.: `anchor="bottom"` * use `--anchor-gap`, `--anchor-offset` and `--anchor-padding` variables by default This way simply adding `anchor="bottom"` to one of the anchorable components will also use these variables defined on the component. * update playgrounds to use new string-based `anchor` prop + CSS variables * update changelog
This commit is contained in:
@@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Allow passing a boolean to the `anchor` prop ([#3121](https://github.com/tailwindlabs/headlessui/pull/3121))
|
||||
- Add `portal` prop to `Combobox`, `Listbox`, `Menu` and `Popover` components ([#3124](https://github.com/tailwindlabs/headlessui/pull/3124))
|
||||
- Add frozen value to `ComboboxOptions` component ([#3126](https://github.com/tailwindlabs/headlessui/pull/3126))
|
||||
- Add string shorthand for the `anchor` prop ([#3133](https://github.com/tailwindlabs/headlessui/pull/3133))
|
||||
|
||||
## [1.7.19] - 2024-04-15
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ type BaseAnchorProps = {
|
||||
}
|
||||
|
||||
export type AnchorProps =
|
||||
| boolean // Enable with defaults, or disable entirely
|
||||
| false // Disable entirely
|
||||
| (`${Placement}` | `${Placement} ${Align}`) // String value to define the placement
|
||||
| Partial<
|
||||
BaseAnchorProps & {
|
||||
/**
|
||||
@@ -50,7 +51,8 @@ export type AnchorProps =
|
||||
>
|
||||
|
||||
export type AnchorPropsWithSelection =
|
||||
| boolean // Enable with defaults, or disable entirely
|
||||
| false // Disable entirely
|
||||
| (`${Placement | 'selection'}` | `${Placement | 'selection'} ${Align}`)
|
||||
| Partial<
|
||||
BaseAnchorProps & {
|
||||
/**
|
||||
@@ -93,11 +95,11 @@ PlacementContext.displayName = 'PlacementContext'
|
||||
|
||||
export function useResolvedAnchor<T extends AnchorProps | AnchorPropsWithSelection>(
|
||||
anchor?: T
|
||||
): Exclude<T, boolean> | null {
|
||||
): Exclude<T, boolean | string> | null {
|
||||
return useMemo(() => {
|
||||
if (anchor === true) return {} as Exclude<T, boolean> // Enable with defaults
|
||||
if (!anchor) return null // Disable entirely
|
||||
return anchor as Exclude<T, boolean> // User-provided value
|
||||
if (typeof anchor === 'string') return { to: anchor } as Exclude<T, boolean | string> // Simple string based value,
|
||||
return anchor as Exclude<T, boolean | string> // User-provided value
|
||||
}, [anchor])
|
||||
}
|
||||
|
||||
@@ -124,8 +126,8 @@ export function useFloatingPanelProps() {
|
||||
export function useFloatingPanel(
|
||||
placement: (AnchorPropsWithSelection & InternalFloatingPanelProps) | null = null
|
||||
) {
|
||||
if (placement === true) placement = {} // Enable with defaults
|
||||
if (placement === false) placement = null // Disable entirely
|
||||
if (typeof placement === 'string') placement = { to: placement } // Simple string based value
|
||||
|
||||
let updatePlacementConfig = useContext(PlacementContext)
|
||||
let stablePlacement = useMemo(
|
||||
@@ -389,12 +391,12 @@ function useFixScrollingPixel(element: HTMLElement | null) {
|
||||
}
|
||||
|
||||
function useResolvedConfig(
|
||||
config: (Exclude<AnchorPropsWithSelection, boolean> & InternalFloatingPanelProps) | null,
|
||||
config: (Exclude<AnchorPropsWithSelection, boolean | string> & InternalFloatingPanelProps) | null,
|
||||
element?: HTMLElement | null
|
||||
) {
|
||||
let gap = useResolvePxValue(config?.gap, element)
|
||||
let offset = useResolvePxValue(config?.offset, element)
|
||||
let padding = useResolvePxValue(config?.padding, element)
|
||||
let gap = useResolvePxValue(config?.gap ?? 'var(--anchor-gap, 0)', element)
|
||||
let offset = useResolvePxValue(config?.offset ?? 'var(--anchor-offset, 0)', element)
|
||||
let padding = useResolvePxValue(config?.padding ?? 'var(--anchor-padding, 0)', element)
|
||||
|
||||
return { ...config, gap, offset, padding }
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ export default function Home() {
|
||||
afterLeave={() => console.log('After leave')}
|
||||
>
|
||||
<Menu.Items
|
||||
anchor={{ to: 'bottom start', gap: 'var(--gap)' }}
|
||||
className="w-[calc(var(--button-width)*2)] divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none [--gap:theme(spacing.2)]"
|
||||
anchor="bottom start"
|
||||
className="w-[calc(var(--button-width)*2)] divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none [--anchor-gap:theme(spacing.2)]"
|
||||
>
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
|
||||
@@ -60,8 +60,8 @@ export default function Home() {
|
||||
<Popover as="div" className="relative">
|
||||
<Button>Portal</Button>
|
||||
<Popover.Panel
|
||||
anchor={{ to: 'bottom start', gap: 4 }}
|
||||
className="flex w-64 flex-col border-2 border-blue-900 bg-gray-100"
|
||||
anchor="bottom start"
|
||||
className="flex w-64 flex-col border-2 border-blue-900 bg-gray-100 [--anchor-gap:theme(spacing.1)]"
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Button key={item}>Portal - {item}</Button>
|
||||
@@ -72,9 +72,9 @@ export default function Home() {
|
||||
<Popover as="div" className="relative">
|
||||
<Button>Focus in Portal</Button>
|
||||
<Popover.Panel
|
||||
anchor={{ to: 'bottom start', gap: 4 }}
|
||||
focus
|
||||
className="flex w-64 flex-col border-2 border-blue-900 bg-gray-100"
|
||||
anchor="bottom start"
|
||||
className="flex w-64 flex-col border-2 border-blue-900 bg-gray-100 [--anchor-gap:theme(spacing.1)]"
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Button key={item}>Focus in Portal - {item}</Button>
|
||||
|
||||
Reference in New Issue
Block a user