@astryxdesign/core 0.7.1 → 0.7.2-canary.07f97c6
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/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +38 -14
- package/dist/Drawer/Drawer.d.ts +191 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -0
- package/dist/Drawer/Drawer.js +382 -0
- package/dist/Drawer/DrawerHeader.d.ts +85 -0
- package/dist/Drawer/DrawerHeader.d.ts.map +1 -0
- package/dist/Drawer/DrawerHeader.js +134 -0
- package/dist/Drawer/index.d.ts +11 -0
- package/dist/Drawer/index.d.ts.map +1 -0
- package/dist/Drawer/index.js +11 -0
- package/dist/Drawer/useDrawerDialogPresence.d.ts +42 -0
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -0
- package/dist/Drawer/useDrawerDialogPresence.js +236 -0
- package/dist/DropdownMenu/DropdownMenu.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +4 -4
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +10 -2
- package/dist/Layer/layerViewportInset.stylex.d.ts +6 -2
- package/dist/Layer/layerViewportInset.stylex.d.ts.map +1 -1
- package/dist/Layer/layerViewportInset.stylex.js +6 -2
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +13 -7
- package/dist/Layer/useLayerDismissal.d.ts +1 -1
- package/dist/Layer/useLayerDismissal.js +1 -1
- package/dist/Layout/container.stylex.d.ts +5 -5
- package/dist/Layout/container.stylex.js +4 -4
- package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -1
- package/dist/Markdown/plugins/frontmatter.js +7 -3
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +22 -8
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +100 -2
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +3 -1
- package/dist/Pagination/Pagination.d.ts +8 -5
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +21 -8
- package/dist/Popover/Popover.js +2 -2
- package/dist/PowerSearch/PowerSearch.js +1 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +6 -2
- package/dist/Switch/Switch.d.ts +1 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +13 -1
- package/dist/TabList/TabMenu.d.ts +2 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -3
- package/dist/Text/useTruncation.d.ts +7 -5
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +16 -5
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +11 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +26 -0
- package/dist/astryx.css +21 -10
- package/dist/hooks/useEntryAnimation.d.ts +2 -1
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +14 -12
- package/dist/hooks/useIsHydrating.d.ts +6 -0
- package/dist/hooks/useIsHydrating.d.ts.map +1 -0
- package/dist/hooks/useIsHydrating.js +30 -0
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +112 -1
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +112 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +0 -3
- package/dist/utils/focusOutline.stylex.d.ts +4 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +4 -0
- package/dist/utils/sharedMutationObserver.d.ts +21 -0
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -0
- package/dist/utils/sharedMutationObserver.js +69 -0
- package/locales/en.json +444 -0
- package/locales/pseudo.json +333 -0
- package/package.json +9 -4
- package/src/Button/Button.doc.mjs +2 -2
- package/src/Button/Button.test.tsx +155 -9
- package/src/Button/Button.tsx +39 -16
- package/src/Button/__tests__/Button.a11y.known-failures.ts +1 -62
- package/src/Button/__tests__/Button.a11y.states.ts +0 -10
- package/src/Drawer/Drawer.doc.mjs +472 -0
- package/src/Drawer/Drawer.spec.md +357 -0
- package/src/Drawer/Drawer.test.tsx +1300 -0
- package/src/Drawer/Drawer.tsx +613 -0
- package/src/Drawer/DrawerDirection.test.tsx +127 -0
- package/src/Drawer/DrawerHeader.doc.mjs +293 -0
- package/src/Drawer/DrawerHeader.test.tsx +102 -0
- package/src/Drawer/DrawerHeader.tsx +235 -0
- package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +401 -0
- package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +297 -0
- package/src/Drawer/index.ts +13 -0
- package/src/Drawer/useDrawerDialogPresence.ts +284 -0
- package/src/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +2 -2
- package/src/HoverCard/HoverCard.test.tsx +58 -0
- package/src/HoverCard/useHoverCard.tsx +10 -2
- package/src/IconButton/IconButton.doc.mjs +2 -2
- package/src/IconButton/IconButton.test.tsx +3 -1
- package/src/Layer/layerViewportInset.source-build.test.mjs +90 -0
- package/src/Layer/layerViewportInset.stylex.ts +6 -2
- package/src/Layer/layerViewportInset.test.ts +1 -0
- package/src/Layer/useLayer.tsx +24 -13
- package/src/Layer/useLayerDismissal.ts +1 -1
- package/src/Layout/container.stylex.ts +6 -6
- package/src/List/ListItem.doc.mjs +8 -5
- package/src/Markdown/plugins/frontmatter.test.tsx +25 -0
- package/src/Markdown/plugins/frontmatter.ts +7 -3
- package/src/MultiSelector/MultiSelector.test.tsx +159 -0
- package/src/MultiSelector/MultiSelector.tsx +40 -7
- package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +112 -2
- package/src/MultiSelector/hooks.ts +3 -1
- package/src/MultiSelector/useMultiCombobox.doc.mjs +267 -0
- package/src/Pagination/Pagination.doc.mjs +7 -7
- package/src/Pagination/Pagination.spec.md +9 -9
- package/src/Pagination/Pagination.test.tsx +242 -0
- package/src/Pagination/Pagination.tsx +27 -11
- package/src/Popover/Popover.test.tsx +2 -2
- package/src/Popover/Popover.tsx +4 -4
- package/src/PowerSearch/PowerSearch.test.tsx +85 -0
- package/src/PowerSearch/PowerSearch.tsx +2 -2
- package/src/Selector/hooks.ts +6 -2
- package/src/Selector/useCombobox.doc.mjs +237 -0
- package/src/Switch/Switch.test.tsx +49 -0
- package/src/Switch/Switch.tsx +11 -0
- package/src/TabList/TabList.test.tsx +67 -0
- package/src/TabList/TabMenu.doc.mjs +4 -3
- package/src/TabList/TabMenu.tsx +7 -4
- package/src/Text/useTruncation.test.tsx +69 -0
- package/src/Text/useTruncation.ts +16 -5
- package/src/Tokenizer/Tokenizer.test.tsx +54 -1
- package/src/Tooltip/Tooltip.test.tsx +31 -0
- package/src/Tooltip/useTooltip.test.tsx +13 -0
- package/src/Tooltip/useTooltip.tsx +11 -0
- package/src/Typeahead/BaseTypeahead.tsx +26 -0
- package/src/Typeahead/Typeahead.test.tsx +260 -2
- package/src/hooks/useEntryAnimation.doc.mjs +4 -4
- package/src/hooks/useEntryAnimation.ssr.test.tsx +143 -0
- package/src/hooks/useEntryAnimation.ts +15 -16
- package/src/hooks/useIsHydrating.test.tsx +70 -0
- package/src/hooks/useIsHydrating.ts +31 -0
- package/src/i18n/InternationalizationProvider.doc.mjs +8 -0
- package/src/i18n/InternationalizationProvider.spec.md +252 -0
- package/src/i18n/__tests__/InternationalizationProvider.test.tsx +128 -0
- package/src/i18n/generated-locales/en.generated.ts +121 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +121 -0
- package/src/i18n/useTranslator.test.tsx +37 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +0 -3
- package/src/theme/derivedVarRegistry.ts +0 -3
- package/src/utils/focusOutline.stylex.ts +4 -0
- package/src/utils/sharedMutationObserver.test.ts +53 -0
- package/src/utils/sharedMutationObserver.ts +78 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file Drawer.tsx
|
|
7
|
+
* @input Uses React, StyleX, theme tokens, the shared layer text/structure resets and content boundary, the Dialog purpose type, shared focus/dismissal/depth primitives, container padding lowering, scroll locking/dialog presence, BaseProps, merged refs/props, themeProps
|
|
8
|
+
* @output Exports Drawer component and DrawerProps
|
|
9
|
+
* @position Core implementation; consumed by index.ts, tested by Drawer.test.tsx, demonstrated in Storybook
|
|
10
|
+
*
|
|
11
|
+
* Overlay panel for inspectors and detail views — the "click a table row,
|
|
12
|
+
* see its details" pattern. Slides in from the inline start or end edge and
|
|
13
|
+
* floats above the page content: unlike a docked panel it never reflows the
|
|
14
|
+
* layout underneath, it overlays it (with or without a scrim).
|
|
15
|
+
*
|
|
16
|
+
* Inline axis only (start/end). Block-axis sheets are BottomSheet's job;
|
|
17
|
+
* a drawer is always a full-height side panel.
|
|
18
|
+
*
|
|
19
|
+
* Sizing is viewport-aware: `width` is the desktop budget, and below
|
|
20
|
+
* the mobile breakpoint it preserves a 56px reveal of the page behind, capped
|
|
21
|
+
* by the requested width (or fills the viewport with the experimental
|
|
22
|
+
* `experimental.isFullWidthOnMobile` option).
|
|
23
|
+
*
|
|
24
|
+
* Uses the native `<dialog>` element (same precedent as Dialog/MobileNav):
|
|
25
|
+
* - `showModal()` when `hasScrim` (default) — top-layer rendering, focus
|
|
26
|
+
* trapping, `::backdrop`, no z-index management.
|
|
27
|
+
* - `showPopover()` when `hasScrim={false}` — non-modal top-layer overlay;
|
|
28
|
+
* the page behind stays interactive (e.g. master-detail inspectors).
|
|
29
|
+
*
|
|
30
|
+
* Entry animation uses `@starting-style`; exit slides out before the active
|
|
31
|
+
* modal-dialog or manual-popover host releases the top layer and focus returns
|
|
32
|
+
* to the element that opened the drawer. React owns `display` for both legs
|
|
33
|
+
* rather than a discrete `display` transition, so the panel stops painting in
|
|
34
|
+
* the same commit as the host closes — see the `rendered` style for why.
|
|
35
|
+
*
|
|
36
|
+
* Sibling drawers use the shared layer dismissal stack for topmost-only Escape
|
|
37
|
+
* handling and the browser top layer's chronological paint order.
|
|
38
|
+
*
|
|
39
|
+
* Dismissal follows Dialog: `purpose` decides whether Escape and a scrim click
|
|
40
|
+
* request close, and Drawer renders no close button of its own. Compose
|
|
41
|
+
* `DrawerHeader` with `onOpenChange` for a visible close action, as with
|
|
42
|
+
* `DialogHeader`.
|
|
43
|
+
*
|
|
44
|
+
* The scrolling content area is the drawer's container, like Dialog's inner
|
|
45
|
+
* box: the `padding` prop, else the theme's `padding` on `drawer`, insets it
|
|
46
|
+
* through container tokens, defaulting to `--spacing-4` like Dialog, and the
|
|
47
|
+
* applied inset is published so a lone Section child escapes it and a Layout
|
|
48
|
+
* redistributes it to its regions. Compose `DrawerHeader` inside a `Layout`, as
|
|
49
|
+
* `DialogHeader` is composed in Dialog, so the header is not padded twice. The
|
|
50
|
+
* root dialog stays the overlay boundary (`overlayPaddingReset`).
|
|
51
|
+
*
|
|
52
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
53
|
+
* - /packages/core/src/Drawer/Drawer.doc.mjs (props table, features, usage)
|
|
54
|
+
* - /packages/core/src/Drawer/Drawer.test.tsx (tests for new/changed behavior)
|
|
55
|
+
* - /packages/core/src/Drawer/Drawer.spec.md (component contract)
|
|
56
|
+
* - /packages/core/src/Drawer/index.ts (exports if types change)
|
|
57
|
+
* - /packages/cli/assets/templates/blocks/components/Drawer/ (showcase blocks)
|
|
58
|
+
* - /apps/storybook/stories/Drawer.stories.tsx (examples and visual coverage)
|
|
59
|
+
*/
|
|
60
|
+
import { useCallback, useRef, useState } from 'react';
|
|
61
|
+
import * as stylex from '@stylexjs/stylex';
|
|
62
|
+
import "../theme/tokens.stylex.js";
|
|
63
|
+
import { borderVars, colorVars, durationVars, easeVars, shadowVars } from "../theme/tokens.stylex.js";
|
|
64
|
+
import { useFocusTrap, useMergedRefs, useScrollLock } from "../hooks/index.js";
|
|
65
|
+
import { LayerDepthProvider, useLayerDismissal } from "../Layer/index.js";
|
|
66
|
+
import { layerTextReset } from "../Layer/layerTextReset.stylex.js";
|
|
67
|
+
import { layerStructureReset } from "../Layer/layerStructureReset.stylex.js";
|
|
68
|
+
import { LayerContentBoundary } from "../Layer/layerScopedContext.js";
|
|
69
|
+
import { composeEventHandlers, mergeProps, themeProps } from "../utils/index.js";
|
|
70
|
+
import { container, overlayPaddingReset } from "../Layout/index.js";
|
|
71
|
+
import { containerPaddingBlockEndVarStyles, containerPaddingBlockStartVarStyles, containerPaddingInlineVarStyles, paddingStyles, spacingStepToToken } from "../Layout/padding.stylex.js";
|
|
72
|
+
import { useDrawerDialogPresence } from "./useDrawerDialogPresence.js";
|
|
73
|
+
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// Styles
|
|
76
|
+
// =============================================================================
|
|
77
|
+
|
|
78
|
+
// Below this viewport width the drawer preserves a fixed reveal of the page
|
|
79
|
+
// behind instead of growing proportionally with the viewport. 640px is the
|
|
80
|
+
// repo's mobile breakpoint.
|
|
81
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
82
|
+
const MOBILE_BREAKPOINT = 640;
|
|
83
|
+
|
|
84
|
+
// Material's established mobile drawer pattern leaves a 56dp reveal. Using
|
|
85
|
+
// the same value in CSS pixels gives the overlay a stable visual relationship
|
|
86
|
+
// to the page behind while the requested width remains an upper bound.
|
|
87
|
+
const MOBILE_PAGE_REVEAL = 56;
|
|
88
|
+
const MOBILE_WIDTH_FULL = '100dvw';
|
|
89
|
+
const styles = {
|
|
90
|
+
dialog: {
|
|
91
|
+
kVAEAm: "xixxii4",
|
|
92
|
+
kogj98: "x1ghz6dp",
|
|
93
|
+
kmVPX3: "x1717udv",
|
|
94
|
+
kMzoRj: "xc342km",
|
|
95
|
+
ksu8eU: "xng3xce",
|
|
96
|
+
kaIpWk: "x2u8bby",
|
|
97
|
+
ks0D6T: "x1x1rfll",
|
|
98
|
+
kskxy: "x7ab17h",
|
|
99
|
+
kB7OPa: "x9f619",
|
|
100
|
+
kXwgrk: "xdt5ytf",
|
|
101
|
+
kWkggS: "x10xzikg",
|
|
102
|
+
kGVxlE: "x1kcpxr7",
|
|
103
|
+
kVQacm: "xb3r6kr",
|
|
104
|
+
kZeWKH: "xish69e",
|
|
105
|
+
kI3sdo: "x1a2a7pz",
|
|
106
|
+
k87sOh: "x13vifvy",
|
|
107
|
+
krVfgx: "x1ey2m1c",
|
|
108
|
+
kZKoxP: "xtdtrs8",
|
|
109
|
+
k1xSpc: "x1s85apg",
|
|
110
|
+
k1ekBW: "x11xpdln",
|
|
111
|
+
kIyJzY: "x80gvsz",
|
|
112
|
+
kAMwcw: "xlr8y92",
|
|
113
|
+
k6CgDc: "xzg1mie",
|
|
114
|
+
$$css: true
|
|
115
|
+
},
|
|
116
|
+
rendered: {
|
|
117
|
+
k1xSpc: "x78zum5",
|
|
118
|
+
$$css: true
|
|
119
|
+
},
|
|
120
|
+
end: {
|
|
121
|
+
kt4wiu: "xtijo5x",
|
|
122
|
+
kLqNvP: "xhi6v0a",
|
|
123
|
+
k2ei4v: "xgbv0en",
|
|
124
|
+
kVhnKS: "x1t7ytsu",
|
|
125
|
+
kGJrpR: "x1j92z86",
|
|
126
|
+
k3aq6I: "xumwmo6 xbw113y",
|
|
127
|
+
$$css: true
|
|
128
|
+
},
|
|
129
|
+
endOpen: {
|
|
130
|
+
k3aq6I: "xbryuvx x1yqmsfc xrxqmjf",
|
|
131
|
+
$$css: true
|
|
132
|
+
},
|
|
133
|
+
start: {
|
|
134
|
+
kLqNvP: "x1o0tod",
|
|
135
|
+
kt4wiu: "x1woyocn",
|
|
136
|
+
ke9TFa: "xw8tdv1",
|
|
137
|
+
k8ry5P: "x18b5jzi",
|
|
138
|
+
kBCPoo: "x1gejf6u",
|
|
139
|
+
k3aq6I: "x5i6ehr x1eqdj8u",
|
|
140
|
+
$$css: true
|
|
141
|
+
},
|
|
142
|
+
startOpen: {
|
|
143
|
+
k3aq6I: "xbryuvx x6mt36l x1r0dqvm",
|
|
144
|
+
$$css: true
|
|
145
|
+
},
|
|
146
|
+
scrim: {
|
|
147
|
+
kGyWv1: "xnixb3f",
|
|
148
|
+
kba3nw: "x1abwkk1",
|
|
149
|
+
k5sjJv: "xph5o2a",
|
|
150
|
+
kND0Po: "x167zut7",
|
|
151
|
+
k9an0g: "xft5bk6",
|
|
152
|
+
kb4ib: "x15h3t91",
|
|
153
|
+
kA5Tbj: "x1viac0w",
|
|
154
|
+
$$css: true
|
|
155
|
+
},
|
|
156
|
+
scrimOpen: {
|
|
157
|
+
k5sjJv: "xb3n6bw xxiuuzi",
|
|
158
|
+
$$css: true
|
|
159
|
+
},
|
|
160
|
+
content: {
|
|
161
|
+
kzQI83: "x1iyjqo2",
|
|
162
|
+
kAzted: "x2lwn1j",
|
|
163
|
+
kzqmXN: "xh8yej3",
|
|
164
|
+
kORKVm: "x1odjw0f",
|
|
165
|
+
kXHlph: "x6ikm8r",
|
|
166
|
+
kZeWKH: "xish69e",
|
|
167
|
+
kFalU9: "xx69xxh",
|
|
168
|
+
kI3sdo: "x1a2a7pz",
|
|
169
|
+
kWkggS: "x10xzikg",
|
|
170
|
+
$$css: true
|
|
171
|
+
},
|
|
172
|
+
contentSafeArea: {
|
|
173
|
+
kGO01o: "xvmia7e",
|
|
174
|
+
$$css: true
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const _temp = {
|
|
178
|
+
ks0D6T: "xlbgzzq",
|
|
179
|
+
"$$css": true
|
|
180
|
+
};
|
|
181
|
+
const dynamicStyles = {
|
|
182
|
+
inlineSize: (desktopWidth, mobileWidth) => [_temp, {
|
|
183
|
+
kzqmXN: (desktopWidth != null ? "x11ymkkh " : desktopWidth) + (mobileWidth != null ? "xci7e5a" : mobileWidth),
|
|
184
|
+
$$css: true
|
|
185
|
+
}, {
|
|
186
|
+
"--x-1xmrurk": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(desktopWidth),
|
|
187
|
+
"--x-fqzwsx": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(mobileWidth)
|
|
188
|
+
}]
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// =============================================================================
|
|
192
|
+
// Types
|
|
193
|
+
// =============================================================================
|
|
194
|
+
|
|
195
|
+
// =============================================================================
|
|
196
|
+
// Component
|
|
197
|
+
// =============================================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* An overlay panel for inspectors and detail views.
|
|
201
|
+
*
|
|
202
|
+
* Slides in from the logical start or end edge and floats above the page
|
|
203
|
+
* using the native `<dialog>` element: modal with a scrim by default, or a
|
|
204
|
+
* non-modal overlay with `hasScrim={false}` that leaves the page behind
|
|
205
|
+
* interactive. `width` is the desktop budget; below 640px the panel preserves
|
|
206
|
+
* a 56px page reveal without exceeding that budget (or fills the viewport
|
|
207
|
+
* with the experimental `experimental.isFullWidthOnMobile`). Escape
|
|
208
|
+
* closes the top-most open drawer; focus returns to the element that
|
|
209
|
+
* opened it.
|
|
210
|
+
*
|
|
211
|
+
* Dismissal matches Dialog: `purpose` decides whether Escape and a scrim click
|
|
212
|
+
* close the drawer, and `DrawerHeader` renders a close button when given
|
|
213
|
+
* `onOpenChange`.
|
|
214
|
+
*
|
|
215
|
+
* Like Dialog, the drawer is a container: `padding` (or a theme's `padding`
|
|
216
|
+
* on `drawer`) insets the content area, a lone Section child escapes that
|
|
217
|
+
* inset, and a Layout inside picks it up for its header, content, and footer
|
|
218
|
+
* regions. The default inset is `--spacing-4`, matching Dialog.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```
|
|
222
|
+
* const [selected, setSelected] = useState(null);
|
|
223
|
+
* const handleOpenChange = isOpen => !isOpen && setSelected(null);
|
|
224
|
+
* <Drawer
|
|
225
|
+
* isOpen={selected != null}
|
|
226
|
+
* onOpenChange={handleOpenChange}
|
|
227
|
+
* label={`Details: ${selected?.name}`}>
|
|
228
|
+
* <Layout
|
|
229
|
+
* header={<DrawerHeader title={selected?.name} onOpenChange={handleOpenChange} />}
|
|
230
|
+
* content={<LayoutContent><HostDetails host={selected} /></LayoutContent>}
|
|
231
|
+
* />
|
|
232
|
+
* </Drawer>
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export function Drawer({
|
|
236
|
+
isOpen,
|
|
237
|
+
onOpenChange,
|
|
238
|
+
side = 'end',
|
|
239
|
+
width = 400,
|
|
240
|
+
experimental,
|
|
241
|
+
label,
|
|
242
|
+
hasScrim = true,
|
|
243
|
+
purpose = 'info',
|
|
244
|
+
padding,
|
|
245
|
+
children,
|
|
246
|
+
xstyle,
|
|
247
|
+
className,
|
|
248
|
+
style,
|
|
249
|
+
onClick: onClickProp,
|
|
250
|
+
onKeyDown: onKeyDownProp,
|
|
251
|
+
ref,
|
|
252
|
+
...props
|
|
253
|
+
}) {
|
|
254
|
+
const dialogRef = useRef(null);
|
|
255
|
+
const {
|
|
256
|
+
containerRef: focusTrapRef
|
|
257
|
+
} = useFocusTrap({
|
|
258
|
+
isActive: isOpen && hasScrim
|
|
259
|
+
});
|
|
260
|
+
const mergedDialogRef = useMergedRefs(ref, dialogRef, focusTrapRef);
|
|
261
|
+
// Derive dismissal behavior from purpose, as Dialog does.
|
|
262
|
+
const allowEscape = purpose !== 'required';
|
|
263
|
+
const allowScrimClick = purpose === 'info';
|
|
264
|
+
// Same lowering as Dialog: with no padding prop the content area reads the
|
|
265
|
+
// theme's --astryx-drawer-padding chain (--spacing-4 when unset), else the
|
|
266
|
+
// explicit step.
|
|
267
|
+
const usesThemePadding = padding == null;
|
|
268
|
+
const effectivePadding = padding ?? 4;
|
|
269
|
+
const paddingToken = spacingStepToToken[effectivePadding];
|
|
270
|
+
// Whether the panel paints: true while open and for the whole slide-out.
|
|
271
|
+
const [isRendered, setIsRendered] = useState(isOpen);
|
|
272
|
+
|
|
273
|
+
// Adjusted during render, not in an effect: the panel has to be rendered in
|
|
274
|
+
// the same commit that targets the open transform, or @starting-style has
|
|
275
|
+
// nothing to animate from.
|
|
276
|
+
if (isOpen && !isRendered) {
|
|
277
|
+
setIsRendered(true);
|
|
278
|
+
}
|
|
279
|
+
useDrawerDialogPresence({
|
|
280
|
+
dialogRef,
|
|
281
|
+
isOpen,
|
|
282
|
+
isModal: hasScrim,
|
|
283
|
+
setIsRendered
|
|
284
|
+
});
|
|
285
|
+
const handleDismiss = useCallback(() => {
|
|
286
|
+
onOpenChange(false);
|
|
287
|
+
}, [onOpenChange]);
|
|
288
|
+
|
|
289
|
+
// The shared stack owns Escape delivery across every overlay family. The
|
|
290
|
+
// provider below gives layers opened from Drawer content a greater logical
|
|
291
|
+
// depth even when they render elsewhere in the DOM or browser top layer.
|
|
292
|
+
const {
|
|
293
|
+
shouldDismissOnCloseRequest
|
|
294
|
+
} = useLayerDismissal({
|
|
295
|
+
// The native host stays present through the slide-out. Keep this entry on
|
|
296
|
+
// the stack until the same commit that hides the host, so a second Escape
|
|
297
|
+
// cannot fall through to a lower layer during the exit animation.
|
|
298
|
+
isActive: isRendered,
|
|
299
|
+
// A `required` drawer consumes Escape without closing, so the press cannot
|
|
300
|
+
// fall through and dismiss a layer behind it either.
|
|
301
|
+
escapeBehavior: allowEscape ? 'close' : 'block',
|
|
302
|
+
onDismiss: handleDismiss,
|
|
303
|
+
getContainer: () => dialogRef.current
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Lock body scroll while a modal drawer is open (iOS Safari workaround).
|
|
307
|
+
useScrollLock(isOpen && hasScrim);
|
|
308
|
+
|
|
309
|
+
// A modal dialog's native cancel event is a platform close request (for
|
|
310
|
+
// example Android Back). Keep controlled state authoritative and apply the
|
|
311
|
+
// same top-most/IME rules as the shared Escape listener.
|
|
312
|
+
const handleCancel = useCallback(event => {
|
|
313
|
+
event.preventDefault();
|
|
314
|
+
if (shouldDismissOnCloseRequest() && allowEscape) {
|
|
315
|
+
handleDismiss();
|
|
316
|
+
}
|
|
317
|
+
}, [allowEscape, handleDismiss, shouldDismissOnCloseRequest]);
|
|
318
|
+
|
|
319
|
+
// Clicks on the ::backdrop target the <dialog> element itself; clicks on
|
|
320
|
+
// drawer content always target a child (the content area fills the panel).
|
|
321
|
+
const handleClick = useCallback(event => {
|
|
322
|
+
if (event.target === event.currentTarget && hasScrim && allowScrimClick) {
|
|
323
|
+
onOpenChange(false);
|
|
324
|
+
}
|
|
325
|
+
}, [allowScrimClick, hasScrim, onOpenChange]);
|
|
326
|
+
const widthValue = typeof width === 'number' ? `${width}px` : width;
|
|
327
|
+
const isFullWidthOnMobile = experimental?.isFullWidthOnMobile ?? false;
|
|
328
|
+
const mobileWidth = isFullWidthOnMobile ? MOBILE_WIDTH_FULL : `min(${widthValue}, calc(100dvw - ${MOBILE_PAGE_REVEAL}px))`;
|
|
329
|
+
|
|
330
|
+
// The side the panel is ANCHORED to, which is the side it must slide back
|
|
331
|
+
// out to. Latched at open, because a consumer commonly derives `side` from
|
|
332
|
+
// the same state that drives `isOpen` (`side={selected?.side ?? 'end'}`):
|
|
333
|
+
// that state clears on close, so the live prop flips mid-exit and the panel
|
|
334
|
+
// teleports to the other edge and slides out the wrong way. Children stay
|
|
335
|
+
// mounted for the exit for the same reason; so does the anchor.
|
|
336
|
+
const exitSideRef = useRef(side);
|
|
337
|
+
if (isOpen) {
|
|
338
|
+
exitSideRef.current = side;
|
|
339
|
+
}
|
|
340
|
+
const anchoredSide = isOpen ? side : exitSideRef.current;
|
|
341
|
+
const sideStyle = anchoredSide === 'start' ? styles.start : styles.end;
|
|
342
|
+
const sideOpenStyle = anchoredSide === 'start' ? styles.startOpen : styles.endOpen;
|
|
343
|
+
|
|
344
|
+
// Filter out native `open` to prevent InvalidStateError when passed
|
|
345
|
+
const {
|
|
346
|
+
open: _open,
|
|
347
|
+
...safeProps
|
|
348
|
+
} = props;
|
|
349
|
+
return /*#__PURE__*/_jsx("dialog", {
|
|
350
|
+
ref: mergedDialogRef,
|
|
351
|
+
...mergeProps(themeProps('drawer', {
|
|
352
|
+
side: anchoredSide
|
|
353
|
+
}), stylex.props(layerTextReset.reset, layerStructureReset.reset, styles.dialog, overlayPaddingReset.reset, sideStyle, dynamicStyles.inlineSize(widthValue, mobileWidth), isRendered && styles.rendered, isOpen && sideOpenStyle, hasScrim && styles.scrim, hasScrim && isOpen && styles.scrimOpen, xstyle), className, style),
|
|
354
|
+
...safeProps,
|
|
355
|
+
popover: hasScrim ? undefined : 'manual',
|
|
356
|
+
"aria-label": label,
|
|
357
|
+
"aria-modal": hasScrim ? 'true' : undefined,
|
|
358
|
+
...(purpose === 'required' && hasScrim ? {
|
|
359
|
+
role: 'alertdialog'
|
|
360
|
+
} : undefined),
|
|
361
|
+
onClick: composeEventHandlers(onClickProp, handleClick),
|
|
362
|
+
onKeyDown: onKeyDownProp,
|
|
363
|
+
onCancel: handleCancel,
|
|
364
|
+
children: /*#__PURE__*/_jsx(LayerDepthProvider, {
|
|
365
|
+
children: /*#__PURE__*/_jsx(LayerContentBoundary, {
|
|
366
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
367
|
+
tabIndex: -1,
|
|
368
|
+
...stylex.props(styles.content, ...container(usesThemePadding ? {
|
|
369
|
+
useThemeDefault: 'drawer'
|
|
370
|
+
} : {
|
|
371
|
+
paddingInnerX: paddingToken,
|
|
372
|
+
paddingInnerY: paddingToken,
|
|
373
|
+
paddingOuterX: paddingToken,
|
|
374
|
+
paddingOuterY: paddingToken
|
|
375
|
+
}), !usesThemePadding && effectivePadding !== 4 && paddingStyles[effectivePadding], !usesThemePadding && effectivePadding !== 4 && containerPaddingInlineVarStyles[effectivePadding], !usesThemePadding && effectivePadding !== 4 && containerPaddingBlockStartVarStyles[effectivePadding], !usesThemePadding && effectivePadding !== 4 && containerPaddingBlockEndVarStyles[effectivePadding], styles.contentSafeArea),
|
|
376
|
+
children: children
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
})
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
Drawer.displayName = 'Drawer';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DrawerHeader.tsx
|
|
3
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, i18n, BaseProps, isRenderable, mergeProps, themeProps
|
|
4
|
+
* @output Exports DrawerHeader component and DrawerHeaderProps
|
|
5
|
+
* @position Drawer header; composed in a Layout header slot inside Drawer, tested by DrawerHeader.test.tsx
|
|
6
|
+
*
|
|
7
|
+
* Mirrors DialogHeader's API: a title, an optional subtitle, start/end content
|
|
8
|
+
* slots, and a close button that renders only when `onOpenChange` is passed.
|
|
9
|
+
* Two differences follow from Drawer's contract:
|
|
10
|
+
* - It does not move focus on mount. Drawer owns focus entry through
|
|
11
|
+
* `data-autofocus` once its native host is presented.
|
|
12
|
+
* - It does not name the drawer. Drawer's required `label` does.
|
|
13
|
+
*
|
|
14
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
15
|
+
* - /packages/core/src/Drawer/DrawerHeader.doc.mjs (props, anatomy, theming)
|
|
16
|
+
* - /packages/core/src/Drawer/DrawerHeader.test.tsx (tests for new/changed behavior)
|
|
17
|
+
* - /packages/core/src/Drawer/index.ts (exports if types change)
|
|
18
|
+
* - /packages/cli/assets/templates/blocks/components/Drawer/ (showcase blocks)
|
|
19
|
+
* - /packages/cli/assets/templates/blocks/components/DrawerHeader/ (showcase blocks)
|
|
20
|
+
* - /apps/storybook/stories/Drawer.stories.tsx (examples and visual coverage)
|
|
21
|
+
* - /apps/storybook/stories/DrawerHeader.stories.tsx (component audit fixture)
|
|
22
|
+
*/
|
|
23
|
+
import type { ReactNode } from 'react';
|
|
24
|
+
import type { BaseProps } from '../BaseProps';
|
|
25
|
+
export interface DrawerHeaderProps extends BaseProps<HTMLDivElement> {
|
|
26
|
+
/** Ref forwarded to the root element */
|
|
27
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
28
|
+
/**
|
|
29
|
+
* The title of the drawer, rendered as an h2. Keep it inline and
|
|
30
|
+
* non-interactive. It does not name the drawer; Drawer's `label` does.
|
|
31
|
+
*/
|
|
32
|
+
title: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Optional subtitle displayed below the title in smaller, secondary text.
|
|
35
|
+
* Accepts inline content such as a Link; it renders inside a span, so
|
|
36
|
+
* avoid block elements. Nothing renders for `null`, `undefined`, booleans,
|
|
37
|
+
* or an empty string.
|
|
38
|
+
*/
|
|
39
|
+
subtitle?: ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* Called with `false` when the close button is clicked. Pass the Drawer's
|
|
42
|
+
* own `onOpenChange`. If not provided, no close button is rendered.
|
|
43
|
+
*/
|
|
44
|
+
onOpenChange?: (isOpen: boolean) => unknown;
|
|
45
|
+
/**
|
|
46
|
+
* Content to render before the title (e.g., a back button).
|
|
47
|
+
*/
|
|
48
|
+
startContent?: ReactNode;
|
|
49
|
+
/**
|
|
50
|
+
* Content to render after the title, before the close button (e.g., action
|
|
51
|
+
* buttons).
|
|
52
|
+
*/
|
|
53
|
+
endContent?: ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Overrides automatic end-slot compensation. When omitted, rendering the
|
|
56
|
+
* close action applies block and logical inline-end compensation. Use
|
|
57
|
+
* `inline`, `block`, or `all` to select the axes explicitly.
|
|
58
|
+
*/
|
|
59
|
+
endContentEdgeCompensation?: 'inline' | 'block' | 'all';
|
|
60
|
+
/**
|
|
61
|
+
* Adds a themed border at the bottom edge.
|
|
62
|
+
* When false, spacing collapse is applied automatically for seamless visual flow.
|
|
63
|
+
* Defaults to the parent Layout's `defaultHasDividers` context value.
|
|
64
|
+
*/
|
|
65
|
+
hasDivider?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Header for a Drawer: a title, optional subtitle and start/end content, and a
|
|
69
|
+
* close button when `onOpenChange` is passed. Same API as DialogHeader.
|
|
70
|
+
*
|
|
71
|
+
* Uses LayoutHeader internally for consistent styling with other layout headers.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```
|
|
75
|
+
* <Drawer isOpen={isOpen} onOpenChange={setIsOpen} label="Details">
|
|
76
|
+
* <DrawerHeader title="Details" onOpenChange={setIsOpen} />
|
|
77
|
+
* <Section padding={4}>Content</Section>
|
|
78
|
+
* </Drawer>
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function DrawerHeader({ title, subtitle, onOpenChange, startContent, endContent, endContentEdgeCompensation, hasDivider, xstyle, className, style, ref, ...rest }: DrawerHeaderProps): import("react").JSX.Element;
|
|
82
|
+
export declare namespace DrawerHeader {
|
|
83
|
+
var displayName: string;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=DrawerHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DrawerHeader.d.ts","sourceRoot":"","sources":["../../src/Drawer/DrawerHeader.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA0C5C,MAAM,WAAW,iBAAkB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAClE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE5C;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IAEzB;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IAEvB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IAExD;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,iBAAiB,+BAoFnB;yBAjGe,YAAY"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file DrawerHeader.tsx
|
|
7
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, i18n, BaseProps, isRenderable, mergeProps, themeProps
|
|
8
|
+
* @output Exports DrawerHeader component and DrawerHeaderProps
|
|
9
|
+
* @position Drawer header; composed in a Layout header slot inside Drawer, tested by DrawerHeader.test.tsx
|
|
10
|
+
*
|
|
11
|
+
* Mirrors DialogHeader's API: a title, an optional subtitle, start/end content
|
|
12
|
+
* slots, and a close button that renders only when `onOpenChange` is passed.
|
|
13
|
+
* Two differences follow from Drawer's contract:
|
|
14
|
+
* - It does not move focus on mount. Drawer owns focus entry through
|
|
15
|
+
* `data-autofocus` once its native host is presented.
|
|
16
|
+
* - It does not name the drawer. Drawer's required `label` does.
|
|
17
|
+
*
|
|
18
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
19
|
+
* - /packages/core/src/Drawer/DrawerHeader.doc.mjs (props, anatomy, theming)
|
|
20
|
+
* - /packages/core/src/Drawer/DrawerHeader.test.tsx (tests for new/changed behavior)
|
|
21
|
+
* - /packages/core/src/Drawer/index.ts (exports if types change)
|
|
22
|
+
* - /packages/cli/assets/templates/blocks/components/Drawer/ (showcase blocks)
|
|
23
|
+
* - /packages/cli/assets/templates/blocks/components/DrawerHeader/ (showcase blocks)
|
|
24
|
+
* - /apps/storybook/stories/Drawer.stories.tsx (examples and visual coverage)
|
|
25
|
+
* - /apps/storybook/stories/DrawerHeader.stories.tsx (component audit fixture)
|
|
26
|
+
*/
|
|
27
|
+
import * as stylex from '@stylexjs/stylex';
|
|
28
|
+
import "../theme/tokens.stylex.js";
|
|
29
|
+
import { sizeVars, spacingVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
30
|
+
import { Button } from "../Button/index.js";
|
|
31
|
+
import { Heading } from "../Heading/index.js";
|
|
32
|
+
import { Icon } from "../Icon/index.js";
|
|
33
|
+
import { LayoutHeader } from "../Layout/index.js";
|
|
34
|
+
import { Text } from "../Text/index.js";
|
|
35
|
+
import { useTranslator } from "../i18n/index.js";
|
|
36
|
+
import { isRenderable, mergeProps, themeProps } from "../utils/index.js";
|
|
37
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
38
|
+
/**
|
|
39
|
+
* Header for a Drawer: a title, optional subtitle and start/end content, and a
|
|
40
|
+
* close button when `onOpenChange` is passed. Same API as DialogHeader.
|
|
41
|
+
*
|
|
42
|
+
* Uses LayoutHeader internally for consistent styling with other layout headers.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```
|
|
46
|
+
* <Drawer isOpen={isOpen} onOpenChange={setIsOpen} label="Details">
|
|
47
|
+
* <DrawerHeader title="Details" onOpenChange={setIsOpen} />
|
|
48
|
+
* <Section padding={4}>Content</Section>
|
|
49
|
+
* </Drawer>
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function DrawerHeader({
|
|
53
|
+
title,
|
|
54
|
+
subtitle,
|
|
55
|
+
onOpenChange,
|
|
56
|
+
startContent,
|
|
57
|
+
endContent,
|
|
58
|
+
endContentEdgeCompensation,
|
|
59
|
+
hasDivider,
|
|
60
|
+
xstyle,
|
|
61
|
+
className,
|
|
62
|
+
style,
|
|
63
|
+
ref,
|
|
64
|
+
...rest
|
|
65
|
+
}) {
|
|
66
|
+
const t = useTranslator();
|
|
67
|
+
const hasSubtitle = isRenderable(subtitle);
|
|
68
|
+
const hasStartContent = isRenderable(startContent);
|
|
69
|
+
const hasEndContent = isRenderable(endContent);
|
|
70
|
+
const shouldCompensateEndBlock = endContentEdgeCompensation == null ? onOpenChange != null : endContentEdgeCompensation === 'block' || endContentEdgeCompensation === 'all';
|
|
71
|
+
const shouldCompensateEndInline = endContentEdgeCompensation == null ? onOpenChange != null : endContentEdgeCompensation === 'inline' || endContentEdgeCompensation === 'all';
|
|
72
|
+
return /*#__PURE__*/_jsx(LayoutHeader, {
|
|
73
|
+
ref: ref,
|
|
74
|
+
hasDivider: hasDivider,
|
|
75
|
+
xstyle: xstyle,
|
|
76
|
+
className: className,
|
|
77
|
+
style: style,
|
|
78
|
+
...rest,
|
|
79
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
80
|
+
...mergeProps(themeProps('drawer-header'), {
|
|
81
|
+
className: "x78zum5 x1cy8zhl x1qughib xjcht0a"
|
|
82
|
+
}),
|
|
83
|
+
children: [hasStartContent && /*#__PURE__*/_jsx("div", {
|
|
84
|
+
...mergeProps(themeProps('drawer-header-start-content'), {
|
|
85
|
+
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s"
|
|
86
|
+
}),
|
|
87
|
+
children: startContent
|
|
88
|
+
}), /*#__PURE__*/_jsxs("div", {
|
|
89
|
+
...mergeProps(themeProps('drawer-header-title-block'), {
|
|
90
|
+
className: "x78zum5 xdt5ytf xsn7fz1 x98rzlu xeuugli xqixskq"
|
|
91
|
+
}),
|
|
92
|
+
children: [/*#__PURE__*/_jsx(Heading, {
|
|
93
|
+
level: 2,
|
|
94
|
+
children: title
|
|
95
|
+
}), hasSubtitle && /*#__PURE__*/_jsx(Text, {
|
|
96
|
+
type: "body",
|
|
97
|
+
size: "sm",
|
|
98
|
+
color: "secondary",
|
|
99
|
+
children: subtitle
|
|
100
|
+
})]
|
|
101
|
+
}), (hasEndContent || onOpenChange != null) && /*#__PURE__*/_jsxs("div", {
|
|
102
|
+
...mergeProps(themeProps('drawer-header-end-content'), {
|
|
103
|
+
0: {
|
|
104
|
+
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s"
|
|
105
|
+
},
|
|
106
|
+
2: {
|
|
107
|
+
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s xhzvc8f"
|
|
108
|
+
},
|
|
109
|
+
1: {
|
|
110
|
+
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s x3kzqx6"
|
|
111
|
+
},
|
|
112
|
+
3: {
|
|
113
|
+
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s xhzvc8f x3kzqx6"
|
|
114
|
+
}
|
|
115
|
+
}[!!shouldCompensateEndBlock << 1 | !!shouldCompensateEndInline << 0]),
|
|
116
|
+
children: [endContent, onOpenChange && /*#__PURE__*/_jsx(Button, {
|
|
117
|
+
variant: "ghost",
|
|
118
|
+
label: t('@astryx.dialog.close'),
|
|
119
|
+
tooltip: t('@astryx.dialog.close'),
|
|
120
|
+
icon: /*#__PURE__*/_jsx(Icon, {
|
|
121
|
+
icon: "close",
|
|
122
|
+
color: "inherit",
|
|
123
|
+
...themeProps('drawer-header-close-icon')
|
|
124
|
+
}),
|
|
125
|
+
onClick: () => {
|
|
126
|
+
onOpenChange(false);
|
|
127
|
+
},
|
|
128
|
+
isIconOnly: true
|
|
129
|
+
})]
|
|
130
|
+
})]
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
DrawerHeader.displayName = 'DrawerHeader';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* @input Drawer.tsx, DrawerHeader.tsx
|
|
4
|
+
* @output Re-exports Drawer and DrawerHeader components and their prop types
|
|
5
|
+
* @position Core entry point for the Drawer directory (`@astryxdesign/core/Drawer`)
|
|
6
|
+
*/
|
|
7
|
+
export { Drawer } from './Drawer';
|
|
8
|
+
export type { DrawerProps } from './Drawer';
|
|
9
|
+
export { DrawerHeader } from './DrawerHeader';
|
|
10
|
+
export type { DrawerHeaderProps } from './DrawerHeader';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Drawer/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,YAAY,EAAC,WAAW,EAAC,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file index.ts
|
|
5
|
+
* @input Drawer.tsx, DrawerHeader.tsx
|
|
6
|
+
* @output Re-exports Drawer and DrawerHeader components and their prop types
|
|
7
|
+
* @position Core entry point for the Drawer directory (`@astryxdesign/core/Drawer`)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { Drawer } from "./Drawer.js";
|
|
11
|
+
export { DrawerHeader } from "./DrawerHeader.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useDrawerDialogPresence.ts
|
|
3
|
+
* @input Controlled open state, modal mode, dialog ref, and rendered-state setter
|
|
4
|
+
* @output Coordinates native top-layer presence, exit timing, focus restoration, and unmount cleanup
|
|
5
|
+
* @position Drawer-internal hook; consumed only by Drawer.tsx
|
|
6
|
+
*
|
|
7
|
+
* The drawer has two independent notions of presence:
|
|
8
|
+
* - React's rendered state keeps the panel visible for its CSS exit.
|
|
9
|
+
* - Native `showModal()` or `showPopover()` state keeps it in the browser top
|
|
10
|
+
* layer.
|
|
11
|
+
*
|
|
12
|
+
* Their close ordering is a browser-visible invariant: the panel must finish
|
|
13
|
+
* its exit, then leave the active native host and hide in the same task. If the
|
|
14
|
+
* hide lands a frame later, a transformed ancestor becomes the containing block
|
|
15
|
+
* for the now-non-top-layer `position: fixed` panel and it paints back inside
|
|
16
|
+
* the page for one frame.
|
|
17
|
+
*
|
|
18
|
+
* SYNC: When modified, update:
|
|
19
|
+
* - /packages/core/src/Drawer/Drawer.test.tsx
|
|
20
|
+
* - /.github/scripts/modal-close-visibility.js
|
|
21
|
+
*/
|
|
22
|
+
import { type Dispatch, type RefObject, type SetStateAction } from 'react';
|
|
23
|
+
type UseDrawerDialogPresenceOptions = {
|
|
24
|
+
dialogRef: RefObject<HTMLDialogElement | null>;
|
|
25
|
+
isOpen: boolean;
|
|
26
|
+
isModal: boolean;
|
|
27
|
+
setIsRendered: Dispatch<SetStateAction<boolean>>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Coordinates the native dialog and React-rendered presence for Drawer.
|
|
31
|
+
*
|
|
32
|
+
* Opening captures the trigger, enters the modal-dialog or manual-popover host,
|
|
33
|
+
* and honours the component's `data-autofocus` contract. Closing waits for the
|
|
34
|
+
* actual transform transition (with a computed-duration backstop), then leaves
|
|
35
|
+
* the native host and synchronously hides the panel before the browser can paint
|
|
36
|
+
* it outside the top layer, restores focus to the captured trigger, and only
|
|
37
|
+
* then dispatches the popover host's synthetic `close`. Unmount cleanup closes
|
|
38
|
+
* a host left open by React Activity or a removed subtree.
|
|
39
|
+
*/
|
|
40
|
+
export declare function useDrawerDialogPresence({ dialogRef, isOpen, isModal, setIsRendered, }: UseDrawerDialogPresenceOptions): void;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=useDrawerDialogPresence.d.ts.map
|