@wordpress/ui 0.13.1-next.v.202605131032.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -1
- package/CONTRIBUTING.md +34 -0
- package/README.md +15 -0
- package/build/alert-dialog/portal.cjs.map +2 -2
- package/build/alert-dialog/types.cjs.map +1 -1
- package/build/button/button.cjs +1 -1
- package/build/button/button.cjs.map +2 -2
- package/build/card/content.cjs +1 -1
- package/build/card/content.cjs.map +2 -2
- package/build/card/full-bleed.cjs +1 -1
- package/build/card/full-bleed.cjs.map +2 -2
- package/build/card/header.cjs +1 -1
- package/build/card/header.cjs.map +2 -2
- package/build/card/root.cjs +1 -1
- package/build/card/root.cjs.map +2 -2
- package/build/collapsible-card/header.cjs.map +2 -2
- package/build/dialog/portal.cjs.map +2 -2
- package/build/dialog/types.cjs.map +1 -1
- package/build/drawer/portal.cjs.map +2 -2
- package/build/drawer/types.cjs.map +1 -1
- package/build/form/primitives/autocomplete/clear.cjs +4 -1
- package/build/form/primitives/autocomplete/clear.cjs.map +2 -2
- package/build/form/primitives/autocomplete/empty.cjs +1 -1
- package/build/form/primitives/autocomplete/empty.cjs.map +2 -2
- package/build/form/primitives/autocomplete/index.cjs +4 -1
- package/build/form/primitives/autocomplete/index.cjs.map +2 -2
- package/build/form/primitives/autocomplete/item.cjs +1 -1
- package/build/form/primitives/autocomplete/item.cjs.map +2 -2
- package/build/form/primitives/autocomplete/list-body.cjs +1 -1
- package/build/form/primitives/autocomplete/list-body.cjs.map +2 -2
- package/build/form/primitives/autocomplete/list.cjs +1 -1
- package/build/form/primitives/autocomplete/list.cjs.map +2 -2
- package/build/form/primitives/autocomplete/popup.cjs +14 -31
- package/build/form/primitives/autocomplete/popup.cjs.map +3 -3
- package/build/form/primitives/autocomplete/portal.cjs +10 -2
- package/build/form/primitives/autocomplete/portal.cjs.map +2 -2
- package/build/form/primitives/autocomplete/positioner.cjs +158 -0
- package/build/form/primitives/autocomplete/positioner.cjs.map +7 -0
- package/build/form/primitives/autocomplete/types.cjs.map +1 -1
- package/build/form/primitives/constants.cjs.map +2 -2
- package/build/form/primitives/select/index.cjs +4 -1
- package/build/form/primitives/select/index.cjs.map +2 -2
- package/build/form/primitives/select/item.cjs +1 -1
- package/build/form/primitives/select/item.cjs.map +2 -2
- package/build/form/primitives/select/popup.cjs +18 -36
- package/build/form/primitives/select/popup.cjs.map +3 -3
- package/build/form/primitives/select/portal.cjs +11 -5
- package/build/form/primitives/select/portal.cjs.map +2 -2
- package/build/form/primitives/select/positioner.cjs +159 -0
- package/build/form/primitives/select/positioner.cjs.map +7 -0
- package/build/form/primitives/select/types.cjs.map +1 -1
- package/build/icon-button/icon-button.cjs +1 -1
- package/build/icon-button/icon-button.cjs.map +2 -2
- package/build/index.cjs +7 -1
- package/build/index.cjs.map +2 -2
- package/build/popover/index.cjs +3 -0
- package/build/popover/index.cjs.map +2 -2
- package/build/popover/popup.cjs +23 -51
- package/build/popover/popup.cjs.map +3 -3
- package/build/popover/portal.cjs.map +2 -2
- package/build/popover/positioner.cjs +168 -0
- package/build/popover/positioner.cjs.map +7 -0
- package/build/popover/root.cjs.map +2 -2
- package/build/popover/types.cjs.map +1 -1
- package/build/tooltip/portal.cjs +10 -2
- package/build/tooltip/portal.cjs.map +2 -2
- package/build/tooltip/positioner.cjs.map +2 -2
- package/build/tooltip/types.cjs.map +1 -1
- package/build/utils/use-enable-wp-compat-overlay-slot.cjs +39 -0
- package/build/utils/use-enable-wp-compat-overlay-slot.cjs.map +7 -0
- package/build/utils/wp-compat-overlay-slot.cjs +177 -0
- package/build/utils/wp-compat-overlay-slot.cjs.map +7 -0
- package/build-module/alert-dialog/portal.mjs.map +2 -2
- package/build-module/button/button.mjs +1 -1
- package/build-module/button/button.mjs.map +2 -2
- package/build-module/card/content.mjs +1 -1
- package/build-module/card/content.mjs.map +2 -2
- package/build-module/card/full-bleed.mjs +1 -1
- package/build-module/card/full-bleed.mjs.map +2 -2
- package/build-module/card/header.mjs +1 -1
- package/build-module/card/header.mjs.map +2 -2
- package/build-module/card/root.mjs +1 -1
- package/build-module/card/root.mjs.map +2 -2
- package/build-module/collapsible-card/header.mjs.map +2 -2
- package/build-module/dialog/portal.mjs.map +2 -2
- package/build-module/drawer/portal.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/clear.mjs +4 -1
- package/build-module/form/primitives/autocomplete/clear.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/empty.mjs +1 -1
- package/build-module/form/primitives/autocomplete/empty.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/index.mjs +3 -1
- package/build-module/form/primitives/autocomplete/index.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/item.mjs +1 -1
- package/build-module/form/primitives/autocomplete/item.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/list-body.mjs +1 -1
- package/build-module/form/primitives/autocomplete/list-body.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/list.mjs +1 -1
- package/build-module/form/primitives/autocomplete/list.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/popup.mjs +14 -31
- package/build-module/form/primitives/autocomplete/popup.mjs.map +3 -3
- package/build-module/form/primitives/autocomplete/portal.mjs +10 -2
- package/build-module/form/primitives/autocomplete/portal.mjs.map +2 -2
- package/build-module/form/primitives/autocomplete/positioner.mjs +123 -0
- package/build-module/form/primitives/autocomplete/positioner.mjs.map +7 -0
- package/build-module/form/primitives/constants.mjs.map +2 -2
- package/build-module/form/primitives/select/index.mjs +3 -1
- package/build-module/form/primitives/select/index.mjs.map +2 -2
- package/build-module/form/primitives/select/item.mjs +1 -1
- package/build-module/form/primitives/select/item.mjs.map +2 -2
- package/build-module/form/primitives/select/popup.mjs +18 -36
- package/build-module/form/primitives/select/popup.mjs.map +3 -3
- package/build-module/form/primitives/select/portal.mjs +11 -5
- package/build-module/form/primitives/select/portal.mjs.map +2 -2
- package/build-module/form/primitives/select/positioner.mjs +124 -0
- package/build-module/form/primitives/select/positioner.mjs.map +7 -0
- package/build-module/icon-button/icon-button.mjs +1 -1
- package/build-module/icon-button/icon-button.mjs.map +2 -2
- package/build-module/index.mjs +5 -1
- package/build-module/index.mjs.map +2 -2
- package/build-module/popover/index.mjs +2 -0
- package/build-module/popover/index.mjs.map +2 -2
- package/build-module/popover/popup.mjs +23 -51
- package/build-module/popover/popup.mjs.map +3 -3
- package/build-module/popover/portal.mjs.map +2 -2
- package/build-module/popover/positioner.mjs +133 -0
- package/build-module/popover/positioner.mjs.map +7 -0
- package/build-module/popover/root.mjs.map +2 -2
- package/build-module/tooltip/portal.mjs +10 -2
- package/build-module/tooltip/portal.mjs.map +2 -2
- package/build-module/tooltip/positioner.mjs.map +2 -2
- package/build-module/utils/use-enable-wp-compat-overlay-slot.mjs +14 -0
- package/build-module/utils/use-enable-wp-compat-overlay-slot.mjs.map +7 -0
- package/build-module/utils/wp-compat-overlay-slot.mjs +148 -0
- package/build-module/utils/wp-compat-overlay-slot.mjs.map +7 -0
- package/build-types/alert-dialog/portal.d.ts +8 -5
- package/build-types/alert-dialog/portal.d.ts.map +1 -1
- package/build-types/alert-dialog/types.d.ts +2 -2
- package/build-types/alert-dialog/types.d.ts.map +1 -1
- package/build-types/badge/stories/e2e/index.story.d.ts +7 -0
- package/build-types/badge/stories/e2e/index.story.d.ts.map +1 -0
- package/build-types/button/stories/e2e/index.story.d.ts +8 -0
- package/build-types/button/stories/e2e/index.story.d.ts.map +1 -0
- package/build-types/card/full-bleed.d.ts +18 -0
- package/build-types/card/full-bleed.d.ts.map +1 -1
- package/build-types/card/stories/index.story.d.ts +23 -0
- package/build-types/card/stories/index.story.d.ts.map +1 -1
- package/build-types/collapsible-card/header.d.ts +2 -0
- package/build-types/collapsible-card/header.d.ts.map +1 -1
- package/build-types/collapsible-card/stories/index.story.d.ts +14 -0
- package/build-types/collapsible-card/stories/index.story.d.ts.map +1 -1
- package/build-types/dialog/portal.d.ts +8 -6
- package/build-types/dialog/portal.d.ts.map +1 -1
- package/build-types/dialog/types.d.ts +2 -2
- package/build-types/dialog/types.d.ts.map +1 -1
- package/build-types/drawer/portal.d.ts +8 -6
- package/build-types/drawer/portal.d.ts.map +1 -1
- package/build-types/drawer/types.d.ts +2 -2
- package/build-types/drawer/types.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/clear.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/index.d.ts +2 -1
- package/build-types/form/primitives/autocomplete/index.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/popup.d.ts +1 -0
- package/build-types/form/primitives/autocomplete/popup.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/portal.d.ts +9 -4
- package/build-types/form/primitives/autocomplete/portal.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/positioner.d.ts +12 -0
- package/build-types/form/primitives/autocomplete/positioner.d.ts.map +1 -0
- package/build-types/form/primitives/autocomplete/stories/index.story.d.ts.map +1 -1
- package/build-types/form/primitives/autocomplete/types.d.ts +11 -9
- package/build-types/form/primitives/autocomplete/types.d.ts.map +1 -1
- package/build-types/form/primitives/constants.d.ts +9 -4
- package/build-types/form/primitives/constants.d.ts.map +1 -1
- package/build-types/form/primitives/select/index.d.ts +2 -1
- package/build-types/form/primitives/select/index.d.ts.map +1 -1
- package/build-types/form/primitives/select/popup.d.ts +1 -0
- package/build-types/form/primitives/select/popup.d.ts.map +1 -1
- package/build-types/form/primitives/select/portal.d.ts +9 -4
- package/build-types/form/primitives/select/portal.d.ts.map +1 -1
- package/build-types/form/primitives/select/positioner.d.ts +12 -0
- package/build-types/form/primitives/select/positioner.d.ts.map +1 -0
- package/build-types/form/primitives/select/stories/index.story.d.ts.map +1 -1
- package/build-types/form/primitives/select/types.d.ts +11 -2
- package/build-types/form/primitives/select/types.d.ts.map +1 -1
- package/build-types/icon/stories/index.story.d.ts.map +1 -1
- package/build-types/index.d.ts +2 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/popover/index.d.ts +2 -1
- package/build-types/popover/index.d.ts.map +1 -1
- package/build-types/popover/popup.d.ts +2 -1
- package/build-types/popover/popup.d.ts.map +1 -1
- package/build-types/popover/portal.d.ts +8 -5
- package/build-types/popover/portal.d.ts.map +1 -1
- package/build-types/popover/positioner.d.ts +12 -0
- package/build-types/popover/positioner.d.ts.map +1 -0
- package/build-types/popover/root.d.ts +5 -2
- package/build-types/popover/root.d.ts.map +1 -1
- package/build-types/popover/stories/index.story.d.ts +10 -21
- package/build-types/popover/stories/index.story.d.ts.map +1 -1
- package/build-types/popover/types.d.ts +12 -3
- package/build-types/popover/types.d.ts.map +1 -1
- package/build-types/tooltip/portal.d.ts +9 -4
- package/build-types/tooltip/portal.d.ts.map +1 -1
- package/build-types/tooltip/positioner.d.ts +8 -5
- package/build-types/tooltip/positioner.d.ts.map +1 -1
- package/build-types/tooltip/types.d.ts +3 -3
- package/build-types/tooltip/types.d.ts.map +1 -1
- package/build-types/utils/test/use-enable-wp-compat-overlay-slot.test.d.ts +2 -0
- package/build-types/utils/test/use-enable-wp-compat-overlay-slot.test.d.ts.map +1 -0
- package/build-types/utils/test/wp-compat-overlay-slot.test.d.ts +2 -0
- package/build-types/utils/test/wp-compat-overlay-slot.test.d.ts.map +1 -0
- package/build-types/utils/use-enable-wp-compat-overlay-slot.d.ts +17 -0
- package/build-types/utils/use-enable-wp-compat-overlay-slot.d.ts.map +1 -0
- package/build-types/utils/wp-compat-overlay-slot.d.ts +30 -0
- package/build-types/utils/wp-compat-overlay-slot.d.ts.map +1 -0
- package/package.json +12 -12
- package/src/alert-dialog/portal.tsx +1 -4
- package/src/alert-dialog/types.ts +2 -4
- package/src/badge/stories/e2e/index.story.tsx +20 -0
- package/src/button/stories/e2e/index.story.tsx +130 -0
- package/src/button/stories/index.story.tsx +1 -1
- package/src/button/style.module.css +17 -12
- package/src/card/full-bleed.tsx +18 -0
- package/src/card/stories/index.story.tsx +115 -1
- package/src/card/style.module.css +16 -0
- package/src/card/test/index.test.tsx +18 -1
- package/src/collapsible-card/header.tsx +2 -0
- package/src/collapsible-card/stories/index.story.tsx +66 -0
- package/src/dialog/portal.tsx +1 -5
- package/src/dialog/types.ts +2 -2
- package/src/drawer/portal.tsx +1 -5
- package/src/drawer/types.ts +2 -2
- package/src/form/primitives/autocomplete/clear.tsx +10 -4
- package/src/form/primitives/autocomplete/index.ts +2 -1
- package/src/form/primitives/autocomplete/popup.tsx +17 -21
- package/src/form/primitives/autocomplete/portal.tsx +11 -5
- package/src/form/primitives/autocomplete/positioner.tsx +29 -0
- package/src/form/primitives/autocomplete/stories/index.story.tsx +1 -0
- package/src/form/primitives/autocomplete/test/index.test.tsx +219 -0
- package/src/form/primitives/autocomplete/types.ts +15 -15
- package/src/form/primitives/constants.ts +7 -4
- package/src/form/primitives/select/index.ts +2 -1
- package/src/form/primitives/select/popup.tsx +30 -34
- package/src/form/primitives/select/portal.tsx +15 -8
- package/src/form/primitives/select/positioner.tsx +33 -0
- package/src/form/primitives/select/stories/index.story.tsx +1 -0
- package/src/form/primitives/select/test/index.test.tsx +134 -0
- package/src/form/primitives/select/types.ts +12 -2
- package/src/form/select-control/test/index.test.tsx +64 -0
- package/src/icon/stories/index.story.tsx +3 -2
- package/src/icon-button/icon-button.tsx +1 -1
- package/src/icon-button/test/index.test.tsx +10 -10
- package/src/index.ts +2 -0
- package/src/popover/index.ts +12 -1
- package/src/popover/popup.tsx +28 -45
- package/src/popover/portal.tsx +1 -4
- package/src/popover/positioner.tsx +42 -0
- package/src/popover/root.tsx +5 -2
- package/src/popover/stories/index.story.tsx +85 -138
- package/src/popover/test/index.test.tsx +36 -1
- package/src/popover/types.ts +13 -15
- package/src/tabs/stories/index.story.tsx +2 -2
- package/src/tooltip/portal.tsx +11 -5
- package/src/tooltip/positioner.tsx +1 -4
- package/src/tooltip/style.module.css +1 -1
- package/src/tooltip/test/index.test.tsx +110 -0
- package/src/tooltip/types.ts +3 -5
- package/src/utils/css/item-popup.module.css +7 -4
- package/src/utils/css/wp-compat-overlay-slot.module.css +35 -0
- package/src/utils/test/use-enable-wp-compat-overlay-slot.test.tsx +74 -0
- package/src/utils/test/wp-compat-overlay-slot.test.ts +300 -0
- package/src/utils/use-enable-wp-compat-overlay-slot.ts +32 -0
- package/src/utils/wp-compat-overlay-slot.ts +129 -0
|
@@ -25,6 +25,8 @@ import type { HeaderProps } from './types';
|
|
|
25
25
|
* Avoid placing interactive elements (buttons, links, inputs) inside the
|
|
26
26
|
* header, since the entire area is clickable and their events will bubble
|
|
27
27
|
* to trigger the collapse toggle.
|
|
28
|
+
*
|
|
29
|
+
* Place full-bleed media in `CollapsibleCard.Content`, not the header.
|
|
28
30
|
*/
|
|
29
31
|
export const Header = forwardRef< HTMLDivElement, HeaderProps >(
|
|
30
32
|
function CollapsibleCardHeader(
|
|
@@ -33,6 +33,7 @@ const meta: Meta< typeof CollapsibleCard.Root > = {
|
|
|
33
33
|
'CollapsibleCard.Header': CollapsibleCard.Header,
|
|
34
34
|
'CollapsibleCard.HeaderDescription': CollapsibleCard.HeaderDescription,
|
|
35
35
|
'CollapsibleCard.Content': CollapsibleCard.Content,
|
|
36
|
+
'Card.FullBleed': Card.FullBleed,
|
|
36
37
|
},
|
|
37
38
|
parameters: {
|
|
38
39
|
componentStatus: {
|
|
@@ -314,3 +315,68 @@ export const ComparedToCard: Story = {
|
|
|
314
315
|
</div>
|
|
315
316
|
),
|
|
316
317
|
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* When `Card.FullBleed` is the sole child of `CollapsibleCard.Content` and a
|
|
321
|
+
* header sits above it, the media bumps against the card's side and
|
|
322
|
+
* bottom edges while the header retains its normal padding. (Unlike a plain
|
|
323
|
+
* `Card`, a header is always required here for the collapse trigger — see
|
|
324
|
+
* `Card` stories for a body-only `FullBleedCoverOnly` example.)
|
|
325
|
+
*/
|
|
326
|
+
export const FullBleedCoverWithHeader: Story = {
|
|
327
|
+
argTypes: { open: { control: false } },
|
|
328
|
+
args: {
|
|
329
|
+
defaultOpen: true,
|
|
330
|
+
children: (
|
|
331
|
+
<>
|
|
332
|
+
<CollapsibleCard.Header>
|
|
333
|
+
<Card.Title>Card title</Card.Title>
|
|
334
|
+
</CollapsibleCard.Header>
|
|
335
|
+
<CollapsibleCard.Content>
|
|
336
|
+
<Card.FullBleed>
|
|
337
|
+
<div
|
|
338
|
+
style={ {
|
|
339
|
+
height: 180,
|
|
340
|
+
background:
|
|
341
|
+
'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
|
342
|
+
} }
|
|
343
|
+
/>
|
|
344
|
+
</Card.FullBleed>
|
|
345
|
+
</CollapsibleCard.Content>
|
|
346
|
+
</>
|
|
347
|
+
),
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* `Card.FullBleed` breaks out of the content padding to span edge-to-edge.
|
|
353
|
+
* Useful for images, dividers, or embedded content inside the collapsible
|
|
354
|
+
* region.
|
|
355
|
+
*/
|
|
356
|
+
export const WithFullBleed: Story = {
|
|
357
|
+
argTypes: { open: { control: false } },
|
|
358
|
+
args: {
|
|
359
|
+
defaultOpen: true,
|
|
360
|
+
children: (
|
|
361
|
+
<>
|
|
362
|
+
<CollapsibleCard.Header>
|
|
363
|
+
<Card.Title>Featured image</Card.Title>
|
|
364
|
+
</CollapsibleCard.Header>
|
|
365
|
+
<CollapsibleCard.Content
|
|
366
|
+
render={ <Stack direction="column" gap="lg" /> }
|
|
367
|
+
>
|
|
368
|
+
<Card.FullBleed>
|
|
369
|
+
<div
|
|
370
|
+
style={ {
|
|
371
|
+
height: 160,
|
|
372
|
+
background:
|
|
373
|
+
'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
374
|
+
} }
|
|
375
|
+
/>
|
|
376
|
+
</Card.FullBleed>
|
|
377
|
+
<Text>Content below the full-bleed area.</Text>
|
|
378
|
+
</CollapsibleCard.Content>
|
|
379
|
+
</>
|
|
380
|
+
),
|
|
381
|
+
},
|
|
382
|
+
};
|
package/src/dialog/portal.tsx
CHANGED
|
@@ -3,11 +3,7 @@ import { forwardRef } from '@wordpress/element';
|
|
|
3
3
|
import type { PortalProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* `Popup`, etc.) outside the DOM hierarchy. Pass to `Dialog.Popup`'s
|
|
8
|
-
* `portal` prop to customize `container`, `className`, `style`, and other
|
|
9
|
-
* Base UI portal options. When `portal` is omitted, `Dialog.Popup` uses this
|
|
10
|
-
* component with default props.
|
|
6
|
+
* Used to apply custom portal behavior to `Dialog`'s overlay content.
|
|
11
7
|
*/
|
|
12
8
|
const Portal = forwardRef< HTMLDivElement, PortalProps >(
|
|
13
9
|
function DialogPortal( props, ref ) {
|
package/src/dialog/types.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Dialog as _Dialog } from '@base-ui/react/dialog';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
import type { Button } from '../button';
|
|
5
5
|
import type { IconButton } from '../icon-button';
|
|
6
6
|
import type { ComponentProps } from '../utils/types';
|
|
7
7
|
|
|
8
|
-
export type PortalProps =
|
|
8
|
+
export type PortalProps = ComponentProps< typeof _Dialog.Portal >;
|
|
9
9
|
|
|
10
10
|
export interface RootProps
|
|
11
11
|
extends Pick<
|
package/src/drawer/portal.tsx
CHANGED
|
@@ -3,11 +3,7 @@ import { forwardRef } from '@wordpress/element';
|
|
|
3
3
|
import type { PortalProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* with the inner `Popup`, etc.) outside the DOM hierarchy. Pass to
|
|
8
|
-
* `Drawer.Popup`'s `portal` prop to customize `container`, `className`,
|
|
9
|
-
* `style`, and other Base UI portal options. When `portal` is omitted,
|
|
10
|
-
* `Drawer.Popup` uses this component with default props.
|
|
6
|
+
* Used to apply custom portal behavior to `Drawer`'s overlay content.
|
|
11
7
|
*/
|
|
12
8
|
const Portal = forwardRef< HTMLDivElement, PortalProps >(
|
|
13
9
|
function DrawerPortal( props, ref ) {
|
package/src/drawer/types.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Drawer as _Drawer } from '@base-ui/react/drawer';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
3
3
|
import type { Button } from '../button';
|
|
4
4
|
import type { IconButton } from '../icon-button';
|
|
5
5
|
import type { ComponentProps } from '../utils/types';
|
|
6
6
|
|
|
7
|
-
export type PortalProps =
|
|
7
|
+
export type PortalProps = ComponentProps< typeof _Drawer.Portal >;
|
|
8
8
|
|
|
9
9
|
export interface RootProps
|
|
10
10
|
extends Pick<
|
|
@@ -5,12 +5,18 @@ import { __ } from '@wordpress/i18n';
|
|
|
5
5
|
import { IconButton } from '../../../icon-button';
|
|
6
6
|
import type { AutocompleteClearProps } from './types';
|
|
7
7
|
|
|
8
|
-
const DEFAULT_RENDER = (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const DEFAULT_RENDER = (
|
|
9
|
+
{
|
|
10
|
+
'aria-label': ariaLabel = __( 'Clear' ),
|
|
11
|
+
...props
|
|
12
|
+
}: AutocompleteClearProps,
|
|
13
|
+
{ disabled }: _Autocomplete.Clear.State
|
|
14
|
+
) => (
|
|
12
15
|
<IconButton
|
|
13
16
|
icon={ closeSmall }
|
|
17
|
+
focusableWhenDisabled={ false }
|
|
18
|
+
disabled={ disabled }
|
|
19
|
+
aria-hidden={ disabled || undefined }
|
|
14
20
|
size="small"
|
|
15
21
|
variant="minimal"
|
|
16
22
|
tone="neutral"
|
|
@@ -6,7 +6,8 @@ export { Input } from './input';
|
|
|
6
6
|
export { Item } from './item';
|
|
7
7
|
export { List } from './list';
|
|
8
8
|
export { ListBody } from './list-body';
|
|
9
|
-
export { Portal } from './portal';
|
|
10
9
|
export { Popup } from './popup';
|
|
10
|
+
export { Portal } from './portal';
|
|
11
|
+
export { Positioner } from './positioner';
|
|
11
12
|
export { Root } from './root';
|
|
12
13
|
export { Value } from './value';
|
|
@@ -8,35 +8,31 @@ import {
|
|
|
8
8
|
import { unlock } from '../../../lock-unlock';
|
|
9
9
|
import { renderSlotWithChildren } from '../../../utils/render-slot-with-children';
|
|
10
10
|
import itemPopupStyles from '../../../utils/css/item-popup.module.css';
|
|
11
|
-
import resetStyles from '../../../utils/css/resets.module.css';
|
|
12
|
-
import styles from './style.module.css';
|
|
13
11
|
import { Portal } from './portal';
|
|
12
|
+
import { Positioner } from './positioner';
|
|
14
13
|
import type { AutocompletePopupProps } from './types';
|
|
15
|
-
import { ITEM_POPUP_POSITIONER_PROPS } from '../constants';
|
|
16
14
|
|
|
17
15
|
const ThemeProvider: typeof ThemeProviderType =
|
|
18
16
|
unlock( themePrivateApis ).ThemeProvider;
|
|
19
17
|
|
|
20
18
|
export const Popup = forwardRef< HTMLDivElement, AutocompletePopupProps >(
|
|
21
|
-
function Popup( { className, portal, ...restProps }, ref ) {
|
|
22
|
-
const
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
>
|
|
30
|
-
<ThemeProvider>
|
|
31
|
-
<_Autocomplete.Popup
|
|
32
|
-
ref={ ref }
|
|
33
|
-
className={ clsx( itemPopupStyles.popup, className ) }
|
|
34
|
-
{ ...restProps }
|
|
35
|
-
/>
|
|
36
|
-
</ThemeProvider>
|
|
37
|
-
</_Autocomplete.Positioner>
|
|
19
|
+
function Popup( { className, portal, positioner, ...restProps }, ref ) {
|
|
20
|
+
const popupContent = (
|
|
21
|
+
<ThemeProvider>
|
|
22
|
+
<_Autocomplete.Popup
|
|
23
|
+
ref={ ref }
|
|
24
|
+
className={ clsx( itemPopupStyles.popup, className ) }
|
|
25
|
+
{ ...restProps }
|
|
26
|
+
/>
|
|
27
|
+
</ThemeProvider>
|
|
38
28
|
);
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
const positionedPopup = renderSlotWithChildren(
|
|
31
|
+
positioner,
|
|
32
|
+
<Positioner />,
|
|
33
|
+
popupContent
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return renderSlotWithChildren( portal, <Portal />, positionedPopup );
|
|
41
37
|
}
|
|
42
38
|
);
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { Autocomplete as _Autocomplete } from '@base-ui/react/autocomplete';
|
|
2
2
|
import { forwardRef } from '@wordpress/element';
|
|
3
3
|
import type { PortalProps } from './types';
|
|
4
|
+
import { getWpCompatOverlaySlot } from '../../../utils/wp-compat-overlay-slot';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
* `Autocomplete.Popup` uses this component with default props.
|
|
7
|
+
* Used to apply custom portal behavior to `Autocomplete`'s popup content.
|
|
8
|
+
* `container` defaults to the `@wordpress/ui` compat overlay slot.
|
|
9
9
|
*/
|
|
10
10
|
const Portal = forwardRef< HTMLDivElement, PortalProps >(
|
|
11
|
-
function AutocompletePortal(
|
|
12
|
-
return
|
|
11
|
+
function AutocompletePortal( { container, ...restProps }, ref ) {
|
|
12
|
+
return (
|
|
13
|
+
<_Autocomplete.Portal
|
|
14
|
+
container={ container ?? getWpCompatOverlaySlot() }
|
|
15
|
+
{ ...restProps }
|
|
16
|
+
ref={ ref }
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
13
19
|
}
|
|
14
20
|
);
|
|
15
21
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { Autocomplete as _Autocomplete } from '@base-ui/react/autocomplete';
|
|
3
|
+
import { forwardRef } from '@wordpress/element';
|
|
4
|
+
import type { PositionerProps } from './types';
|
|
5
|
+
import resetStyles from '../../../utils/css/resets.module.css';
|
|
6
|
+
import styles from './style.module.css';
|
|
7
|
+
import { ITEM_POPUP_POSITIONER_PROPS } from '../constants';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Used to apply custom positioning to `Autocomplete`'s popup content.
|
|
11
|
+
*/
|
|
12
|
+
const Positioner = forwardRef< HTMLDivElement, PositionerProps >(
|
|
13
|
+
function AutocompletePositioner( { className, ...props }, ref ) {
|
|
14
|
+
return (
|
|
15
|
+
<_Autocomplete.Positioner
|
|
16
|
+
{ ...ITEM_POPUP_POSITIONER_PROPS }
|
|
17
|
+
{ ...props }
|
|
18
|
+
ref={ ref }
|
|
19
|
+
className={ clsx(
|
|
20
|
+
resetStyles[ 'box-sizing' ],
|
|
21
|
+
styles.positioner,
|
|
22
|
+
className
|
|
23
|
+
) }
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export { Positioner };
|
|
@@ -13,6 +13,7 @@ const meta: Meta< typeof Autocomplete.Root > = {
|
|
|
13
13
|
component: Autocomplete.Root,
|
|
14
14
|
subcomponents: {
|
|
15
15
|
'Autocomplete.Portal': Autocomplete.Portal,
|
|
16
|
+
'Autocomplete.Positioner': Autocomplete.Positioner,
|
|
16
17
|
'Autocomplete.Popup': Autocomplete.Popup,
|
|
17
18
|
'Autocomplete.Input': Autocomplete.Input,
|
|
18
19
|
'Autocomplete.InputGroup': Autocomplete.InputGroup,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { createRef } from '@wordpress/element';
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
4
5
|
import * as Autocomplete from '../index';
|
|
6
|
+
import { useEnableWpCompatOverlaySlot } from '../../../../utils/use-enable-wp-compat-overlay-slot';
|
|
5
7
|
|
|
6
8
|
const ITEMS = [
|
|
7
9
|
{ id: '1', value: 'Item 1' },
|
|
@@ -9,6 +11,15 @@ const ITEMS = [
|
|
|
9
11
|
{ id: '3', value: 'Item 3' },
|
|
10
12
|
];
|
|
11
13
|
|
|
14
|
+
function renderDisabledAutocompleteWithClear() {
|
|
15
|
+
return render(
|
|
16
|
+
<Autocomplete.Root items={ ITEMS } disabled defaultValue="Item 1">
|
|
17
|
+
<Autocomplete.Input placeholder="Search" />
|
|
18
|
+
<Autocomplete.Clear />
|
|
19
|
+
</Autocomplete.Root>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
describe( 'Autocomplete', () => {
|
|
13
24
|
it( 'forwards ref', async () => {
|
|
14
25
|
const user = userEvent.setup();
|
|
@@ -159,4 +170,212 @@ describe( 'Autocomplete', () => {
|
|
|
159
170
|
);
|
|
160
171
|
} );
|
|
161
172
|
} );
|
|
173
|
+
|
|
174
|
+
describe( 'positioner', () => {
|
|
175
|
+
it( 'should render the custom positioner element wrapping the popup content', async () => {
|
|
176
|
+
const user = userEvent.setup();
|
|
177
|
+
|
|
178
|
+
render(
|
|
179
|
+
<Autocomplete.Root items={ ITEMS }>
|
|
180
|
+
<Autocomplete.Input placeholder="Search" />
|
|
181
|
+
<Autocomplete.Popup
|
|
182
|
+
positioner={
|
|
183
|
+
<Autocomplete.Positioner data-testid="custom-positioner" />
|
|
184
|
+
}
|
|
185
|
+
>
|
|
186
|
+
<Autocomplete.List>
|
|
187
|
+
<Autocomplete.ListBody>
|
|
188
|
+
<Autocomplete.Collection>
|
|
189
|
+
{ ( item ) => (
|
|
190
|
+
<Autocomplete.Item
|
|
191
|
+
key={ item.id }
|
|
192
|
+
value={ item }
|
|
193
|
+
>
|
|
194
|
+
{ item.value }
|
|
195
|
+
</Autocomplete.Item>
|
|
196
|
+
) }
|
|
197
|
+
</Autocomplete.Collection>
|
|
198
|
+
</Autocomplete.ListBody>
|
|
199
|
+
</Autocomplete.List>
|
|
200
|
+
</Autocomplete.Popup>
|
|
201
|
+
</Autocomplete.Root>
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
await user.type( screen.getByRole( 'combobox' ), 'Item 1' );
|
|
205
|
+
|
|
206
|
+
const item = await screen.findByRole( 'option', {
|
|
207
|
+
name: 'Item 1',
|
|
208
|
+
} );
|
|
209
|
+
const positioner = screen.getByTestId( 'custom-positioner' );
|
|
210
|
+
|
|
211
|
+
expect( positioner ).toContainElement( item );
|
|
212
|
+
} );
|
|
213
|
+
} );
|
|
214
|
+
|
|
215
|
+
// Slot is identified by a data attribute, not a user-facing role/text.
|
|
216
|
+
/* eslint-disable testing-library/no-node-access */
|
|
217
|
+
describe( 'wp compat overlay slot', () => {
|
|
218
|
+
const SLOT_SELECTOR = '[data-wp-compat-overlay-slot]';
|
|
219
|
+
|
|
220
|
+
// Exercises the public opt-in path rather than poking the flag.
|
|
221
|
+
function WithSlotEnabled( { children }: { children: ReactNode } ) {
|
|
222
|
+
useEnableWpCompatOverlaySlot();
|
|
223
|
+
return <>{ children }</>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
afterEach( () => {
|
|
227
|
+
// The hook is one-way at runtime; reset explicitly between tests.
|
|
228
|
+
delete ( window as { __wpUiCompatOverlaySlotEnabled?: boolean } )
|
|
229
|
+
.__wpUiCompatOverlaySlotEnabled;
|
|
230
|
+
document
|
|
231
|
+
.querySelectorAll( SLOT_SELECTOR )
|
|
232
|
+
.forEach( ( el ) => el.remove() );
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
it( 'portals the popup into the slot when the consumer opts in', async () => {
|
|
236
|
+
const user = userEvent.setup();
|
|
237
|
+
|
|
238
|
+
render(
|
|
239
|
+
<WithSlotEnabled>
|
|
240
|
+
<Autocomplete.Root items={ ITEMS }>
|
|
241
|
+
<Autocomplete.Input placeholder="Search" />
|
|
242
|
+
<Autocomplete.Popup>
|
|
243
|
+
<Autocomplete.List>
|
|
244
|
+
<Autocomplete.ListBody>
|
|
245
|
+
<Autocomplete.Collection>
|
|
246
|
+
{ ( item ) => (
|
|
247
|
+
<Autocomplete.Item
|
|
248
|
+
key={ item.id }
|
|
249
|
+
value={ item }
|
|
250
|
+
>
|
|
251
|
+
{ item.value }
|
|
252
|
+
</Autocomplete.Item>
|
|
253
|
+
) }
|
|
254
|
+
</Autocomplete.Collection>
|
|
255
|
+
</Autocomplete.ListBody>
|
|
256
|
+
</Autocomplete.List>
|
|
257
|
+
</Autocomplete.Popup>
|
|
258
|
+
</Autocomplete.Root>
|
|
259
|
+
</WithSlotEnabled>
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
await user.type( screen.getByRole( 'combobox' ), 'Item 1' );
|
|
263
|
+
|
|
264
|
+
const item = await screen.findByRole( 'option', {
|
|
265
|
+
name: 'Item 1',
|
|
266
|
+
} );
|
|
267
|
+
expect( item ).toBeVisible();
|
|
268
|
+
|
|
269
|
+
const slot = document.querySelector( SLOT_SELECTOR );
|
|
270
|
+
expect( slot ).not.toBeNull();
|
|
271
|
+
expect( slot ).toContainElement( item );
|
|
272
|
+
} );
|
|
273
|
+
|
|
274
|
+
it( 'does not create a slot when the consumer has not opted in (dormant default)', async () => {
|
|
275
|
+
const user = userEvent.setup();
|
|
276
|
+
|
|
277
|
+
render(
|
|
278
|
+
<Autocomplete.Root items={ ITEMS }>
|
|
279
|
+
<Autocomplete.Input placeholder="Search" />
|
|
280
|
+
<Autocomplete.Popup>
|
|
281
|
+
<Autocomplete.List>
|
|
282
|
+
<Autocomplete.ListBody>
|
|
283
|
+
<Autocomplete.Collection>
|
|
284
|
+
{ ( item ) => (
|
|
285
|
+
<Autocomplete.Item
|
|
286
|
+
key={ item.id }
|
|
287
|
+
value={ item }
|
|
288
|
+
>
|
|
289
|
+
{ item.value }
|
|
290
|
+
</Autocomplete.Item>
|
|
291
|
+
) }
|
|
292
|
+
</Autocomplete.Collection>
|
|
293
|
+
</Autocomplete.ListBody>
|
|
294
|
+
</Autocomplete.List>
|
|
295
|
+
</Autocomplete.Popup>
|
|
296
|
+
</Autocomplete.Root>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
await user.type( screen.getByRole( 'combobox' ), 'Item 1' );
|
|
300
|
+
|
|
301
|
+
const item = await screen.findByRole( 'option', {
|
|
302
|
+
name: 'Item 1',
|
|
303
|
+
} );
|
|
304
|
+
expect( item ).toBeVisible();
|
|
305
|
+
|
|
306
|
+
expect( document.querySelector( SLOT_SELECTOR ) ).toBeNull();
|
|
307
|
+
} );
|
|
308
|
+
|
|
309
|
+
it( 'lets a caller-supplied portal container override the slot', async () => {
|
|
310
|
+
const user = userEvent.setup();
|
|
311
|
+
const containerRef = createRef< HTMLDivElement >();
|
|
312
|
+
|
|
313
|
+
render(
|
|
314
|
+
<WithSlotEnabled>
|
|
315
|
+
<Autocomplete.Root items={ ITEMS }>
|
|
316
|
+
<Autocomplete.Input placeholder="Search" />
|
|
317
|
+
<div
|
|
318
|
+
ref={ containerRef }
|
|
319
|
+
data-testid="custom-container"
|
|
320
|
+
/>
|
|
321
|
+
<Autocomplete.Popup
|
|
322
|
+
portal={
|
|
323
|
+
<Autocomplete.Portal
|
|
324
|
+
container={ containerRef }
|
|
325
|
+
/>
|
|
326
|
+
}
|
|
327
|
+
>
|
|
328
|
+
<Autocomplete.List>
|
|
329
|
+
<Autocomplete.ListBody>
|
|
330
|
+
<Autocomplete.Collection>
|
|
331
|
+
{ ( item ) => (
|
|
332
|
+
<Autocomplete.Item
|
|
333
|
+
key={ item.id }
|
|
334
|
+
value={ item }
|
|
335
|
+
>
|
|
336
|
+
{ item.value }
|
|
337
|
+
</Autocomplete.Item>
|
|
338
|
+
) }
|
|
339
|
+
</Autocomplete.Collection>
|
|
340
|
+
</Autocomplete.ListBody>
|
|
341
|
+
</Autocomplete.List>
|
|
342
|
+
</Autocomplete.Popup>
|
|
343
|
+
</Autocomplete.Root>
|
|
344
|
+
</WithSlotEnabled>
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
await user.type( screen.getByRole( 'combobox' ), 'Item 1' );
|
|
348
|
+
|
|
349
|
+
const item = await screen.findByRole( 'option', {
|
|
350
|
+
name: 'Item 1',
|
|
351
|
+
} );
|
|
352
|
+
expect( item ).toBeVisible();
|
|
353
|
+
expect( screen.getByTestId( 'custom-container' ) ).toContainElement(
|
|
354
|
+
item
|
|
355
|
+
);
|
|
356
|
+
} );
|
|
357
|
+
} );
|
|
358
|
+
/* eslint-enable testing-library/no-node-access */
|
|
359
|
+
|
|
360
|
+
describe( 'when disabled', () => {
|
|
361
|
+
it( 'hides the clear button from screen readers', () => {
|
|
362
|
+
renderDisabledAutocompleteWithClear();
|
|
363
|
+
|
|
364
|
+
expect(
|
|
365
|
+
screen.queryByRole( 'button', { name: 'Clear' } )
|
|
366
|
+
).not.toBeInTheDocument();
|
|
367
|
+
} );
|
|
368
|
+
|
|
369
|
+
it( 'does not show a tooltip when the clear button is hovered', async () => {
|
|
370
|
+
const user = userEvent.setup( { pointerEventsCheck: 0 } );
|
|
371
|
+
renderDisabledAutocompleteWithClear();
|
|
372
|
+
|
|
373
|
+
const clearButton = screen.getByLabelText( 'Clear', {
|
|
374
|
+
selector: 'button',
|
|
375
|
+
} );
|
|
376
|
+
await user.hover( clearButton );
|
|
377
|
+
|
|
378
|
+
expect( screen.queryByRole( 'tooltip' ) ).not.toBeInTheDocument();
|
|
379
|
+
} );
|
|
380
|
+
} );
|
|
162
381
|
} );
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Autocomplete as _Autocomplete } from '@base-ui/react/autocomplete';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
3
|
import type { ComponentProps } from '../../../utils/types';
|
|
4
4
|
|
|
5
5
|
export type AutocompleteCollectionProps = _Autocomplete.Collection.Props;
|
|
6
6
|
|
|
7
|
-
export type PortalProps =
|
|
8
|
-
|
|
9
|
-
>;
|
|
7
|
+
export type PortalProps = ComponentProps< typeof _Autocomplete.Portal >;
|
|
8
|
+
|
|
9
|
+
export type PositionerProps = ComponentProps< typeof _Autocomplete.Positioner >;
|
|
10
10
|
|
|
11
11
|
export type AutocompleteClearProps = ComponentProps<
|
|
12
12
|
typeof _Autocomplete.Clear
|
|
@@ -58,17 +58,17 @@ export type AutocompletePopupProps = ComponentProps<
|
|
|
58
58
|
* portal element; they would be ignored.
|
|
59
59
|
*/
|
|
60
60
|
portal?: ReactElement< Omit< PortalProps, 'children' > >;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export type AutocompleteRootProps = ComponentProps<
|
|
64
|
-
typeof _Autocomplete.Root
|
|
65
|
-
> & {
|
|
66
|
-
children?: React.ReactNode;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export type AutocompleteValueProps = {
|
|
70
61
|
/**
|
|
71
|
-
*
|
|
62
|
+
* Optional positioner element, typically `<Autocomplete.Positioner />`
|
|
63
|
+
* with custom positioning props (`side`, `align`, `sideOffset`, collision
|
|
64
|
+
* settings, etc.). When omitted, `Autocomplete.Popup` uses
|
|
65
|
+
* `Autocomplete.Positioner` with default props. Do not pass `children` on
|
|
66
|
+
* the positioner element; they would be ignored.
|
|
72
67
|
*/
|
|
73
|
-
|
|
68
|
+
positioner?: ReactElement< Omit< PositionerProps, 'children' > >;
|
|
74
69
|
};
|
|
70
|
+
|
|
71
|
+
export type AutocompleteRootProps< Value = unknown > =
|
|
72
|
+
_Autocomplete.Root.Props< Value >;
|
|
73
|
+
|
|
74
|
+
export type AutocompleteValueProps = _Autocomplete.Value.Props;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { Select as _Select } from '@base-ui/react/select';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Shared positioning
|
|
2
|
+
* Shared positioning defaults for item-list popups. Consumed as individual
|
|
3
|
+
* default values by `Select.Positioner` and `Autocomplete.Positioner`. Each
|
|
4
|
+
* key is validated by the consuming positioner's prop types at the use
|
|
5
|
+
* site, so a value here that does not satisfy either Base UI positioner's
|
|
6
|
+
* type will surface as a type error in `select/positioner.tsx` or
|
|
7
|
+
* `autocomplete/positioner.tsx`.
|
|
5
8
|
*/
|
|
6
9
|
export const ITEM_POPUP_POSITIONER_PROPS = {
|
|
7
10
|
align: 'start',
|
|
8
11
|
sideOffset: 8,
|
|
9
12
|
collisionPadding: 12,
|
|
10
|
-
}
|
|
13
|
+
} as const;
|