@astryxdesign/lab 0.7.2-canary.9fa2213 → 0.7.2-canary.a2f0c93
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/blocks/CommentComposerShowcase.doc.mjs +15 -0
- package/blocks/CommentComposerShowcase.tsx +45 -0
- package/blocks/CommentPinInMargin.doc.mjs +14 -0
- package/blocks/CommentPinInMargin.tsx +77 -0
- package/blocks/CommentPinShowcase.doc.mjs +15 -0
- package/blocks/CommentPinShowcase.tsx +188 -0
- package/blocks/CommentThreadInPopover.doc.mjs +21 -0
- package/blocks/CommentThreadInPopover.tsx +89 -0
- package/blocks/CommentThreadShowcase.doc.mjs +15 -0
- package/blocks/CommentThreadShowcase.tsx +67 -0
- package/blocks/CommentsInCard.doc.mjs +14 -0
- package/blocks/CommentsInCard.tsx +78 -0
- package/blocks/CommentsInline.doc.mjs +14 -0
- package/blocks/CommentsInline.tsx +297 -0
- package/blocks/CommentsShowcase.doc.mjs +15 -0
- package/blocks/CommentsShowcase.tsx +341 -0
- package/dist/Chat/ChatTypingIndicator.d.ts.map +1 -1
- package/dist/Chat/ChatTypingIndicator.js +1 -1
- package/dist/CommentPin/CommentPin.d.ts +86 -0
- package/dist/CommentPin/CommentPin.d.ts.map +1 -0
- package/dist/CommentPin/CommentPin.js +260 -0
- package/dist/CommentPin/index.d.ts +9 -0
- package/dist/CommentPin/index.d.ts.map +1 -0
- package/dist/CommentPin/index.js +11 -0
- package/dist/Comments/CommentActions.d.ts +55 -0
- package/dist/Comments/CommentActions.d.ts.map +1 -0
- package/dist/Comments/CommentActions.js +472 -0
- package/dist/Comments/CommentComposer.d.ts +108 -0
- package/dist/Comments/CommentComposer.d.ts.map +1 -0
- package/dist/Comments/CommentComposer.js +507 -0
- package/dist/Comments/CommentMarkdown.d.ts +15 -0
- package/dist/Comments/CommentMarkdown.d.ts.map +1 -0
- package/dist/Comments/CommentMarkdown.js +136 -0
- package/dist/Comments/CommentParts.d.ts +238 -0
- package/dist/Comments/CommentParts.d.ts.map +1 -0
- package/dist/Comments/CommentParts.js +823 -0
- package/dist/Comments/CommentRow.d.ts +20 -0
- package/dist/Comments/CommentRow.d.ts.map +1 -0
- package/dist/Comments/CommentRow.js +693 -0
- package/dist/Comments/CommentThread.d.ts +66 -0
- package/dist/Comments/CommentThread.d.ts.map +1 -0
- package/dist/Comments/CommentThread.js +169 -0
- package/dist/Comments/Comments.d.ts +105 -0
- package/dist/Comments/Comments.d.ts.map +1 -0
- package/dist/Comments/Comments.js +426 -0
- package/dist/Comments/CommentsContext.d.ts +117 -0
- package/dist/Comments/CommentsContext.d.ts.map +1 -0
- package/dist/Comments/CommentsContext.js +515 -0
- package/dist/Comments/i18n.d.ts +18 -0
- package/dist/Comments/i18n.d.ts.map +1 -0
- package/dist/Comments/i18n.js +34 -0
- package/dist/Comments/icons.d.ts +24 -0
- package/dist/Comments/icons.d.ts.map +1 -0
- package/dist/Comments/icons.js +93 -0
- package/dist/Comments/index.d.ts +16 -0
- package/dist/Comments/index.d.ts.map +1 -0
- package/dist/Comments/index.js +15 -0
- package/dist/Comments/types.d.ts +341 -0
- package/dist/Comments/types.d.ts.map +1 -0
- package/dist/Comments/types.js +100 -0
- package/dist/Comments/useCommentThreadDismissal.d.ts +88 -0
- package/dist/Comments/useCommentThreadDismissal.d.ts.map +1 -0
- package/dist/Comments/useCommentThreadDismissal.js +344 -0
- package/dist/Comments/utils.d.ts +44 -0
- package/dist/Comments/utils.d.ts.map +1 -0
- package/dist/Comments/utils.js +84 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/lab.css +98 -46
- package/package.json +4 -4
- package/src/Chat/ChatTypingIndicator.tsx +3 -0
- package/src/CommentPin/CommentPin.doc.mjs +328 -0
- package/src/CommentPin/CommentPin.spec.md +307 -0
- package/src/CommentPin/CommentPin.test.tsx +350 -0
- package/src/CommentPin/CommentPin.tsx +484 -0
- package/src/CommentPin/index.ts +13 -0
- package/src/Comments/CommentActions.tsx +542 -0
- package/src/Comments/CommentComposer.doc.mjs +177 -0
- package/src/Comments/CommentComposer.test.tsx +255 -0
- package/src/Comments/CommentComposer.tsx +680 -0
- package/src/Comments/CommentMarkdown.tsx +184 -0
- package/src/Comments/CommentParts.tsx +937 -0
- package/src/Comments/CommentRow.tsx +918 -0
- package/src/Comments/CommentThread.doc.mjs +580 -0
- package/src/Comments/CommentThread.test.tsx +634 -0
- package/src/Comments/CommentThread.tsx +226 -0
- package/src/Comments/Comments.doc.mjs +648 -0
- package/src/Comments/Comments.test.tsx +1335 -0
- package/src/Comments/Comments.tsx +643 -0
- package/src/Comments/CommentsContext.tsx +755 -0
- package/src/Comments/i18n.ts +40 -0
- package/src/Comments/icons.tsx +129 -0
- package/src/Comments/index.ts +39 -0
- package/src/Comments/types.ts +392 -0
- package/src/Comments/useCommentThreadDismissal.ts +426 -0
- package/src/Comments/utils.ts +114 -0
- package/src/index.ts +37 -8
- package/blocks/DrawerFullWidthMobile.doc.mjs +0 -21
- package/blocks/DrawerFullWidthMobile.tsx +0 -42
- package/blocks/DrawerHeaderShowcase.doc.mjs +0 -15
- package/blocks/DrawerHeaderShowcase.tsx +0 -73
- package/blocks/DrawerHeaderSubtitleEndContent.doc.mjs +0 -22
- package/blocks/DrawerHeaderSubtitleEndContent.tsx +0 -49
- package/blocks/DrawerHeaderWithCloseButton.doc.mjs +0 -21
- package/blocks/DrawerHeaderWithCloseButton.tsx +0 -42
- package/blocks/DrawerMobileReveal.doc.mjs +0 -21
- package/blocks/DrawerMobileReveal.tsx +0 -43
- package/blocks/DrawerScrim.doc.mjs +0 -22
- package/blocks/DrawerScrim.tsx +0 -68
- package/blocks/DrawerShowcase.doc.mjs +0 -22
- package/blocks/DrawerShowcase.tsx +0 -41
- package/blocks/DrawerStart.doc.mjs +0 -21
- package/blocks/DrawerStart.tsx +0 -41
- package/blocks/DrawerWidths.doc.mjs +0 -22
- package/blocks/DrawerWidths.tsx +0 -54
- package/dist/Drawer/Drawer.d.ts +0 -181
- package/dist/Drawer/Drawer.d.ts.map +0 -1
- package/dist/Drawer/Drawer.js +0 -388
- package/dist/Drawer/DrawerHeader.d.ts +0 -82
- package/dist/Drawer/DrawerHeader.d.ts.map +0 -1
- package/dist/Drawer/DrawerHeader.js +0 -131
- package/dist/Drawer/index.d.ts +0 -11
- package/dist/Drawer/index.d.ts.map +0 -1
- package/dist/Drawer/index.js +0 -11
- package/dist/Drawer/useDrawerDialogPresence.d.ts +0 -42
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +0 -1
- package/dist/Drawer/useDrawerDialogPresence.js +0 -235
- package/src/Drawer/Drawer.doc.mjs +0 -470
- package/src/Drawer/Drawer.spec.md +0 -343
- package/src/Drawer/Drawer.test.tsx +0 -1039
- package/src/Drawer/Drawer.tsx +0 -618
- package/src/Drawer/DrawerHeader.doc.mjs +0 -291
- package/src/Drawer/DrawerHeader.test.tsx +0 -89
- package/src/Drawer/DrawerHeader.tsx +0 -237
- package/src/Drawer/index.ts +0 -13
- package/src/Drawer/useDrawerDialogPresence.ts +0 -283
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'New comment',
|
|
7
|
+
displayName: 'New comment',
|
|
8
|
+
description:
|
|
9
|
+
'A comment composer quoting the passage it comments on: type, add an emoji, and post with Enter or Post.',
|
|
10
|
+
exampleFor: 'CommentComposer',
|
|
11
|
+
isReady: true,
|
|
12
|
+
isShowcase: true,
|
|
13
|
+
aspectRatio: 16 / 9,
|
|
14
|
+
componentsUsed: ['CommentComposer', 'VStack', 'Text'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input A controlled draft and a list of posted comments
|
|
7
|
+
* @output A comment composer that posts into a short list above it
|
|
8
|
+
* @position Lab CommentComposer's docsite showcase and copyable CLI block
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
13
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
14
|
+
import {CommentComposer} from '@astryxdesign/lab';
|
|
15
|
+
|
|
16
|
+
export default function CommentComposerShowcase() {
|
|
17
|
+
const [draft, setDraft] = useState('');
|
|
18
|
+
const [posted, setPosted] = useState<string[]>([]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<VStack gap={3} width={420}>
|
|
22
|
+
{posted.length === 0 ? (
|
|
23
|
+
<Text type="supporting" color="secondary">
|
|
24
|
+
Nothing posted yet.
|
|
25
|
+
</Text>
|
|
26
|
+
) : (
|
|
27
|
+
posted.map((body, index) => (
|
|
28
|
+
<Text key={index} type="body">
|
|
29
|
+
{body}
|
|
30
|
+
</Text>
|
|
31
|
+
))
|
|
32
|
+
)}
|
|
33
|
+
<CommentComposer
|
|
34
|
+
value={draft}
|
|
35
|
+
onChange={setDraft}
|
|
36
|
+
onSubmit={body => {
|
|
37
|
+
setPosted(current => [...current, body]);
|
|
38
|
+
setDraft('');
|
|
39
|
+
}}
|
|
40
|
+
anchor={{quote: 'Guardrail alerts page the release oncall.'}}
|
|
41
|
+
onAnchorDismiss={() => {}}
|
|
42
|
+
/>
|
|
43
|
+
</VStack>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Host-owned placement',
|
|
7
|
+
displayName: 'Comment pin in a document margin',
|
|
8
|
+
description:
|
|
9
|
+
'A caller-positioned CommentPin in a document margin. The host maps its anchor to the pin’s logical bottom-start corner and owns the surrounding layout; the pin owns its compact interactive surface. Activating the pin toggles its selected state.',
|
|
10
|
+
exampleFor: 'CommentPin',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: ['Card', 'CommentPin', 'Text', 'VStack'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input A host-positioned CommentPin, its lead commenter, and local
|
|
7
|
+
* selected state
|
|
8
|
+
* @output A document passage with one comment pin in its margin; activating
|
|
9
|
+
* the pin toggles its selection and aria-pressed state
|
|
10
|
+
* @position Copyable Lab CommentPin placement example
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {useState} from 'react';
|
|
14
|
+
import * as stylex from '@stylexjs/stylex';
|
|
15
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
16
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
17
|
+
import {VStack} from '@astryxdesign/core/Stack';
|
|
18
|
+
import {colorVars, spacingVars} from '@astryxdesign/core/theme/tokens.stylex';
|
|
19
|
+
import {CommentPin} from '@astryxdesign/lab';
|
|
20
|
+
|
|
21
|
+
const styles = stylex.create({
|
|
22
|
+
document: {
|
|
23
|
+
display: 'grid',
|
|
24
|
+
gridTemplateColumns: 'minmax(0, 1fr) 64px',
|
|
25
|
+
minHeight: 220,
|
|
26
|
+
},
|
|
27
|
+
content: {
|
|
28
|
+
padding: spacingVars['--spacing-5'],
|
|
29
|
+
},
|
|
30
|
+
margin: {
|
|
31
|
+
position: 'relative',
|
|
32
|
+
borderInlineStartWidth: 1,
|
|
33
|
+
borderInlineStartStyle: 'solid',
|
|
34
|
+
borderInlineStartColor: colorVars['--color-border'],
|
|
35
|
+
backgroundColor: colorVars['--color-background-muted'],
|
|
36
|
+
},
|
|
37
|
+
// Inset far enough from the clipped Card edges that the selected ring and
|
|
38
|
+
// the farther-out keyboard focus outline stay fully visible.
|
|
39
|
+
pin: {
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
insetInlineStart: spacingVars['--spacing-3'],
|
|
42
|
+
insetBlockStart: spacingVars['--spacing-10'],
|
|
43
|
+
transform: 'translateY(-100%)',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export default function CommentPinInMargin() {
|
|
48
|
+
const [isSelected, setIsSelected] = useState(false);
|
|
49
|
+
return (
|
|
50
|
+
<Card padding={0} width={520}>
|
|
51
|
+
<div {...stylex.props(styles.document)}>
|
|
52
|
+
<VStack gap={3} xstyle={styles.content}>
|
|
53
|
+
<Text as="h2" type="display-3" weight="bold">
|
|
54
|
+
Review notes
|
|
55
|
+
</Text>
|
|
56
|
+
<Text type="body">
|
|
57
|
+
The host owns the document anchor and margin layout. The pin stays a
|
|
58
|
+
compact action that identifies the lead commenter.
|
|
59
|
+
</Text>
|
|
60
|
+
<Text type="supporting" color="secondary">
|
|
61
|
+
Selecting the pin can open a thread beside this passage or in a
|
|
62
|
+
shared comments panel.
|
|
63
|
+
</Text>
|
|
64
|
+
</VStack>
|
|
65
|
+
<div aria-label="Comment margin" {...stylex.props(styles.margin)}>
|
|
66
|
+
<CommentPin
|
|
67
|
+
label="Open comment from Maya"
|
|
68
|
+
participants={[{id: 'maya', name: 'Maya Chen'}]}
|
|
69
|
+
isSelected={isSelected}
|
|
70
|
+
onClick={() => setIsSelected(current => !current)}
|
|
71
|
+
xstyle={styles.pin}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</Card>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Avatar-first annotations',
|
|
7
|
+
displayName: 'Avatar-first annotations',
|
|
8
|
+
description:
|
|
9
|
+
'Caller-positioned comment pins over media and in a content margin. Every coordinate maps to the pin’s logical bottom-start corner. The selected group caps its participants at three circles (two Avatars plus +3) and shows its unread count in an accent badge instead of the total; the unread margin pin shows the badge too. Other pins dim without becoming disabled, activating the selected pin again clears the selection, and no pin dims while nothing is selected.',
|
|
10
|
+
exampleFor: 'CommentPin',
|
|
11
|
+
isReady: true,
|
|
12
|
+
isShowcase: true,
|
|
13
|
+
aspectRatio: 16 / 9,
|
|
14
|
+
componentsUsed: ['CommentPin', 'Text', 'VStack'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Three caller-positioned comment pins, logical bottom-start anchors,
|
|
7
|
+
* participant/thread/unread counts, and controlled selected/dimmed state
|
|
8
|
+
* that may be empty
|
|
9
|
+
* @output A grouped pin with a three-circle participant cap and an unread
|
|
10
|
+
* badge, a resolved pin, and an unread margin pin; activating the selected
|
|
11
|
+
* pin clears the selection, and non-selected pins dim only while another pin
|
|
12
|
+
* is selected
|
|
13
|
+
* @position CommentPin docsite showcase and copyable Lab block
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {useState} from 'react';
|
|
17
|
+
import * as stylex from '@stylexjs/stylex';
|
|
18
|
+
import {CommentPin} from '@astryxdesign/lab';
|
|
19
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
20
|
+
import {VStack} from '@astryxdesign/core/Stack';
|
|
21
|
+
import {
|
|
22
|
+
colorVars,
|
|
23
|
+
radiusVars,
|
|
24
|
+
shadowVars,
|
|
25
|
+
spacingVars,
|
|
26
|
+
} from '@astryxdesign/core/theme/tokens.stylex';
|
|
27
|
+
|
|
28
|
+
const PEOPLE = {
|
|
29
|
+
ada: {
|
|
30
|
+
id: 'ada',
|
|
31
|
+
name: 'Ada Lovelace',
|
|
32
|
+
avatarSrc: 'https://i.pravatar.cc/80?img=47',
|
|
33
|
+
},
|
|
34
|
+
lin: {
|
|
35
|
+
id: 'lin',
|
|
36
|
+
name: 'Lin Chen',
|
|
37
|
+
avatarSrc: 'https://i.pravatar.cc/80?img=32',
|
|
38
|
+
},
|
|
39
|
+
mira: {
|
|
40
|
+
id: 'mira',
|
|
41
|
+
name: 'Mira Patel',
|
|
42
|
+
avatarSrc: 'https://i.pravatar.cc/80?img=5',
|
|
43
|
+
},
|
|
44
|
+
noah: {
|
|
45
|
+
id: 'noah',
|
|
46
|
+
name: 'Noah Williams',
|
|
47
|
+
avatarSrc: 'https://i.pravatar.cc/80?img=12',
|
|
48
|
+
},
|
|
49
|
+
sam: {
|
|
50
|
+
id: 'sam',
|
|
51
|
+
name: 'Sam Rivera',
|
|
52
|
+
avatarSrc: 'https://i.pravatar.cc/80?img=15',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const styles = stylex.create({
|
|
57
|
+
root: {
|
|
58
|
+
width: '100%',
|
|
59
|
+
maxWidth: 720,
|
|
60
|
+
},
|
|
61
|
+
canvas: {
|
|
62
|
+
display: 'grid',
|
|
63
|
+
gridTemplateColumns: 'minmax(0, 1fr) 72px',
|
|
64
|
+
minHeight: 360,
|
|
65
|
+
overflow: 'hidden',
|
|
66
|
+
borderWidth: 1,
|
|
67
|
+
borderStyle: 'solid',
|
|
68
|
+
borderColor: colorVars['--color-border'],
|
|
69
|
+
borderRadius: radiusVars['--radius-container'],
|
|
70
|
+
backgroundColor: colorVars['--color-background-surface'],
|
|
71
|
+
boxShadow: shadowVars['--shadow-med'],
|
|
72
|
+
},
|
|
73
|
+
media: {
|
|
74
|
+
position: 'relative',
|
|
75
|
+
minWidth: 0,
|
|
76
|
+
minHeight: 360,
|
|
77
|
+
},
|
|
78
|
+
image: {
|
|
79
|
+
display: 'block',
|
|
80
|
+
width: '100%',
|
|
81
|
+
height: 360,
|
|
82
|
+
objectFit: 'cover',
|
|
83
|
+
},
|
|
84
|
+
groupedMarker: {
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
insetInlineStart: '38%',
|
|
87
|
+
insetBlockStart: '30%',
|
|
88
|
+
transform: 'translateY(-100%)',
|
|
89
|
+
},
|
|
90
|
+
resolvedMarker: {
|
|
91
|
+
position: 'absolute',
|
|
92
|
+
insetInlineStart: '66%',
|
|
93
|
+
insetBlockStart: '69%',
|
|
94
|
+
transform: 'translateY(-100%)',
|
|
95
|
+
},
|
|
96
|
+
margin: {
|
|
97
|
+
position: 'relative',
|
|
98
|
+
minHeight: 360,
|
|
99
|
+
borderInlineStartWidth: 1,
|
|
100
|
+
borderInlineStartStyle: 'solid',
|
|
101
|
+
borderInlineStartColor: colorVars['--color-border'],
|
|
102
|
+
backgroundColor: colorVars['--color-background-muted'],
|
|
103
|
+
},
|
|
104
|
+
marginRule: {
|
|
105
|
+
position: 'absolute',
|
|
106
|
+
insetInlineStart: '50%',
|
|
107
|
+
insetBlock: spacingVars['--spacing-5'],
|
|
108
|
+
width: 1,
|
|
109
|
+
backgroundColor: colorVars['--color-border'],
|
|
110
|
+
},
|
|
111
|
+
marginMarker: {
|
|
112
|
+
position: 'absolute',
|
|
113
|
+
insetInlineStart: '50%',
|
|
114
|
+
insetBlockStart: '48%',
|
|
115
|
+
transform: 'translateY(-100%)',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
type PinId = 'grouped' | 'margin' | 'resolved';
|
|
120
|
+
|
|
121
|
+
export default function CommentPinShowcase() {
|
|
122
|
+
const [selected, setSelected] = useState<PinId | null>('grouped');
|
|
123
|
+
// aria-pressed toggle semantics: activating the selected pin clears it.
|
|
124
|
+
const toggle = (id: PinId) =>
|
|
125
|
+
setSelected(current => (current === id ? null : id));
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<VStack gap={4} xstyle={styles.root}>
|
|
129
|
+
<VStack gap={1}>
|
|
130
|
+
<Text as="h3" type="display-3" weight="bold">
|
|
131
|
+
Annotations keep identity at the anchor
|
|
132
|
+
</Text>
|
|
133
|
+
<Text type="supporting" color="secondary">
|
|
134
|
+
The host positions and groups markers; each marker owns only its
|
|
135
|
+
compact interactive surface.
|
|
136
|
+
</Text>
|
|
137
|
+
</VStack>
|
|
138
|
+
|
|
139
|
+
<div {...stylex.props(styles.canvas)}>
|
|
140
|
+
<div {...stylex.props(styles.media)}>
|
|
141
|
+
<img
|
|
142
|
+
src="https://images.unsplash.com/photo-1558655146-d09347e92766?w=1296&h=720&fit=crop&auto=format"
|
|
143
|
+
alt="Design materials arranged on a desk"
|
|
144
|
+
{...stylex.props(styles.image)}
|
|
145
|
+
/>
|
|
146
|
+
<CommentPin
|
|
147
|
+
label="Open 6 grouped annotations from Ada, Lin, and 3 others, 2 unread"
|
|
148
|
+
participants={[
|
|
149
|
+
PEOPLE.ada,
|
|
150
|
+
PEOPLE.lin,
|
|
151
|
+
PEOPLE.mira,
|
|
152
|
+
PEOPLE.noah,
|
|
153
|
+
PEOPLE.sam,
|
|
154
|
+
]}
|
|
155
|
+
threadCount={6}
|
|
156
|
+
unreadCount={2}
|
|
157
|
+
isSelected={selected === 'grouped'}
|
|
158
|
+
isDimmed={selected != null && selected !== 'grouped'}
|
|
159
|
+
onClick={() => toggle('grouped')}
|
|
160
|
+
xstyle={styles.groupedMarker}
|
|
161
|
+
/>
|
|
162
|
+
<CommentPin
|
|
163
|
+
label="Open resolved annotation from Mira"
|
|
164
|
+
participants={[PEOPLE.mira]}
|
|
165
|
+
isResolved
|
|
166
|
+
isSelected={selected === 'resolved'}
|
|
167
|
+
isDimmed={selected != null && selected !== 'resolved'}
|
|
168
|
+
onClick={() => toggle('resolved')}
|
|
169
|
+
xstyle={styles.resolvedMarker}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div aria-label="Annotation margin" {...stylex.props(styles.margin)}>
|
|
174
|
+
<span aria-hidden="true" {...stylex.props(styles.marginRule)} />
|
|
175
|
+
<CommentPin
|
|
176
|
+
label="Open unread margin annotation from Mira"
|
|
177
|
+
participants={[PEOPLE.mira]}
|
|
178
|
+
unreadCount={1}
|
|
179
|
+
isSelected={selected === 'margin'}
|
|
180
|
+
isDimmed={selected != null && selected !== 'margin'}
|
|
181
|
+
onClick={() => toggle('margin')}
|
|
182
|
+
xstyle={styles.marginMarker}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</VStack>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'In a popover',
|
|
7
|
+
displayName: 'In a popover',
|
|
8
|
+
description:
|
|
9
|
+
'Open one thread from a passage in a Popover with no padding. The thread owns the Popover’s dismissal through `onDismiss`, so turn the Popover’s light and Escape dismissal off: a press outside or Escape closes the thread, but the first one over an unsaved reply shakes it and keeps the draft.',
|
|
10
|
+
exampleFor: 'CommentThread',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: [
|
|
14
|
+
'CommentThread',
|
|
15
|
+
'Popover',
|
|
16
|
+
'Button',
|
|
17
|
+
'Card',
|
|
18
|
+
'Text',
|
|
19
|
+
'VStack',
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled popover state, a controlled comment thread, and its reply
|
|
7
|
+
* callback
|
|
8
|
+
* @output A passage whose comment button opens one thread in a Popover; the
|
|
9
|
+
* thread owns the Popover's dismissal and keeps an unsaved reply on the
|
|
10
|
+
* first press outside or Escape
|
|
11
|
+
* @position Copyable Lab CommentThread example
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {useState} from 'react';
|
|
15
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
16
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
17
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
18
|
+
import {Popover} from '@astryxdesign/core/Popover';
|
|
19
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
20
|
+
import {
|
|
21
|
+
CommentThread,
|
|
22
|
+
type CommentThreadData,
|
|
23
|
+
type CommentUser,
|
|
24
|
+
} from '@astryxdesign/lab';
|
|
25
|
+
|
|
26
|
+
const VIEWER: CommentUser = {id: 'avery', name: 'Avery Kim'};
|
|
27
|
+
|
|
28
|
+
export default function CommentThreadInPopover() {
|
|
29
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
30
|
+
const [thread, setThread] = useState<CommentThreadData>({
|
|
31
|
+
id: 'passage',
|
|
32
|
+
author: {id: 'maya', name: 'Maya Chen'},
|
|
33
|
+
body: 'Should this say three days? The dry run adds one.',
|
|
34
|
+
createdAt: '2026-09-30T19:00:00Z',
|
|
35
|
+
anchor: {quote: 'The final cohort completes rollout in two days.'},
|
|
36
|
+
});
|
|
37
|
+
const replyCount = thread.replies?.length ?? 0;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Card padding={4} width={420}>
|
|
41
|
+
<VStack gap={3} align="start">
|
|
42
|
+
<Text>
|
|
43
|
+
Every region moves to the new build in three cohorts. The final cohort
|
|
44
|
+
completes rollout in two days.
|
|
45
|
+
</Text>
|
|
46
|
+
<Popover
|
|
47
|
+
isOpen={isOpen}
|
|
48
|
+
onOpenChange={setIsOpen}
|
|
49
|
+
// The thread owns dismissal: it keeps an unsaved reply on the first
|
|
50
|
+
// press outside or Escape, which native light dismiss cannot.
|
|
51
|
+
hasLightDismiss={false}
|
|
52
|
+
hasEscapeDismiss={false}
|
|
53
|
+
label="Comment thread"
|
|
54
|
+
padding={0}
|
|
55
|
+
width={340}
|
|
56
|
+
content={
|
|
57
|
+
<CommentThread
|
|
58
|
+
comment={thread}
|
|
59
|
+
currentUser={VIEWER}
|
|
60
|
+
container="card"
|
|
61
|
+
onDismiss={() => setIsOpen(false)}
|
|
62
|
+
onSubmit={body =>
|
|
63
|
+
setThread(current => ({
|
|
64
|
+
...current,
|
|
65
|
+
replies: [
|
|
66
|
+
...(current.replies ?? []),
|
|
67
|
+
{
|
|
68
|
+
id: `reply-${Date.now()}`,
|
|
69
|
+
author: VIEWER,
|
|
70
|
+
body,
|
|
71
|
+
createdAt: new Date().toISOString(),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}))
|
|
75
|
+
}
|
|
76
|
+
/>
|
|
77
|
+
}>
|
|
78
|
+
<Button
|
|
79
|
+
label={
|
|
80
|
+
replyCount === 0 ? '1 comment' : `${replyCount + 1} comments`
|
|
81
|
+
}
|
|
82
|
+
variant="secondary"
|
|
83
|
+
size="sm"
|
|
84
|
+
/>
|
|
85
|
+
</Popover>
|
|
86
|
+
</VStack>
|
|
87
|
+
</Card>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Margin comment',
|
|
7
|
+
displayName: 'Margin comment',
|
|
8
|
+
description:
|
|
9
|
+
'One comment thread in a Card beside a document: it quotes its passage, opens on a press into its replies and reply composer, and resolves.',
|
|
10
|
+
exampleFor: 'CommentThread',
|
|
11
|
+
isReady: true,
|
|
12
|
+
isShowcase: true,
|
|
13
|
+
aspectRatio: 4 / 3,
|
|
14
|
+
componentsUsed: ['CommentThread', 'Card'],
|
|
15
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input A controlled inline comment thread and its reply callback
|
|
7
|
+
* @output A margin comment: one thread quoting its passage, inside a Card
|
|
8
|
+
* @position Lab CommentThread's docsite showcase and copyable CLI block
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
13
|
+
import {
|
|
14
|
+
CommentThread,
|
|
15
|
+
type CommentThreadData,
|
|
16
|
+
type CommentUser,
|
|
17
|
+
} from '@astryxdesign/lab';
|
|
18
|
+
|
|
19
|
+
const VIEWER: CommentUser = {id: 'avery', name: 'Avery Kim'};
|
|
20
|
+
|
|
21
|
+
export default function CommentThreadShowcase() {
|
|
22
|
+
const [thread, setThread] = useState<CommentThreadData>({
|
|
23
|
+
id: 'margin',
|
|
24
|
+
author: {id: 'maya', name: 'Maya Chen'},
|
|
25
|
+
body: 'Should this say three days? The dry run adds one.',
|
|
26
|
+
createdAt: '2026-09-30T19:00:00Z',
|
|
27
|
+
anchor: {quote: 'The final cohort completes rollout in two days.'},
|
|
28
|
+
replies: [
|
|
29
|
+
{
|
|
30
|
+
id: 'margin-reply',
|
|
31
|
+
author: VIEWER,
|
|
32
|
+
body: 'Good catch, updating it.',
|
|
33
|
+
createdAt: '2026-09-30T19:20:00Z',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Card padding={0} width={340}>
|
|
40
|
+
<CommentThread
|
|
41
|
+
comment={thread}
|
|
42
|
+
currentUser={VIEWER}
|
|
43
|
+
container="card"
|
|
44
|
+
onSubmit={body =>
|
|
45
|
+
setThread(current => ({
|
|
46
|
+
...current,
|
|
47
|
+
replies: [
|
|
48
|
+
...(current.replies ?? []),
|
|
49
|
+
{
|
|
50
|
+
id: `reply-${Date.now()}`,
|
|
51
|
+
author: VIEWER,
|
|
52
|
+
body,
|
|
53
|
+
createdAt: new Date().toISOString(),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
onResolveThread={() =>
|
|
59
|
+
setThread(current => ({...current, isResolved: true}))
|
|
60
|
+
}
|
|
61
|
+
onReopenThread={() =>
|
|
62
|
+
setThread(current => ({...current, isResolved: false}))
|
|
63
|
+
}
|
|
64
|
+
/>
|
|
65
|
+
</Card>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Inside a card',
|
|
7
|
+
displayName: 'Inside a card',
|
|
8
|
+
description:
|
|
9
|
+
'Set container="card" on a card parent: threads become divider-bounded Sections, so cards never nest, and the composer docks at the bottom of the card.',
|
|
10
|
+
exampleFor: 'Comments',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 3 / 4,
|
|
13
|
+
componentsUsed: ['Comments', 'Card'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled comments and a post callback
|
|
7
|
+
* @output A compact Comments panel filling a Card, its threads divider-bounded
|
|
8
|
+
* Sections so cards never nest
|
|
9
|
+
* @position Copyable Lab Comments example
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {useState} from 'react';
|
|
13
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
14
|
+
import {
|
|
15
|
+
Comments,
|
|
16
|
+
type CommentThreadData,
|
|
17
|
+
type CommentUser,
|
|
18
|
+
} from '@astryxdesign/lab';
|
|
19
|
+
|
|
20
|
+
const VIEWER: CommentUser = {id: 'avery', name: 'Avery Kim'};
|
|
21
|
+
const MAYA: CommentUser = {id: 'maya', name: 'Maya Chen'};
|
|
22
|
+
|
|
23
|
+
const INITIAL_COMMENTS: readonly CommentThreadData[] = [
|
|
24
|
+
{
|
|
25
|
+
id: 'kickoff',
|
|
26
|
+
author: MAYA,
|
|
27
|
+
body: 'Can you review the rollout plan before Friday?',
|
|
28
|
+
createdAt: '2026-09-30T19:00:00Z',
|
|
29
|
+
replies: [
|
|
30
|
+
{
|
|
31
|
+
id: 'kickoff-reply',
|
|
32
|
+
author: VIEWER,
|
|
33
|
+
body: 'On it. I will leave notes on the final cohort.',
|
|
34
|
+
createdAt: '2026-09-30T19:12:00Z',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'dashboard',
|
|
40
|
+
author: VIEWER,
|
|
41
|
+
body: 'I added the guardrail dashboard to the summary.',
|
|
42
|
+
createdAt: '2026-09-30T19:20:00Z',
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export default function CommentsInCard() {
|
|
47
|
+
const [comments, setComments] = useState(INITIAL_COMMENTS);
|
|
48
|
+
const post = (body: string, parentId?: string) => {
|
|
49
|
+
const comment = {
|
|
50
|
+
id: `comment-${Date.now()}`,
|
|
51
|
+
author: VIEWER,
|
|
52
|
+
body,
|
|
53
|
+
createdAt: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
setComments(current =>
|
|
56
|
+
parentId == null
|
|
57
|
+
? [...current, comment]
|
|
58
|
+
: current.map(root =>
|
|
59
|
+
root.id === parentId
|
|
60
|
+
? {...root, replies: [...(root.replies ?? []), comment]}
|
|
61
|
+
: root,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Card padding={0} width={380} height={460}>
|
|
68
|
+
<Comments
|
|
69
|
+
label="Rollout plan"
|
|
70
|
+
comments={comments}
|
|
71
|
+
currentUser={VIEWER}
|
|
72
|
+
container="card"
|
|
73
|
+
density="compact"
|
|
74
|
+
onSubmit={post}
|
|
75
|
+
/>
|
|
76
|
+
</Card>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */
|
|
4
|
+
export default {
|
|
5
|
+
type: 'block',
|
|
6
|
+
name: 'Inline comments',
|
|
7
|
+
displayName: 'Inline comments',
|
|
8
|
+
description:
|
|
9
|
+
'A realistic Markdown document review with highlighted passages synchronized to a Comments side panel. Selecting an unannotated passage starts a quoted thread; replies and resolution update host-owned data.',
|
|
10
|
+
exampleFor: 'Comments',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 3 / 2,
|
|
13
|
+
componentsUsed: ['Button', 'Card', 'Comments', 'HStack', 'Text', 'VStack'],
|
|
14
|
+
};
|