Skip to content

Commit 38afc1e

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

File tree

10 files changed

+66
-55
lines changed

10 files changed

+66
-55
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export type Floating = {
2+
ancestorScroll?: boolean;
3+
ancestorResize?: boolean;
4+
elementResize?: boolean;
5+
layoutShift?: boolean;
6+
animationFrame?: boolean;
7+
gutter?: number;
8+
shift?: boolean;
9+
flip?: boolean;
10+
size?: boolean;
11+
hide?: 'referenceHidden' | 'escaped';
12+
inline?: boolean;
13+
transform?: string;
14+
arrow?: boolean;
15+
strategy?: 'absolute' | 'fixed';
16+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export type Placement =
2+
| 'top'
3+
| 'top-start'
4+
| 'top-end'
5+
| 'right'
6+
| 'right-start'
7+
| 'right-end'
8+
| 'bottom'
9+
| 'bottom-start'
10+
| 'bottom-end'
11+
| 'left'
12+
| 'left-start'
13+
| 'left-end';
Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { Signal, createContextId } from '@builder.io/qwik';
2-
import { TPlacement } from './popover-root';
2+
3+
import { type Placement } from './placement.type';
4+
import { type Floating } from './floating.type';
35

46
export const popoverContextId = createContextId<PopoverContext>('qui-popover');
57

6-
export type PopoverContext = {
8+
export type PopoverContext = Floating & {
79
// core state
810
compId: string;
911
isOpenSig: Signal<boolean>;
10-
floating?: boolean | TPlacement;
12+
floating?: boolean | Placement;
1113
localId: string;
1214
manual?: boolean;
1315
hover?: boolean;
@@ -17,19 +19,4 @@ export type PopoverContext = {
1719
panelRef?: Signal<HTMLElement | undefined>;
1820
triggerRef?: Signal<HTMLElement | undefined>;
1921
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;
3522
};

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,24 @@ import {
88
useSignal,
99
} from '@builder.io/qwik';
1010
import { popoverContextId, PopoverContext } from './popover-context';
11+
import { type Placement } from './placement.type';
12+
import { type Floating } from './floating.type';
1113

1214
export type PopoverRootProps = {
1315
popover?: 'manual' | 'auto';
1416
manual?: boolean;
1517
ref?: Signal<HTMLElement | undefined>;
16-
floating?: boolean | TPlacement;
18+
floating?: boolean | Placement;
1719
/** @deprecated Use the tooltip instead, which adheres to the WAI-ARIA design pattern. */
1820
hover?: boolean;
1921
id?: string;
2022
'bind:anchor'?: Signal<HTMLElement | undefined>;
2123
'bind:panel'?: Signal<HTMLElement | undefined>;
2224
};
2325

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-
5426
export type PopoverProps = PopoverRootProps & {
55-
floating?: boolean | TPlacement;
56-
} & FloatingProps &
27+
floating?: boolean | Placement;
28+
} & Floating &
5729
PropsOf<'div'>;
5830

5931
export const HPopoverRoot = component$((props: PopoverProps) => {
@@ -74,6 +46,7 @@ export const HPopoverRoot = component$((props: PopoverProps) => {
7446
elementResize = true,
7547
animationFrame = false,
7648
transform,
49+
strategy,
7750
...rest
7851
} = props;
7952

@@ -109,6 +82,7 @@ export const HPopoverRoot = component$((props: PopoverProps) => {
10982
flip,
11083
shift,
11184
hide,
85+
strategy,
11286
ancestorScroll,
11387
ancestorResize,
11488
elementResize,

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
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
useSignal,
1010
$,
1111
} from '@builder.io/qwik';
12-
import { FloatingProps, HPopoverRoot } from '../popover/popover-root';
12+
import { type Floating } from '../popover/floating.type';
13+
import { HPopoverRoot } from '../popover/popover-root';
1314
import { TooltipContext, TooltipContextId, TriggerDataState } from './tooltip-context';
1415

1516
/**
@@ -42,7 +43,7 @@ export type TooltipRootProps = {
4243
placement?: Parameters<typeof HPopoverRoot>['0']['floating'];
4344

4445
id?: string;
45-
} & Pick<FloatingProps, 'flip' | 'gutter'>;
46+
} & Pick<Floating, 'flip' | 'gutter'>;
4647

4748
/**
4849
* TooltipProps combines TooltipRootProps and the properties of a div element.

0 commit comments

Comments
 (0)