You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: apps/website/src/routes/docs/headless/popover/index.mdx
+173-7Lines changed: 173 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,7 +74,7 @@ Here are a couple of example components where the Popover API can be used. Inclu
74
74
]}
75
75
/>
76
76
77
-
## Styling
77
+
## Caveats
78
78
79
79
<Notestatus="warning">
80
80
While this component handles most of the complexity under the hood, there are some minor
@@ -107,14 +107,14 @@ A separate declaration is needed to select popovers in all browsers.
107
107
108
108
## Popovertarget
109
109
110
-
To add a popover trigger, it can be done similar to the native API, using the `popovertarget` attribute along with the corresponding popover id. This includes being used on **native button elements**, but it can be leveraged as a prop as well.
110
+
To add a popover trigger, it can be done similar to the native API, using the `popovertarget` attribute along with the corresponding popover id.
111
111
112
112
```tsx
113
113
// Opens when trigger is clicked, with a matching id to popovertarget
114
114
<PopoverTriggerpopovertarget="get-20-off">Get 20% off your next order!</PopoverTrigger>
115
115
116
-
// Native API: Adding popover here is the same as popover="auto"
117
-
<buttonpopovertarget="get-20-off"popover>Get 20% off your next order!</button>
116
+
// Native API: popovertarget is an invoker
117
+
<buttonpopovertarget="get-20-off">Get 20% off your next order!</button>
118
118
```
119
119
120
120
> This can be used to trigger popups anywhere in the component tree!
@@ -145,8 +145,6 @@ An auto popover will automatically hide when you click outside of it and typical
145
145
146
146
On the other hand, a `manual` popover needs to be manually hidden, such as toggling the button or programmatically, and allows for scenarios like nested popovers in menus.
147
147
148
-
<Showcasename="flip" />
149
-
150
148
### Popover Methods
151
149
152
150
There are two methods for showing and hiding popovers manually in JavaScript. The `showPopover` and `hidePopover` methods.
To use the popover API with floating elements, you can add the `floating={true}` prop to the Popover component. This API enables the use of JavaScript to dynamically position the listbox using the `top` & `left` absolute properties.
173
171
172
+
> Setting `floating={true}` will add 8-10 or so kb, we currently use javascript to float items, and have tried to make it as minimal and incremental as possible for the upcoming anchor API.
173
+
174
174
To float an element, it must have an anchored element to latch onto. This can be done with the `anchorRef` prop.
175
175
176
+
Below is a mini tooltip implementation enabled by anchor behavior. Keep in mind, this is not accessible, but an example of how this API can be used. We strongly suggest using the Qwik UI Tooltip component.
177
+
176
178
### AnchorRef Prop
177
179
178
180
<Showcasename="anchor-ref" />
179
181
180
182
### Floating Prop
181
183
182
-
This API is purposely opt-in / incremental. At some point JavaScript will not be needed here thanks to the CSS Anchor API that is currently experimental.
184
+
This API is purposely opt-in / incremental. At some point JavaScript will not be needed here at all, and so we want to ensure a smooth migration path when that becomes widely supported.
183
185
184
186
We chose not to use an Anchor polyfill here due to the difference in bundle size compared to a JavaScript implementation.
187
+
188
+
## **Configuring the Listbox**
189
+
190
+
The `Popover` component is designed for positioning elements that float and facilitating interactions with them.
191
+
192
+
For the following examples, we'll be using the Combobox component. `ComboboxPopover` is merely a wrapper of the Popover component. Under the hood, it looks something like this:
193
+
194
+
```tsx
195
+
<Popover
196
+
{...props}
197
+
manual
198
+
id={popoverId}
199
+
floating={true}
200
+
// passing in a custom ref for programmatic behavior
201
+
anchorRef={context.inputRef}
202
+
popoverRef={context.popoverRef}
203
+
class="listbox"
204
+
>
205
+
<Slot />
206
+
</Popover>
207
+
```
208
+
209
+
### Placement
210
+
211
+
To set the default position of the listbox, you can use the `placement` prop. In the example below, we've set placement to top. When the user opens the listbox, it will be above the input.
212
+
213
+
<Showcasename="placement" />
214
+
215
+
The example above sets the `placement` prop to `top`. The default placement is **bottom**.
216
+
217
+
### Flip
218
+
219
+
Allows the listbox to flip its position based on available space. It's enabled by default, but can be disabled by adding `flip={false}` on the listbox.
220
+
221
+
<Showcasename="flip" />
222
+
223
+
### Gutter
224
+
225
+
In the previous docs examples, we use the gutter property on the listbox. Gutter is the space between the anchor element and the floating element.
226
+
227
+
<Showcasename="gutter" />
228
+
229
+
> In this case, that is the listbox or unordered list, and the input element. If Flip is enabled, it will provide a gutter space for both the top and bottom.
230
+
231
+
## Styling
232
+
233
+
Styles can be added normally like any other component in Qwik UI, such as adding a class. The Popover API however, exposes the `[popover]` and `:popover-open` attribute and pseudo-class selectors which can be used to style both the open and closed states.
234
+
235
+
From an earlier section, we learned that the `:popover-open` psuedo-class cannot be polyfilled, and so a class is added instead.
236
+
237
+
```tsx
238
+
[popover] {
239
+
/* Make the popover a grid */
240
+
display: grid;
241
+
}
242
+
243
+
[popover]:not(:popover-open) {
244
+
/* Make sure to hide it unless open */
245
+
display: none;
246
+
}
247
+
248
+
/* Duplicate for polyfill browsers */
249
+
[popover]:not(.\:popover-open) {
250
+
display: none;
251
+
}
252
+
```
253
+
254
+
If Tailwind is the framework of choice, then styles can be added using the [arbitrary variant syntax](https://tailwindcss.com/docs/hover-focus-and-other-states#using-arbitrary-variants) or [@apply](https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply) command. Below is an example of styling with `[popover]` as an arbitrary variant.
255
+
256
+
<Showcasename="styling" />
257
+
258
+
> The arbitrary variant can be customized/abstracted even further by [adding a variant](https://tailwindcss.com/docs/plugins#adding-variants) as a plugin in the tailwind config.
259
+
260
+
## Animations
261
+
262
+
### The problem
263
+
264
+
Currently, native animation support for the popover API is [not quite there yet](https://open-ui.org/components/popover.research.explainer/#animation-of-popovers). Part of this has to do with the native API managing the popover state and adding the `display: none` declaration.
265
+
266
+
This declaration is `unanimatable`, meaning you cannot animate to or from display none at this time. With that said, we currently have a way to animate popovers using a clever trick under the hood.
267
+
268
+
> There are [new properties to CSS](https://developer.chrome.com/blog/entry-exit-animations) that aim to fix this problem, but regardless we need to be able to support all browsers.
269
+
270
+
### Custom animation support
271
+
272
+
Regardless of native support, animations need support in polyfill browsers too, and so we've provided a way for you to use both `animation` and `transition` declarations in your `Popover` component for **all browsers**.
273
+
274
+
### Animation declarations
275
+
276
+
<Showcasename="listbox-animation" />
277
+
278
+
To use an animation, add the following CSS classes to the component.
279
+
280
+
- The `.popover-showing` class determines the animation that happens when it is first opened.
281
+
282
+
- The `.popover-closing` class determines what class is added when the listbox is **closed**.
283
+
284
+
Here's the CSS imported from the example:
285
+
286
+
```css
287
+
@keyframesfadeIn {
288
+
from {
289
+
opacity: 0;
290
+
}
291
+
to {
292
+
opacity: 1;
293
+
}
294
+
}
295
+
296
+
@keyframesfadeOut {
297
+
from {
298
+
opacity: 0;
299
+
}
300
+
to {
301
+
opacity: 1;
302
+
}
303
+
}
304
+
305
+
.animate-in {
306
+
animation: fadeIn both500msease;
307
+
}
308
+
309
+
.animate-out {
310
+
animation: fadeOut both500msease;
311
+
}
312
+
```
313
+
314
+
> These classes also apply to transition declarations as well. Below is an example of that use case.
315
+
316
+
### Transition declarations
317
+
318
+
<Showcasename="animation" />
319
+
320
+
Transitions use the same classes for entry and exit animations. Those being `.popover-showing` and `.popover-closing`. They are explained move in the section above.
321
+
322
+
> The [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) is another native solution that aims to solve animating between states. Support is currently in around **~70%** of browsers.
323
+
324
+
## Additional References
325
+
326
+
Qwik UI aims to be in line with the standard whenever possible. Our goal is to give Qwik developers the proper tooling when it comes to creating accessible & complex web applications.
327
+
328
+
To read more about the popover API you can check it out on:
-[What is the top layer?](https://developer.chrome.com/blog/what-is-the-top-layer/)
333
+
334
+
## Backdrops
335
+
336
+
<Showcasename="backdrop" />
337
+
338
+
## Additional Examples
339
+
340
+
### Auto Placement
341
+
342
+
Automatically places the listbox based on available space. **You must set flip to false before using it.** This comes in handy when you're unsure about the optimal placement for the floating element, or if you prefer not to set it manually.
343
+
344
+
<Showcasename="auto-placement" />
345
+
346
+
<Notestatus="caution">
347
+
You cannot use **flip** and **autoPlacement** at the same time. They both manipulate the
348
+
placement but with different strategies. Using both can result in a continuous reset
0 commit comments