@astryxdesign/cli 0.5.2-canary.c9c8564 → 0.5.2-canary.fbfe729
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/assets/templates/blocks/components/ContextMenu/ContextMenuBasic.doc.mjs +1 -1
- package/assets/templates/blocks/components/ContextMenu/ContextMenuBasic.tsx +2 -1
- package/assets/templates/blocks/components/ContextMenu/ContextMenuBottomSheet.doc.mjs +14 -0
- package/assets/templates/blocks/components/ContextMenu/ContextMenuBottomSheet.tsx +59 -0
- package/assets/templates/blocks/components/ContextMenu/ContextMenuShowcase.doc.mjs +1 -1
- package/assets/templates/blocks/components/ContextMenu/ContextMenuShowcase.tsx +2 -1
- package/assets/templates/blocks/components/DropdownMenu/DropdownMenuBottomSheet.doc.mjs +15 -0
- package/assets/templates/blocks/components/DropdownMenu/DropdownMenuBottomSheet.tsx +49 -0
- package/assets/templates/blocks/components/MoreMenu/MoreMenuBottomSheet.doc.mjs +14 -0
- package/assets/templates/blocks/components/MoreMenu/MoreMenuBottomSheet.tsx +63 -0
- package/assets/templates/blocks/components/MultiSelector/MultiSelectorBottomSheet.doc.mjs +15 -0
- package/assets/templates/blocks/components/MultiSelector/MultiSelectorBottomSheet.tsx +26 -0
- package/assets/templates/blocks/components/Popover/PopoverBottomSheetAlternative.doc.mjs +22 -0
- package/assets/templates/blocks/components/Popover/PopoverBottomSheetAlternative.tsx +71 -0
- package/assets/templates/blocks/components/Selector/SelectorBottomSheet.doc.mjs +15 -0
- package/assets/templates/blocks/components/Selector/SelectorBottomSheet.tsx +30 -0
- package/authoring/doctypes/base/type.ts +91 -1
- package/authoring/doctypes/component/component.doc.mjs +7 -1
- package/authoring/index.d.ts +7 -0
- package/foundation/discovery/__fixtures__/component-accessibility-overlay.doc.d.mts +10 -0
- package/foundation/discovery/__fixtures__/component-accessibility-overlay.doc.mjs +14 -0
- package/foundation/discovery/component-loader.mjs +12 -5
- package/foundation/discovery/componentDocOverlay.test.mjs +6 -0
- package/package.json +9 -9
|
@@ -7,7 +7,7 @@ export const doc = {
|
|
|
7
7
|
name: 'ContextMenu — Basic',
|
|
8
8
|
displayName: 'ContextMenu — Basic',
|
|
9
9
|
description:
|
|
10
|
-
'
|
|
10
|
+
'An adaptive context area: long-press opens a BottomSheet on compact touch screens, while right-click opens a cursor-positioned menu elsewhere.',
|
|
11
11
|
isReady: true,
|
|
12
12
|
aspectRatio: 16 / 9,
|
|
13
13
|
componentsUsed: ['ContextMenu'],
|
|
@@ -7,6 +7,7 @@ import {ContextMenu} from '@astryxdesign/core/ContextMenu';
|
|
|
7
7
|
export default function ContextMenuBasic() {
|
|
8
8
|
return (
|
|
9
9
|
<ContextMenu
|
|
10
|
+
presentation="adaptive"
|
|
10
11
|
items={[
|
|
11
12
|
{label: 'Cut', onClick: () => {}},
|
|
12
13
|
{label: 'Copy', onClick: () => {}},
|
|
@@ -25,7 +26,7 @@ export default function ContextMenuBasic() {
|
|
|
25
26
|
color: '#6b7280',
|
|
26
27
|
userSelect: 'none',
|
|
27
28
|
}}>
|
|
28
|
-
|
|
29
|
+
Long-press or right-click this area
|
|
29
30
|
</div>
|
|
30
31
|
</ContextMenu>
|
|
31
32
|
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'ContextMenu',
|
|
7
|
+
name: 'ContextMenu — Bottom Sheet',
|
|
8
|
+
displayName: 'ContextMenu — Bottom Sheet',
|
|
9
|
+
description:
|
|
10
|
+
'A ContextMenu using the explicit BottomSheet presentation. Long-press the target on touch devices or right-click it with a pointer. Keep a visible menu trigger for important mobile actions.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['ContextMenu'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
DocumentDuplicateIcon,
|
|
7
|
+
PencilIcon,
|
|
8
|
+
ShareIcon,
|
|
9
|
+
TrashIcon,
|
|
10
|
+
} from '@heroicons/react/24/outline';
|
|
11
|
+
import {ContextMenu} from '@astryxdesign/core/ContextMenu';
|
|
12
|
+
|
|
13
|
+
export default function ContextMenuBottomSheet() {
|
|
14
|
+
return (
|
|
15
|
+
<ContextMenu
|
|
16
|
+
presentation="bottom-sheet"
|
|
17
|
+
label="Document actions"
|
|
18
|
+
items={[
|
|
19
|
+
{
|
|
20
|
+
label: 'Rename document',
|
|
21
|
+
description: 'Change the title shown to collaborators.',
|
|
22
|
+
icon: PencilIcon,
|
|
23
|
+
onClick: () => {},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Duplicate document',
|
|
27
|
+
description: 'Create a copy in the same workspace.',
|
|
28
|
+
icon: DocumentDuplicateIcon,
|
|
29
|
+
onClick: () => {},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: 'Share document',
|
|
33
|
+
description: 'Invite people or copy a share link.',
|
|
34
|
+
icon: ShareIcon,
|
|
35
|
+
onClick: () => {},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: 'Delete document',
|
|
39
|
+
description: 'Move this document to the trash.',
|
|
40
|
+
icon: TrashIcon,
|
|
41
|
+
variant: 'destructive',
|
|
42
|
+
onClick: () => {},
|
|
43
|
+
},
|
|
44
|
+
]}>
|
|
45
|
+
<div
|
|
46
|
+
style={{
|
|
47
|
+
display: 'flex',
|
|
48
|
+
flexDirection: 'column',
|
|
49
|
+
gap: '8px',
|
|
50
|
+
padding: '32px',
|
|
51
|
+
border: '1px solid #d1d5db',
|
|
52
|
+
borderRadius: '12px',
|
|
53
|
+
}}>
|
|
54
|
+
<strong>Quarterly plan</strong>
|
|
55
|
+
<span>Long-press on touch or right-click for document actions.</span>
|
|
56
|
+
</div>
|
|
57
|
+
</ContextMenu>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -6,7 +6,7 @@ export const doc = {
|
|
|
6
6
|
name: 'ContextMenu',
|
|
7
7
|
displayName: 'Context Menu',
|
|
8
8
|
description:
|
|
9
|
-
'
|
|
9
|
+
'An adaptive context area that supports long-press on compact touch screens and right-click elsewhere.',
|
|
10
10
|
isReady: true,
|
|
11
11
|
isShowcase: true,
|
|
12
12
|
aspectRatio: 1,
|
|
@@ -6,6 +6,7 @@ import {ContextMenu} from '@astryxdesign/core/ContextMenu';
|
|
|
6
6
|
export default function ContextMenuShowcase() {
|
|
7
7
|
return (
|
|
8
8
|
<ContextMenu
|
|
9
|
+
presentation="adaptive"
|
|
9
10
|
items={[
|
|
10
11
|
{label: 'Cut', onClick: () => {}},
|
|
11
12
|
{label: 'Copy', onClick: () => {}},
|
|
@@ -22,7 +23,7 @@ export default function ContextMenuShowcase() {
|
|
|
22
23
|
color: '#6b7280',
|
|
23
24
|
userSelect: 'none',
|
|
24
25
|
}}>
|
|
25
|
-
|
|
26
|
+
Long-press or right-click this area
|
|
26
27
|
</div>
|
|
27
28
|
</ContextMenu>
|
|
28
29
|
);
|
|
@@ -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
|
+
exampleFor: 'DropdownMenu',
|
|
7
|
+
alsoExampleFor: ['BottomSheet', 'useMediaQuery'],
|
|
8
|
+
name: 'DropdownMenu — Adaptive presentation',
|
|
9
|
+
displayName: 'DropdownMenu — Adaptive presentation',
|
|
10
|
+
description:
|
|
11
|
+
'Chooses a bottom sheet for compact touch surfaces and an anchored popover otherwise. The media query is product policy, while DropdownMenu owns both presentations.',
|
|
12
|
+
isReady: true,
|
|
13
|
+
aspectRatio: 3 / 4,
|
|
14
|
+
componentsUsed: ['DropdownMenu', 'Stack', 'Text', 'useMediaQuery'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {
|
|
7
|
+
ArchiveBoxIcon,
|
|
8
|
+
DocumentDuplicateIcon,
|
|
9
|
+
PencilIcon,
|
|
10
|
+
ShareIcon,
|
|
11
|
+
} from '@heroicons/react/24/outline';
|
|
12
|
+
import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
|
|
13
|
+
import {VStack} from '@astryxdesign/core/Stack';
|
|
14
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
15
|
+
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
16
|
+
|
|
17
|
+
const COMPACT_TOUCH_QUERY =
|
|
18
|
+
'(max-width: 639px) and (pointer: coarse) and (hover: none)';
|
|
19
|
+
|
|
20
|
+
const ACTIONS = [
|
|
21
|
+
{label: 'Edit project', icon: PencilIcon},
|
|
22
|
+
{label: 'Duplicate project', icon: DocumentDuplicateIcon},
|
|
23
|
+
{label: 'Share project', icon: ShareIcon},
|
|
24
|
+
{label: 'Archive project', icon: ArchiveBoxIcon},
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export default function DropdownMenuBottomSheet() {
|
|
28
|
+
const [lastAction, setLastAction] = useState<string | null>(null);
|
|
29
|
+
const isCompactTouchSurface = useMediaQuery(COMPACT_TOUCH_QUERY);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<VStack gap={3}>
|
|
33
|
+
<DropdownMenu
|
|
34
|
+
button={{label: 'Project actions'}}
|
|
35
|
+
presentation={isCompactTouchSurface ? 'bottom-sheet' : 'popover'}
|
|
36
|
+
items={ACTIONS.map(({label, icon}) => ({
|
|
37
|
+
label,
|
|
38
|
+
icon,
|
|
39
|
+
onClick: () => setLastAction(label),
|
|
40
|
+
}))}
|
|
41
|
+
/>
|
|
42
|
+
{lastAction && (
|
|
43
|
+
<Text type="supporting" color="secondary">
|
|
44
|
+
Last action: {lastAction}
|
|
45
|
+
</Text>
|
|
46
|
+
)}
|
|
47
|
+
</VStack>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'MoreMenu',
|
|
7
|
+
name: 'MoreMenu — Bottom Sheet',
|
|
8
|
+
displayName: 'MoreMenu — Bottom Sheet',
|
|
9
|
+
description:
|
|
10
|
+
'A visible overflow trigger that opens actions in a BottomSheet. Use this presentation for short action sets on compact touch surfaces.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['MoreMenu'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
DocumentDuplicateIcon,
|
|
7
|
+
PencilIcon,
|
|
8
|
+
ShareIcon,
|
|
9
|
+
TrashIcon,
|
|
10
|
+
} from '@heroicons/react/24/outline';
|
|
11
|
+
import {MoreMenu} from '@astryxdesign/core/MoreMenu';
|
|
12
|
+
|
|
13
|
+
export default function MoreMenuBottomSheet() {
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
style={{
|
|
17
|
+
display: 'flex',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
justifyContent: 'space-between',
|
|
20
|
+
gap: '16px',
|
|
21
|
+
width: 'min(100%, 360px)',
|
|
22
|
+
padding: '16px',
|
|
23
|
+
border: '1px solid #d1d5db',
|
|
24
|
+
borderRadius: '12px',
|
|
25
|
+
}}>
|
|
26
|
+
<div style={{display: 'flex', flexDirection: 'column', gap: '4px'}}>
|
|
27
|
+
<strong>Quarterly plan</strong>
|
|
28
|
+
<span>Updated a few minutes ago</span>
|
|
29
|
+
</div>
|
|
30
|
+
<MoreMenu
|
|
31
|
+
presentation="bottom-sheet"
|
|
32
|
+
label="Project actions"
|
|
33
|
+
items={[
|
|
34
|
+
{
|
|
35
|
+
label: 'Rename project',
|
|
36
|
+
description: 'Update the project title.',
|
|
37
|
+
icon: PencilIcon,
|
|
38
|
+
onClick: () => {},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: 'Duplicate project',
|
|
42
|
+
description: 'Create a copy in this workspace.',
|
|
43
|
+
icon: DocumentDuplicateIcon,
|
|
44
|
+
onClick: () => {},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'Share project',
|
|
48
|
+
description: 'Invite people to collaborate.',
|
|
49
|
+
icon: ShareIcon,
|
|
50
|
+
onClick: () => {},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: 'Delete project',
|
|
54
|
+
description: 'Move this project to the trash.',
|
|
55
|
+
icon: TrashIcon,
|
|
56
|
+
variant: 'destructive',
|
|
57
|
+
onClick: () => {},
|
|
58
|
+
},
|
|
59
|
+
]}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -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
|
+
exampleFor: 'MultiSelector',
|
|
7
|
+
alsoExampleFor: ['BottomSheet'],
|
|
8
|
+
name: 'MultiSelector — Bottom Sheet',
|
|
9
|
+
displayName: 'MultiSelector — Bottom Sheet',
|
|
10
|
+
description:
|
|
11
|
+
'Keeps a multi-selection list open in a bottom sheet while choices are toggled.',
|
|
12
|
+
isReady: true,
|
|
13
|
+
aspectRatio: 3 / 4,
|
|
14
|
+
componentsUsed: ['MultiSelector'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {MultiSelector} from '@astryxdesign/core/MultiSelector';
|
|
7
|
+
|
|
8
|
+
const OPTIONS = ['Design', 'Engineering', 'Marketing', 'Operations'];
|
|
9
|
+
|
|
10
|
+
export default function MultiSelectorBottomSheet() {
|
|
11
|
+
const [value, setValue] = useState<string[]>([]);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div style={{width: 320, maxWidth: '100%'}}>
|
|
15
|
+
<MultiSelector
|
|
16
|
+
label="Teams"
|
|
17
|
+
options={OPTIONS}
|
|
18
|
+
value={value}
|
|
19
|
+
onChange={setValue}
|
|
20
|
+
placeholder="Choose teams"
|
|
21
|
+
hasSelectAll
|
|
22
|
+
presentation="bottom-sheet"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -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
|
+
exampleFor: 'Popover',
|
|
7
|
+
name: 'Popover — Bottom Sheet Alternative',
|
|
8
|
+
displayName: 'Popover — Bottom Sheet Alternative',
|
|
9
|
+
description:
|
|
10
|
+
'Opt-in BottomSheet alternative for compact touch surfaces where actions should use a modal presentation anchored to the bottom edge.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'BottomSheet',
|
|
15
|
+
'Button',
|
|
16
|
+
'Icon',
|
|
17
|
+
'Section',
|
|
18
|
+
'Layout',
|
|
19
|
+
'Text',
|
|
20
|
+
'List',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import * as stylex from '@stylexjs/stylex';
|
|
7
|
+
import {CalendarIcon, TagIcon, UserIcon} from '@heroicons/react/24/outline';
|
|
8
|
+
import {BottomSheet} from '@astryxdesign/core/BottomSheet';
|
|
9
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
10
|
+
import {Icon} from '@astryxdesign/core/Icon';
|
|
11
|
+
import {List, ListItem} from '@astryxdesign/core/List';
|
|
12
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
13
|
+
import {Section} from '@astryxdesign/core/Section';
|
|
14
|
+
import {Heading, Text} from '@astryxdesign/core/Text';
|
|
15
|
+
import {spacingVars} from '@astryxdesign/core/theme/tokens.stylex';
|
|
16
|
+
|
|
17
|
+
const styles = stylex.create({
|
|
18
|
+
header: {
|
|
19
|
+
// Match the unchanged spacious ListItem inset so the title and description
|
|
20
|
+
// align with the action icons.
|
|
21
|
+
marginInlineStart: spacingVars['--spacing-3'],
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const PROJECT_ACTIONS = [
|
|
26
|
+
[UserIcon, 'Assign owner', 'Route follow-up to a teammate.'],
|
|
27
|
+
[TagIcon, 'Add label', 'Group this item with related work.'],
|
|
28
|
+
[CalendarIcon, 'Set due date', 'Pick a reminder for review.'],
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
export default function PopoverBottomSheetAlternative() {
|
|
32
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<Button label="Open project actions" onClick={() => setIsOpen(true)}>
|
|
37
|
+
Open project actions
|
|
38
|
+
</Button>
|
|
39
|
+
<BottomSheet
|
|
40
|
+
isOpen={isOpen}
|
|
41
|
+
onOpenChange={setIsOpen}
|
|
42
|
+
label="Project actions"
|
|
43
|
+
height="hug">
|
|
44
|
+
<Section paddingBlock={4} paddingInline={1}>
|
|
45
|
+
<VStack gap={3}>
|
|
46
|
+
<VStack gap={1} xstyle={styles.header}>
|
|
47
|
+
<Heading level={3}>Project actions</Heading>
|
|
48
|
+
<Text type="supporting" color="secondary">
|
|
49
|
+
Use this modal touch surface when the task should move away from
|
|
50
|
+
its trigger and stay close to the bottom edge.
|
|
51
|
+
</Text>
|
|
52
|
+
</VStack>
|
|
53
|
+
<List density="spacious">
|
|
54
|
+
{PROJECT_ACTIONS.map(([icon, label, description]) => (
|
|
55
|
+
<ListItem
|
|
56
|
+
key={label}
|
|
57
|
+
label={label}
|
|
58
|
+
description={description}
|
|
59
|
+
startContent={
|
|
60
|
+
<Icon icon={icon} size="md" color="secondary" />
|
|
61
|
+
}
|
|
62
|
+
onClick={() => setIsOpen(false)}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</List>
|
|
66
|
+
</VStack>
|
|
67
|
+
</Section>
|
|
68
|
+
</BottomSheet>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -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
|
+
exampleFor: 'Selector',
|
|
7
|
+
alsoExampleFor: ['BottomSheet'],
|
|
8
|
+
name: 'Selector — Bottom Sheet',
|
|
9
|
+
displayName: 'Selector — Bottom Sheet',
|
|
10
|
+
description:
|
|
11
|
+
'Presents a single-selection list in a bottom sheet for compact touch interfaces.',
|
|
12
|
+
isReady: true,
|
|
13
|
+
aspectRatio: 3 / 4,
|
|
14
|
+
componentsUsed: ['Selector'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {Selector} from '@astryxdesign/core/Selector';
|
|
7
|
+
|
|
8
|
+
const OPTIONS = [
|
|
9
|
+
{value: 'design', label: 'Design'},
|
|
10
|
+
{value: 'engineering', label: 'Engineering'},
|
|
11
|
+
{value: 'marketing', label: 'Marketing'},
|
|
12
|
+
{value: 'operations', label: 'Operations'},
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export default function SelectorBottomSheet() {
|
|
16
|
+
const [value, setValue] = useState<string | undefined>();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div style={{width: 320, maxWidth: '100%'}}>
|
|
20
|
+
<Selector
|
|
21
|
+
label="Team"
|
|
22
|
+
options={OPTIONS}
|
|
23
|
+
value={value}
|
|
24
|
+
onChange={setValue}
|
|
25
|
+
placeholder="Choose a team"
|
|
26
|
+
presentation="bottom-sheet"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -64,8 +64,96 @@ export interface ComponentBestPractice {
|
|
|
64
64
|
export interface ComponentAccessibilityRequirement {
|
|
65
65
|
/** Short scannable label, e.g. `"Accessible name"` or `"Loading"`. */
|
|
66
66
|
name: string;
|
|
67
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* The accessibility contract consumers must preserve. Write at about a
|
|
69
|
+
* grade-7 reading level with short sentences, common words, and active
|
|
70
|
+
* voice. For color contrast, put the ratio in `requirement`; name the exact
|
|
71
|
+
* foreground, background, state, and any overlay in `description`; explain
|
|
72
|
+
* exceptions in plain language; and include enough detail for a human or
|
|
73
|
+
* agent to reproduce the check.
|
|
74
|
+
*/
|
|
68
75
|
description: string;
|
|
76
|
+
/** Groups related requirements in the docsite Accessibility tab. */
|
|
77
|
+
category?: 'Color contrast' | 'Keyboard' | 'Semantics' | 'Content';
|
|
78
|
+
/** Relevant WCAG success criterion, e.g. `"1.4.3 Contrast (Minimum)"`. */
|
|
79
|
+
criterion?: string;
|
|
80
|
+
/** Short threshold or rule, e.g. `"4.5:1"`, `"3:1"`, or `"Exempt"`. */
|
|
81
|
+
requirement?: string;
|
|
82
|
+
/** Component states covered by this requirement. */
|
|
83
|
+
states?: string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ComponentAccessibilityThemeStatus = 'Pass' | 'Fail' | 'Not tested';
|
|
87
|
+
|
|
88
|
+
export type ComponentAccessibilityThemeApplicability =
|
|
89
|
+
'Required' | 'Conditional' | 'Supplemental' | 'Decorative';
|
|
90
|
+
|
|
91
|
+
export interface ComponentAccessibilityThemeMeasurement {
|
|
92
|
+
/** Column heading, e.g. `"Rest"` or `"Spinner"`. */
|
|
93
|
+
label: string;
|
|
94
|
+
/** Display value, e.g. `"15.13:1"`. */
|
|
95
|
+
value: string;
|
|
96
|
+
/** Optional supporting detail shown below the value, such as a worst case. */
|
|
97
|
+
detail?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Whether this measurement is required for every use, required only in some
|
|
100
|
+
* contexts, shown as a supplemental cue, or decorative. Each theme declares
|
|
101
|
+
* this intent, informed by the component contract. Never infer it from the
|
|
102
|
+
* measured ratio. Non-required measurements do not determine row status.
|
|
103
|
+
*/
|
|
104
|
+
applicability?: ComponentAccessibilityThemeApplicability;
|
|
105
|
+
/** Rendered foreground and background colors used for this measurement. */
|
|
106
|
+
colorPair?: {
|
|
107
|
+
foreground: string;
|
|
108
|
+
background: string;
|
|
109
|
+
};
|
|
110
|
+
/** Optional per-variant results shown from a compact details trigger. */
|
|
111
|
+
breakdown?: Array<{
|
|
112
|
+
label: string;
|
|
113
|
+
value: string;
|
|
114
|
+
detail?: string;
|
|
115
|
+
colorPair: {
|
|
116
|
+
foreground: string;
|
|
117
|
+
background: string;
|
|
118
|
+
};
|
|
119
|
+
status?: 'Pass' | 'Fail';
|
|
120
|
+
}>;
|
|
121
|
+
/** Mark a failed measurement so the docsite can emphasize it. */
|
|
122
|
+
status?: 'Pass' | 'Fail';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ComponentAccessibilityThemeResult {
|
|
126
|
+
/** Row heading, usually a component variant. */
|
|
127
|
+
name: string;
|
|
128
|
+
/** Measurements shown between the row heading and status. */
|
|
129
|
+
measurements: ComponentAccessibilityThemeMeasurement[];
|
|
130
|
+
/** Overall result for the row. */
|
|
131
|
+
status: ComponentAccessibilityThemeStatus;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ComponentAccessibilityThemeMode {
|
|
135
|
+
/** Theme mode covered by these results. */
|
|
136
|
+
mode: 'Light' | 'Dark';
|
|
137
|
+
/** Detailed results for the component in this mode. */
|
|
138
|
+
results: ComponentAccessibilityThemeResult[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface ComponentAccessibilityThemeTable {
|
|
142
|
+
/** Optional heading for one complete group of measurements. */
|
|
143
|
+
title?: string;
|
|
144
|
+
/** Explains the scope of this measurement group. */
|
|
145
|
+
description?: string;
|
|
146
|
+
/** Detailed results separated by theme mode. */
|
|
147
|
+
modes: ComponentAccessibilityThemeMode[];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface ComponentAccessibilityThemeCoverage {
|
|
151
|
+
/** Display name of the audited theme. */
|
|
152
|
+
theme: string;
|
|
153
|
+
/** Complete groups of measurements for this theme and component. */
|
|
154
|
+
tables: ComponentAccessibilityThemeTable[];
|
|
155
|
+
/** Theme visuals intentionally excluded from measurement, with a reason. */
|
|
156
|
+
notMeasured?: string[];
|
|
69
157
|
}
|
|
70
158
|
|
|
71
159
|
/**
|
|
@@ -462,6 +550,8 @@ export interface UsageDoc {
|
|
|
462
550
|
/** Accessibility requirements specific to this component and its supported
|
|
463
551
|
* content combinations. Generic audit procedure stays in the wiki rubric. */
|
|
464
552
|
accessibility?: ComponentAccessibilityRequirement[];
|
|
553
|
+
/** Verified color-accessibility coverage for bundled themes. */
|
|
554
|
+
accessibilityThemeCoverage?: ComponentAccessibilityThemeCoverage[];
|
|
465
555
|
/** Structural/visual anatomy of the component. Each entry describes one
|
|
466
556
|
* element that makes up the component (icon slot, label, container, etc.).
|
|
467
557
|
* Order entries in the visual reading order (leading → trailing, top → bottom). */
|
|
@@ -132,7 +132,13 @@ export const doc = {
|
|
|
132
132
|
name: 'usage.accessibility',
|
|
133
133
|
type: 'ComponentAccessibilityRequirement[]',
|
|
134
134
|
description:
|
|
135
|
-
'Component-specific
|
|
135
|
+
'Component-specific requirements rendered in the shared Accessibility tab. Write at about a grade-7 reading level with short sentences, common words, and active voice. For color contrast, put the ratio in `requirement`; name the exact foreground, background, state, and any overlay in `description`; explain exceptions in plain language; and give a human or agent enough detail to reproduce the check. Keep repository audit procedures in the wiki rubric.',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'usage.accessibilityThemeCoverage',
|
|
139
|
+
type: 'ComponentAccessibilityThemeCoverage[]',
|
|
140
|
+
description:
|
|
141
|
+
'Verified per-theme accessibility measurements rendered in the shared Accessibility tab. Record light and dark mode separately, include rendered color pairs, and mark failed measurements. Put visuals excluded from the audit in `notMeasured` with a short reason; do not add them as table measurements. Each theme declares `applicability` for measured values, informed by the component contract and never inferred from the ratio: `Conditional` is required only in some contexts, `Supplemental` adds another meaningful cue, and `Decorative` has no required meaning. These values do not change row status. Provide a complete breakdown when one cell summarizes multiple combinations, and protect derived values with an automated audit.',
|
|
136
142
|
},
|
|
137
143
|
{
|
|
138
144
|
name: 'usage.anatomy',
|
package/authoring/index.d.ts
CHANGED
|
@@ -68,6 +68,13 @@ export type {
|
|
|
68
68
|
ComponentExampleDoc,
|
|
69
69
|
ComponentAnatomyElement,
|
|
70
70
|
ComponentAccessibilityRequirement,
|
|
71
|
+
ComponentAccessibilityThemeStatus,
|
|
72
|
+
ComponentAccessibilityThemeApplicability,
|
|
73
|
+
ComponentAccessibilityThemeMeasurement,
|
|
74
|
+
ComponentAccessibilityThemeResult,
|
|
75
|
+
ComponentAccessibilityThemeMode,
|
|
76
|
+
ComponentAccessibilityThemeTable,
|
|
77
|
+
ComponentAccessibilityThemeCoverage,
|
|
71
78
|
ComponentBestPractice,
|
|
72
79
|
ComponentSlotElement,
|
|
73
80
|
ComponentPlaygroundConfig,
|
|
@@ -15,6 +15,16 @@ export namespace docs {
|
|
|
15
15
|
name: string;
|
|
16
16
|
description: string;
|
|
17
17
|
}[];
|
|
18
|
+
let accessibilityThemeCoverage: {
|
|
19
|
+
theme: string;
|
|
20
|
+
tables: {
|
|
21
|
+
modes: {
|
|
22
|
+
mode: string;
|
|
23
|
+
results: never[];
|
|
24
|
+
}[];
|
|
25
|
+
}[];
|
|
26
|
+
notMeasured: string[];
|
|
27
|
+
}[];
|
|
18
28
|
let anatomy: {
|
|
19
29
|
name: string;
|
|
20
30
|
required: boolean;
|
|
@@ -18,6 +18,20 @@ export const docs = {
|
|
|
18
18
|
description: 'Provide an accessible name.',
|
|
19
19
|
},
|
|
20
20
|
],
|
|
21
|
+
accessibilityThemeCoverage: [
|
|
22
|
+
{
|
|
23
|
+
theme: 'Fixture',
|
|
24
|
+
tables: [
|
|
25
|
+
{
|
|
26
|
+
modes: [
|
|
27
|
+
{mode: 'Light', results: []},
|
|
28
|
+
{mode: 'Dark', results: []},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
notMeasured: ['Decorative track — Not part of the contrast audit.'],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
21
35
|
anatomy: [
|
|
22
36
|
{
|
|
23
37
|
name: 'Base-only anatomy',
|
|
@@ -194,16 +194,23 @@ function overlayComponentDoc(docs, translation) {
|
|
|
194
194
|
});
|
|
195
195
|
};
|
|
196
196
|
|
|
197
|
-
/** Preserve
|
|
197
|
+
/** Preserve structured accessibility data without changing established translated output.
|
|
198
198
|
* @param {any} baseUsage
|
|
199
199
|
* @param {any} translatedUsage
|
|
200
200
|
*/
|
|
201
201
|
const mergeUsage = (baseUsage, translatedUsage) => {
|
|
202
202
|
if (!translatedUsage) return baseUsage;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
return {
|
|
204
|
+
...translatedUsage,
|
|
205
|
+
...(translatedUsage.accessibility === undefined &&
|
|
206
|
+
baseUsage?.accessibility !== undefined
|
|
207
|
+
? {accessibility: baseUsage.accessibility}
|
|
208
|
+
: null),
|
|
209
|
+
...(translatedUsage.accessibilityThemeCoverage === undefined &&
|
|
210
|
+
baseUsage?.accessibilityThemeCoverage !== undefined
|
|
211
|
+
? {accessibilityThemeCoverage: baseUsage.accessibilityThemeCoverage}
|
|
212
|
+
: null),
|
|
213
|
+
};
|
|
207
214
|
};
|
|
208
215
|
|
|
209
216
|
const merged = {...docs, ...translation, usage: mergeUsage(docs.usage, translation.usage)};
|
|
@@ -120,6 +120,12 @@ describe('the reported symptom', () => {
|
|
|
120
120
|
|
|
121
121
|
expect(english.usage.accessibility.length).toBeGreaterThan(0);
|
|
122
122
|
expect(dense.usage.accessibility).toEqual(english.usage.accessibility);
|
|
123
|
+
expect(dense.usage.accessibilityThemeCoverage).toEqual(
|
|
124
|
+
english.usage.accessibilityThemeCoverage,
|
|
125
|
+
);
|
|
126
|
+
expect(dense.usage.accessibilityThemeCoverage[0].notMeasured).toContain(
|
|
127
|
+
'Decorative track — Not part of the contrast audit.',
|
|
128
|
+
);
|
|
123
129
|
expect(dense.usage.bestPractices).toBeUndefined();
|
|
124
130
|
expect(dense.usage.anatomy).toBeUndefined();
|
|
125
131
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.5.2-canary.
|
|
3
|
+
"version": "0.5.2-canary.fbfe729",
|
|
4
4
|
"displayName": "CLI",
|
|
5
5
|
"description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"zod": "^4.4.3"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
-
"@astryxdesign/charts": "0.5.2-canary.
|
|
91
|
-
"@astryxdesign/core": "0.5.2-canary.
|
|
92
|
-
"@astryxdesign/lab": "0.5.2-canary.
|
|
93
|
-
"@astryxdesign/theme-neutral": "0.5.2-canary.
|
|
90
|
+
"@astryxdesign/charts": "0.5.2-canary.fbfe729",
|
|
91
|
+
"@astryxdesign/core": "0.5.2-canary.fbfe729",
|
|
92
|
+
"@astryxdesign/lab": "0.5.2-canary.fbfe729",
|
|
93
|
+
"@astryxdesign/theme-neutral": "0.5.2-canary.fbfe729",
|
|
94
94
|
"gpt-tokenizer": "^3.4.0"
|
|
95
95
|
},
|
|
96
96
|
"peerDependenciesMeta": {
|
|
@@ -108,10 +108,10 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
|
-
"@astryxdesign/charts": "0.5.2-canary.
|
|
112
|
-
"@astryxdesign/core": "0.5.2-canary.
|
|
113
|
-
"@astryxdesign/lab": "0.5.2-canary.
|
|
114
|
-
"@astryxdesign/theme-neutral": "0.5.2-canary.
|
|
111
|
+
"@astryxdesign/charts": "0.5.2-canary.fbfe729",
|
|
112
|
+
"@astryxdesign/core": "0.5.2-canary.fbfe729",
|
|
113
|
+
"@astryxdesign/lab": "0.5.2-canary.fbfe729",
|
|
114
|
+
"@astryxdesign/theme-neutral": "0.5.2-canary.fbfe729",
|
|
115
115
|
"gpt-tokenizer": "^3.4.0"
|
|
116
116
|
},
|
|
117
117
|
"scripts": {
|