@astryxdesign/cli 0.7.2-canary.c68f5e8 → 0.7.2-canary.caec7ca
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/api/component/component.test.mjs +357 -184
- package/api/template/template.test.mjs +120 -3
- package/assets/templates/blocks/components/Drawer/DrawerForm.doc.mjs +24 -0
- package/assets/templates/blocks/components/Drawer/DrawerForm.tsx +62 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.tsx +68 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.doc.mjs +21 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.tsx +54 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.tsx +73 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs +22 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.tsx +49 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.doc.mjs +21 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.tsx +42 -0
- package/assets/templates/pages/detail-page/page.tsx +3 -3
- package/assets/templates/pages/table-inbox/page.tsx +10 -8
- package/authoring/doctypes/base/type.ts +6 -0
- package/authoring/doctypes/component/component.doc.mjs +2 -2
- package/clients/cli/lib/component-format.mjs +5 -1
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/doc-compiler/doc-loads.test.mjs +1 -1
- package/package.json +10 -10
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
3
6
|
import {describe, it, expect} from 'vitest';
|
|
4
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
stripTemplateAssetRefs,
|
|
9
|
+
template,
|
|
10
|
+
extractComponents,
|
|
11
|
+
} from './template.mjs';
|
|
5
12
|
|
|
6
13
|
describe('stripTemplateAssetRefs', () => {
|
|
7
14
|
it('replaces a /template-assets image path with an inline data URI', () => {
|
|
@@ -31,8 +38,7 @@ describe('stripTemplateAssetRefs', () => {
|
|
|
31
38
|
});
|
|
32
39
|
|
|
33
40
|
it('preserves surrounding source structure', () => {
|
|
34
|
-
const src =
|
|
35
|
-
"const data = [{src: '/template-assets/x.png', alt: 'X'}];";
|
|
41
|
+
const src = "const data = [{src: '/template-assets/x.png', alt: 'X'}];";
|
|
36
42
|
const out = stripTemplateAssetRefs(src);
|
|
37
43
|
expect(out).toContain("alt: 'X'");
|
|
38
44
|
expect(out).toContain('const data = [{src:');
|
|
@@ -102,4 +108,115 @@ describe('template --skeleton component extraction (prefix-agnostic)', () => {
|
|
|
102
108
|
|
|
103
109
|
expect(result.data.skeleton).toContain('columns={{minWidth: 200}}');
|
|
104
110
|
});
|
|
111
|
+
|
|
112
|
+
it('filters out local helper components declared in the same file', () => {
|
|
113
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
|
|
114
|
+
const pagePath = path.join(tmpDir, 'page.tsx');
|
|
115
|
+
fs.writeFileSync(
|
|
116
|
+
pagePath,
|
|
117
|
+
`
|
|
118
|
+
import {Card} from '@astryxdesign/core/Layout';
|
|
119
|
+
|
|
120
|
+
function TimelineSection() {
|
|
121
|
+
return <div />;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default function Page() {
|
|
125
|
+
return (
|
|
126
|
+
<Card>
|
|
127
|
+
<TimelineSection />
|
|
128
|
+
</Card>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
`,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const components = extractComponents(pagePath);
|
|
136
|
+
expect(components).toContain('Card');
|
|
137
|
+
expect(components).not.toContain('Timeline');
|
|
138
|
+
} finally {
|
|
139
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('filters out a type-annotated local helper', () => {
|
|
144
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
|
|
145
|
+
const pagePath = path.join(tmpDir, 'page.tsx');
|
|
146
|
+
fs.writeFileSync(
|
|
147
|
+
pagePath,
|
|
148
|
+
`
|
|
149
|
+
import type {FC} from 'react';
|
|
150
|
+
import {Card} from '@astryxdesign/core/Layout';
|
|
151
|
+
|
|
152
|
+
const TimelineSection: FC = () => <div />;
|
|
153
|
+
|
|
154
|
+
export default function Page() {
|
|
155
|
+
return (
|
|
156
|
+
<Card>
|
|
157
|
+
<TimelineSection />
|
|
158
|
+
</Card>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
`,
|
|
162
|
+
);
|
|
163
|
+
try {
|
|
164
|
+
const components = extractComponents(pagePath);
|
|
165
|
+
expect(components).toContain('Card');
|
|
166
|
+
expect(components).not.toContain('Timeline');
|
|
167
|
+
} finally {
|
|
168
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('keeps an imported component when declaration-like text appears in a comment or string', () => {
|
|
173
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
|
|
174
|
+
const pagePath = path.join(tmpDir, 'page.tsx');
|
|
175
|
+
fs.writeFileSync(
|
|
176
|
+
pagePath,
|
|
177
|
+
`
|
|
178
|
+
import {Card} from '@astryxdesign/core/Layout';
|
|
179
|
+
|
|
180
|
+
// const Card = () => null;
|
|
181
|
+
const note = 'function Card() {}';
|
|
182
|
+
|
|
183
|
+
export default function Page() {
|
|
184
|
+
return <Card>{note}</Card>;
|
|
185
|
+
}
|
|
186
|
+
`,
|
|
187
|
+
);
|
|
188
|
+
try {
|
|
189
|
+
expect(extractComponents(pagePath)).toContain('Card');
|
|
190
|
+
} finally {
|
|
191
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('keeps a bare imported component whose name is itself a stripped suffix', () => {
|
|
196
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
|
|
197
|
+
const pagePath = path.join(tmpDir, 'page.tsx');
|
|
198
|
+
fs.writeFileSync(
|
|
199
|
+
pagePath,
|
|
200
|
+
`
|
|
201
|
+
import {Section} from '@astryxdesign/core/Section';
|
|
202
|
+
|
|
203
|
+
function SectionHeading() {
|
|
204
|
+
return <div />;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default function Page() {
|
|
208
|
+
return (
|
|
209
|
+
<Section>
|
|
210
|
+
<SectionHeading />
|
|
211
|
+
</Section>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
`,
|
|
215
|
+
);
|
|
216
|
+
try {
|
|
217
|
+
expect(extractComponents(pagePath)).toContain('Section');
|
|
218
|
+
} finally {
|
|
219
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
105
222
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Edit form with footer actions',
|
|
7
|
+
displayName: 'Edit form with footer actions',
|
|
8
|
+
description:
|
|
9
|
+
'Compose a Layout with DrawerHeader, the fields in LayoutContent, and Cancel and Save in a pinned LayoutFooter. purpose="form" keeps Escape but ignores scrim clicks, so a stray click outside does not discard edits.',
|
|
10
|
+
exampleFor: 'Drawer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'Drawer',
|
|
15
|
+
'DrawerHeader',
|
|
16
|
+
'Button',
|
|
17
|
+
'Layout',
|
|
18
|
+
'LayoutContent',
|
|
19
|
+
'LayoutFooter',
|
|
20
|
+
'HStack',
|
|
21
|
+
'VStack',
|
|
22
|
+
'TextInput',
|
|
23
|
+
],
|
|
24
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled open state and two editable fields
|
|
7
|
+
* @output A form drawer with a header, scrolling fields, and pinned footer
|
|
8
|
+
* actions; purpose="form" keeps Escape but ignores scrim clicks
|
|
9
|
+
* @position Copyable Drawer example
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {useState} from 'react';
|
|
13
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
14
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
15
|
+
import {Layout, LayoutContent, LayoutFooter} from '@astryxdesign/core/Layout';
|
|
16
|
+
import {HStack, VStack} from '@astryxdesign/core/Stack';
|
|
17
|
+
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
18
|
+
|
|
19
|
+
export default function DrawerForm() {
|
|
20
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
21
|
+
const [name, setName] = useState('web-prod-04');
|
|
22
|
+
const [owner, setOwner] = useState('Platform team');
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<Button label="Edit host" onClick={() => setIsOpen(true)} />
|
|
27
|
+
<Drawer
|
|
28
|
+
isOpen={isOpen}
|
|
29
|
+
onOpenChange={setIsOpen}
|
|
30
|
+
label="Edit host"
|
|
31
|
+
purpose="form">
|
|
32
|
+
<Layout
|
|
33
|
+
header={<DrawerHeader title="Edit host" onOpenChange={setIsOpen} />}
|
|
34
|
+
content={
|
|
35
|
+
<LayoutContent>
|
|
36
|
+
<VStack gap={4}>
|
|
37
|
+
<TextInput label="Name" value={name} onChange={setName} />
|
|
38
|
+
<TextInput label="Owner" value={owner} onChange={setOwner} />
|
|
39
|
+
</VStack>
|
|
40
|
+
</LayoutContent>
|
|
41
|
+
}
|
|
42
|
+
footer={
|
|
43
|
+
<LayoutFooter>
|
|
44
|
+
<HStack gap={2} hAlign="end">
|
|
45
|
+
<Button
|
|
46
|
+
label="Cancel"
|
|
47
|
+
variant="secondary"
|
|
48
|
+
onClick={() => setIsOpen(false)}
|
|
49
|
+
/>
|
|
50
|
+
<Button
|
|
51
|
+
label="Save"
|
|
52
|
+
variant="primary"
|
|
53
|
+
onClick={() => setIsOpen(false)}
|
|
54
|
+
/>
|
|
55
|
+
</HStack>
|
|
56
|
+
</LayoutFooter>
|
|
57
|
+
}
|
|
58
|
+
/>
|
|
59
|
+
</Drawer>
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Modal or non-modal (hasScrim)',
|
|
7
|
+
displayName: 'Modal or non-modal (hasScrim)',
|
|
8
|
+
description:
|
|
9
|
+
'The default modal drawer dims the page, traps focus, and closes on a scrim click. With hasScrim={false}, the drawer has no scrim or focus trap and leaves the page interactive.',
|
|
10
|
+
exampleFor: 'Drawer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'Drawer',
|
|
15
|
+
'Button',
|
|
16
|
+
'Layout',
|
|
17
|
+
'LayoutContent',
|
|
18
|
+
'HStack',
|
|
19
|
+
'DrawerHeader',
|
|
20
|
+
'Text',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Separate controlled states for modal and non-modal drawers
|
|
7
|
+
* @output A comparison of hasScrim's two presentations
|
|
8
|
+
* @position Copyable Drawer example
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
13
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
+
import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
15
|
+
import {HStack} from '@astryxdesign/core/Stack';
|
|
16
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
17
|
+
|
|
18
|
+
export default function DrawerScrim() {
|
|
19
|
+
const [openModal, setOpenModal] = useState(false);
|
|
20
|
+
const [openPanel, setOpenPanel] = useState(false);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<HStack gap={2}>
|
|
25
|
+
<Button label="Open modal drawer" onClick={() => setOpenModal(true)} />
|
|
26
|
+
<Button
|
|
27
|
+
label="Open non-modal drawer"
|
|
28
|
+
onClick={() => setOpenPanel(true)}
|
|
29
|
+
/>
|
|
30
|
+
</HStack>
|
|
31
|
+
<Drawer
|
|
32
|
+
isOpen={openModal}
|
|
33
|
+
onOpenChange={setOpenModal}
|
|
34
|
+
label="Edit details">
|
|
35
|
+
<Layout
|
|
36
|
+
header={
|
|
37
|
+
<DrawerHeader title="Edit details" onOpenChange={setOpenModal} />
|
|
38
|
+
}
|
|
39
|
+
content={
|
|
40
|
+
<LayoutContent>
|
|
41
|
+
<Text type="body">
|
|
42
|
+
The scrim blocks the page behind this modal drawer.
|
|
43
|
+
</Text>
|
|
44
|
+
</LayoutContent>
|
|
45
|
+
}
|
|
46
|
+
/>
|
|
47
|
+
</Drawer>
|
|
48
|
+
<Drawer
|
|
49
|
+
isOpen={openPanel}
|
|
50
|
+
onOpenChange={setOpenPanel}
|
|
51
|
+
label="Inspector"
|
|
52
|
+
hasScrim={false}>
|
|
53
|
+
<Layout
|
|
54
|
+
header={
|
|
55
|
+
<DrawerHeader title="Inspector" onOpenChange={setOpenPanel} />
|
|
56
|
+
}
|
|
57
|
+
content={
|
|
58
|
+
<LayoutContent>
|
|
59
|
+
<Text type="body">
|
|
60
|
+
The page behind this non-modal drawer stays interactive.
|
|
61
|
+
</Text>
|
|
62
|
+
</LayoutContent>
|
|
63
|
+
}
|
|
64
|
+
/>
|
|
65
|
+
</Drawer>
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Basic',
|
|
7
|
+
displayName: 'Basic',
|
|
8
|
+
description:
|
|
9
|
+
'Open a modal drawer from a single trigger. The scrim dims the page; Escape, a scrim click, or the header close button closes it, and focus returns to the trigger.',
|
|
10
|
+
exampleFor: 'Drawer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
isShowcase: true,
|
|
13
|
+
aspectRatio: 16 / 9,
|
|
14
|
+
componentsUsed: [
|
|
15
|
+
'Drawer',
|
|
16
|
+
'Button',
|
|
17
|
+
'Layout',
|
|
18
|
+
'LayoutContent',
|
|
19
|
+
'DrawerHeader',
|
|
20
|
+
'Text',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled open state for a modal Drawer
|
|
7
|
+
* @output A single-trigger drawer with a scrim and a DrawerHeader close button
|
|
8
|
+
* @position Drawer's docsite showcase and copyable CLI block
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
13
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
+
import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
15
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
16
|
+
|
|
17
|
+
export default function DrawerShowcase() {
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Button label="Open drawer" onClick={() => setIsOpen(true)} />
|
|
23
|
+
<Drawer
|
|
24
|
+
isOpen={isOpen}
|
|
25
|
+
onOpenChange={setIsOpen}
|
|
26
|
+
label="Details"
|
|
27
|
+
width={360}>
|
|
28
|
+
<Layout
|
|
29
|
+
header={<DrawerHeader title="Details" onOpenChange={setIsOpen} />}
|
|
30
|
+
content={
|
|
31
|
+
<LayoutContent>
|
|
32
|
+
<Text type="body">
|
|
33
|
+
Close with Escape, the scrim, or the close button.
|
|
34
|
+
</Text>
|
|
35
|
+
</LayoutContent>
|
|
36
|
+
}
|
|
37
|
+
/>
|
|
38
|
+
</Drawer>
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Slide in from the start edge',
|
|
7
|
+
displayName: 'Slide in from the start edge',
|
|
8
|
+
description:
|
|
9
|
+
'Set side="start" to open from the inline start edge: left in LTR and right in RTL. The default end edge follows the inspector convention.',
|
|
10
|
+
exampleFor: 'Drawer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'Drawer',
|
|
15
|
+
'Button',
|
|
16
|
+
'Layout',
|
|
17
|
+
'LayoutContent',
|
|
18
|
+
'DrawerHeader',
|
|
19
|
+
'Text',
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled open state and the logical start edge
|
|
7
|
+
* @output A Drawer that opens from the start side of the viewport
|
|
8
|
+
* @position Copyable Drawer example
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
13
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
+
import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
15
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
16
|
+
|
|
17
|
+
export default function DrawerStart() {
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Button label="Open start drawer" onClick={() => setIsOpen(true)} />
|
|
23
|
+
<Drawer
|
|
24
|
+
isOpen={isOpen}
|
|
25
|
+
onOpenChange={setIsOpen}
|
|
26
|
+
label="Navigation"
|
|
27
|
+
side="start">
|
|
28
|
+
<Layout
|
|
29
|
+
header={<DrawerHeader title="Navigation" onOpenChange={setIsOpen} />}
|
|
30
|
+
content={
|
|
31
|
+
<LayoutContent>
|
|
32
|
+
<Text type="body">
|
|
33
|
+
The start edge follows the page's writing direction.
|
|
34
|
+
</Text>
|
|
35
|
+
</LayoutContent>
|
|
36
|
+
}
|
|
37
|
+
/>
|
|
38
|
+
</Drawer>
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Width budget: pixels or any CSS length',
|
|
7
|
+
displayName: 'Width budget: pixels or any CSS length',
|
|
8
|
+
description:
|
|
9
|
+
'Compare a numeric pixel width with rem and percentage lengths. The width budget also caps the drawer on mobile.',
|
|
10
|
+
exampleFor: 'Drawer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'Drawer',
|
|
15
|
+
'Button',
|
|
16
|
+
'Layout',
|
|
17
|
+
'LayoutContent',
|
|
18
|
+
'HStack',
|
|
19
|
+
'DrawerHeader',
|
|
20
|
+
'Text',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input A pixel, rem, or percentage width and controlled open state
|
|
7
|
+
* @output A Drawer demonstrating the selected width budget
|
|
8
|
+
* @position Copyable Drawer example
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
13
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
+
import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
15
|
+
import {HStack} from '@astryxdesign/core/Stack';
|
|
16
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
17
|
+
|
|
18
|
+
export default function DrawerWidths() {
|
|
19
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
20
|
+
const [width, setWidth] = useState<number | string>(320);
|
|
21
|
+
|
|
22
|
+
function openAtWidth(value: number | string) {
|
|
23
|
+
setWidth(value);
|
|
24
|
+
setIsOpen(true);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<HStack gap={2}>
|
|
30
|
+
<Button label="320px" onClick={() => openAtWidth(320)} />
|
|
31
|
+
<Button label="32rem" onClick={() => openAtWidth('32rem')} />
|
|
32
|
+
<Button label="50%" onClick={() => openAtWidth('50%')} />
|
|
33
|
+
</HStack>
|
|
34
|
+
<Drawer
|
|
35
|
+
isOpen={isOpen}
|
|
36
|
+
onOpenChange={setIsOpen}
|
|
37
|
+
label="Width budget"
|
|
38
|
+
width={width}>
|
|
39
|
+
<Layout
|
|
40
|
+
header={
|
|
41
|
+
<DrawerHeader title="Width budget" onOpenChange={setIsOpen} />
|
|
42
|
+
}
|
|
43
|
+
content={
|
|
44
|
+
<LayoutContent>
|
|
45
|
+
<Text type="body">
|
|
46
|
+
Width: {typeof width === 'number' ? `${width}px` : width}
|
|
47
|
+
</Text>
|
|
48
|
+
</LayoutContent>
|
|
49
|
+
}
|
|
50
|
+
/>
|
|
51
|
+
</Drawer>
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'DrawerHeader',
|
|
7
|
+
displayName: 'Drawer Header',
|
|
8
|
+
description:
|
|
9
|
+
'DrawerHeader provides a structured header for drawers with slots for title, subtitle, close button, and optional start or end content.',
|
|
10
|
+
exampleFor: 'DrawerHeader',
|
|
11
|
+
isReady: true,
|
|
12
|
+
isShowcase: true,
|
|
13
|
+
aspectRatio: 16 / 9,
|
|
14
|
+
componentsUsed: ['DrawerHeader', 'Layout', 'LayoutContent', 'Text'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Static header content: title, subtitle, a no-op close handler, and a divider
|
|
7
|
+
* @output The DrawerHeader row at the top of a square-cornered, drawer-width panel, visible without opening an overlay
|
|
8
|
+
* @position DrawerHeader's docsite showcase. Drawer has no inline mode
|
|
9
|
+
* like Dialog's isInline, so a plain panel stands in for the drawer; in an
|
|
10
|
+
* app, compose DrawerHeader in a Layout header slot inside a Drawer.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as stylex from '@stylexjs/stylex';
|
|
14
|
+
import {DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
15
|
+
import {
|
|
16
|
+
Layout,
|
|
17
|
+
LayoutContent,
|
|
18
|
+
overlayPaddingReset,
|
|
19
|
+
} from '@astryxdesign/core/Layout';
|
|
20
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
21
|
+
import {
|
|
22
|
+
borderVars,
|
|
23
|
+
colorVars,
|
|
24
|
+
spacingVars,
|
|
25
|
+
} from '@astryxdesign/core/theme/tokens.stylex';
|
|
26
|
+
|
|
27
|
+
const styles = stylex.create({
|
|
28
|
+
// A card-like panel with square corners, matching the drawer's own corners.
|
|
29
|
+
panel: {
|
|
30
|
+
width: 360,
|
|
31
|
+
maxWidth: '100%',
|
|
32
|
+
boxSizing: 'border-box',
|
|
33
|
+
backgroundColor: colorVars['--color-background-card'],
|
|
34
|
+
borderWidth: borderVars['--border-width'],
|
|
35
|
+
borderStyle: 'solid',
|
|
36
|
+
borderColor: colorVars['--color-border'],
|
|
37
|
+
borderRadius: 0,
|
|
38
|
+
},
|
|
39
|
+
body: {
|
|
40
|
+
padding: spacingVars['--spacing-4'],
|
|
41
|
+
backgroundColor: colorVars['--color-background-muted'],
|
|
42
|
+
borderRadius: 0,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default function DrawerHeaderShowcase() {
|
|
47
|
+
return (
|
|
48
|
+
// Like the Drawer root, the panel is a padding boundary, so the Layout
|
|
49
|
+
// regions inset by their own --spacing-4 default: the drawer's inset.
|
|
50
|
+
<div {...stylex.props(styles.panel, overlayPaddingReset.reset)}>
|
|
51
|
+
<Layout
|
|
52
|
+
height="auto"
|
|
53
|
+
header={
|
|
54
|
+
<DrawerHeader
|
|
55
|
+
title="Host details"
|
|
56
|
+
subtitle="web-prod-04"
|
|
57
|
+
onOpenChange={() => {}}
|
|
58
|
+
hasDivider
|
|
59
|
+
/>
|
|
60
|
+
}
|
|
61
|
+
content={
|
|
62
|
+
<LayoutContent>
|
|
63
|
+
<div {...stylex.props(styles.body)}>
|
|
64
|
+
<Text type="body" color="secondary">
|
|
65
|
+
Drawer body content goes here.
|
|
66
|
+
</Text>
|
|
67
|
+
</div>
|
|
68
|
+
</LayoutContent>
|
|
69
|
+
}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'With subtitle and end content',
|
|
7
|
+
displayName: 'With subtitle and end content',
|
|
8
|
+
description:
|
|
9
|
+
'Add a subtitle under the title and put a status, such as a Badge, in endContent. The close button stays at the end of the row.',
|
|
10
|
+
exampleFor: 'DrawerHeader',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'Drawer',
|
|
15
|
+
'DrawerHeader',
|
|
16
|
+
'Badge',
|
|
17
|
+
'Button',
|
|
18
|
+
'Layout',
|
|
19
|
+
'LayoutContent',
|
|
20
|
+
'Text',
|
|
21
|
+
],
|
|
22
|
+
};
|