Skip to content

Commit 3deefee

Browse files
committed
minor: adds strategy prop to use when positioning the floating element in popover
1 parent 257e0e5 commit 3deefee

File tree

11 files changed

+77
-63
lines changed

11 files changed

+77
-63
lines changed

.changeset/three-toes-love.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik-ui/headless': minor
3+
---
4+
5+
minor: adds strategy prop to use when positioning the floating element in popover

apps/website/src/routes/docs/headless/popover/index.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,15 @@ To read more about the popover API you can check it out on:
362362
},
363363
{
364364
name: 'floating',
365-
type: 'boolean | TPlacement',
365+
type: 'boolean | Placement',
366366
description: 'Enables extra JavaScript behavior for floating elements.',
367367
},
368+
{
369+
name: 'strategy',
370+
type: 'absolute | fixed',
371+
description:
372+
'The strategy to use when positioning the floating element. The default value is absolute, which suites most cases, while fixed position might be better in legacy browsers like iOS 15.4.',
373+
},
368374
{
369375
name: 'anchorRef',
370376
type: 'Signal',

packages/kit-headless/src/components/popover/floating.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { PropsOf, Slot, component$, useContext, useTask$ } from '@builder.io/qwi
1313

1414
import { HPopoverPanelImpl } from './popover-panel-impl';
1515
import { isServer } from '@builder.io/qwik/build';
16-
import { popoverContextId } from './popover-context';
16+
import { popoverContextId } from './popover-types';
1717

1818
export const FloatingPopover = component$((props: PropsOf<'div'>) => {
1919
const context = useContext(popoverContextId);
@@ -58,6 +58,7 @@ export const FloatingPopover = component$((props: PropsOf<'div'>) => {
5858
await computePosition(anchor as ReferenceElement, popover, {
5959
placement: placement as Placement,
6060
middleware,
61+
strategy: context.strategy,
6162
}).then(async (resolvedData) => {
6263
const { x, y } = resolvedData;
6364

packages/kit-headless/src/components/popover/popover-panel-arrow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PropsOf, component$, useContext } from '@builder.io/qwik';
22

3-
import { popoverContextId } from './popover-context';
3+
import { popoverContextId } from './popover-types';
44

55
export const HPopoverPanelArrow = component$((props: PropsOf<'div'>) => {
66
const context = useContext(popoverContextId);

packages/kit-headless/src/components/popover/popover-panel-impl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313

1414
import { isServer } from '@builder.io/qwik/build';
1515
import { useCombinedRef } from '../../hooks/combined-refs';
16-
import { popoverContextId } from './popover-context';
16+
import { popoverContextId } from './popover-types';
1717
import popoverStyles from './popover.css?inline';
1818

1919
// We don't need a provider, that way we connect all context to the root

packages/kit-headless/src/components/popover/popover-panel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { component$, PropsOf, Slot, useContext } from '@builder.io/qwik';
22
import { FloatingPopover } from './floating';
33
import { HPopoverPanelImpl } from './popover-panel-impl';
4-
import { popoverContextId } from './popover-context';
4+
import { popoverContextId } from './popover-types';
55

66
// TODO: improve the type so that it only includes FloatingProps when floating is true.
77

@@ -11,7 +11,7 @@ export const HPopoverPanel = component$((props: PropsOf<'div'>) => {
1111

1212
if (context.floating) {
1313
return (
14-
<FloatingPopover data-floating {...props}>
14+
<FloatingPopover data-floating={context.strategy || 'absolute'} {...props}>
1515
<Slot />
1616
</FloatingPopover>
1717
);

packages/kit-headless/src/components/popover/popover-root.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,29 @@ import {
77
useId,
88
useSignal,
99
} from '@builder.io/qwik';
10-
import { popoverContextId, PopoverContext } from './popover-context';
10+
11+
import {
12+
popoverContextId,
13+
type Floating,
14+
type Placement,
15+
type PopoverContext,
16+
} from './popover-types';
1117

1218
export type PopoverRootProps = {
1319
popover?: 'manual' | 'auto';
1420
manual?: boolean;
1521
ref?: Signal<HTMLElement | undefined>;
16-
floating?: boolean | TPlacement;
22+
floating?: boolean | Placement;
1723
/** @deprecated Use the tooltip instead, which adheres to the WAI-ARIA design pattern. */
1824
hover?: boolean;
1925
id?: string;
2026
'bind:anchor'?: Signal<HTMLElement | undefined>;
2127
'bind:panel'?: Signal<HTMLElement | undefined>;
2228
};
2329

24-
export type FloatingProps = {
25-
ancestorScroll?: boolean;
26-
ancestorResize?: boolean;
27-
elementResize?: boolean;
28-
layoutShift?: boolean;
29-
animationFrame?: boolean;
30-
gutter?: number;
31-
shift?: boolean;
32-
flip?: boolean;
33-
size?: boolean;
34-
hide?: 'referenceHidden' | 'escaped';
35-
inline?: boolean;
36-
transform?: string;
37-
arrow?: boolean;
38-
};
39-
40-
export type TPlacement =
41-
| 'top'
42-
| 'top-start'
43-
| 'top-end'
44-
| 'right'
45-
| 'right-start'
46-
| 'right-end'
47-
| 'bottom'
48-
| 'bottom-start'
49-
| 'bottom-end'
50-
| 'left'
51-
| 'left-start'
52-
| 'left-end';
53-
5430
export type PopoverProps = PopoverRootProps & {
55-
floating?: boolean | TPlacement;
56-
} & FloatingProps &
31+
floating?: boolean | Placement;
32+
} & Floating &
5733
PropsOf<'div'>;
5834

5935
export const HPopoverRoot = component$((props: PopoverProps) => {
@@ -74,6 +50,7 @@ export const HPopoverRoot = component$((props: PopoverProps) => {
7450
elementResize = true,
7551
animationFrame = false,
7652
transform,
53+
strategy,
7754
...rest
7855
} = props;
7956

@@ -109,6 +86,7 @@ export const HPopoverRoot = component$((props: PopoverProps) => {
10986
flip,
11087
shift,
11188
hide,
89+
strategy,
11290
ancestorScroll,
11391
ancestorResize,
11492
elementResize,

packages/kit-headless/src/components/popover/popover-trigger.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Slot, component$, $, PropsOf, useContext } from '@builder.io/qwik';
2-
import { popoverContextId } from './popover-context';
2+
import { popoverContextId } from './popover-types';
33
import { usePopover } from './use-popover';
44

55
type PopoverTriggerProps = {
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,41 @@
1-
import { Signal, createContextId } from '@builder.io/qwik';
2-
import { TPlacement } from './popover-root';
1+
import { type Signal, createContextId } from '@builder.io/qwik';
32

4-
export const popoverContextId = createContextId<PopoverContext>('qui-popover');
3+
export type Floating = {
4+
ancestorScroll?: boolean;
5+
ancestorResize?: boolean;
6+
elementResize?: boolean;
7+
layoutShift?: boolean;
8+
animationFrame?: boolean;
9+
gutter?: number;
10+
shift?: boolean;
11+
flip?: boolean;
12+
size?: boolean;
13+
hide?: 'referenceHidden' | 'escaped';
14+
inline?: boolean;
15+
transform?: string;
16+
arrow?: boolean;
17+
strategy?: 'absolute' | 'fixed';
18+
};
19+
20+
export type Placement =
21+
| 'top'
22+
| 'top-start'
23+
| 'top-end'
24+
| 'right'
25+
| 'right-start'
26+
| 'right-end'
27+
| 'bottom'
28+
| 'bottom-start'
29+
| 'bottom-end'
30+
| 'left'
31+
| 'left-start'
32+
| 'left-end';
533

6-
export type PopoverContext = {
34+
export type PopoverContext = Floating & {
735
// core state
836
compId: string;
937
isOpenSig: Signal<boolean>;
10-
floating?: boolean | TPlacement;
38+
floating?: boolean | Placement;
1139
localId: string;
1240
manual?: boolean;
1341
hover?: boolean;
@@ -17,19 +45,6 @@ export type PopoverContext = {
1745
panelRef?: Signal<HTMLElement | undefined>;
1846
triggerRef?: Signal<HTMLElement | undefined>;
1947
arrowRef?: Signal<HTMLElement | undefined>;
20-
21-
// floating props
22-
ancestorScroll?: boolean;
23-
ancestorResize?: boolean;
24-
elementResize?: boolean;
25-
layoutShift?: boolean;
26-
animationFrame?: boolean;
27-
gutter?: number;
28-
shift?: boolean;
29-
flip?: boolean;
30-
size?: boolean;
31-
arrow?: boolean;
32-
hide?: 'referenceHidden' | 'escaped';
33-
inline?: boolean;
34-
transform?: string;
3548
};
49+
50+
export const popoverContextId = createContextId<PopoverContext>('qui-popover');

packages/kit-headless/src/components/popover/popover.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
overflow: unset;
2020
position: absolute;
2121
}
22+
23+
[data-floating='fixed'] {
24+
position: fixed;
25+
}
2226
}
2327

2428
/** override the polyfill's layer, which gets dynamically imported later on. */
@@ -30,4 +34,8 @@
3034
overflow: unset;
3135
position: absolute;
3236
}
37+
38+
[data-floating='fixed'] {
39+
position: fixed;
40+
}
3341
}

0 commit comments

Comments
 (0)