@astryxdesign/lab 0.7.2-canary.f16dc9f → 0.7.2-canary.fbc03f7

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.
Files changed (156) hide show
  1. package/blocks/CommentComposerShowcase.doc.mjs +15 -0
  2. package/blocks/CommentComposerShowcase.tsx +45 -0
  3. package/blocks/CommentPinInMargin.doc.mjs +14 -0
  4. package/blocks/CommentPinInMargin.tsx +77 -0
  5. package/blocks/CommentPinShowcase.doc.mjs +15 -0
  6. package/blocks/CommentPinShowcase.tsx +188 -0
  7. package/blocks/CommentThreadInPopover.doc.mjs +21 -0
  8. package/blocks/CommentThreadInPopover.tsx +89 -0
  9. package/blocks/CommentThreadShowcase.doc.mjs +15 -0
  10. package/blocks/CommentThreadShowcase.tsx +67 -0
  11. package/blocks/CommentsInCard.doc.mjs +14 -0
  12. package/blocks/CommentsInCard.tsx +78 -0
  13. package/blocks/CommentsInline.doc.mjs +14 -0
  14. package/blocks/CommentsInline.tsx +297 -0
  15. package/blocks/CommentsShowcase.doc.mjs +15 -0
  16. package/blocks/CommentsShowcase.tsx +341 -0
  17. package/blocks/EmojiPickerInPopover.doc.mjs +14 -0
  18. package/blocks/EmojiPickerInPopover.tsx +48 -0
  19. package/blocks/EmojiPickerShowcase.doc.mjs +15 -0
  20. package/blocks/EmojiPickerShowcase.tsx +33 -0
  21. package/dist/Chat/ChatTypingIndicator.d.ts.map +1 -1
  22. package/dist/Chat/ChatTypingIndicator.js +1 -1
  23. package/dist/CommentPin/CommentPin.d.ts +86 -0
  24. package/dist/CommentPin/CommentPin.d.ts.map +1 -0
  25. package/dist/CommentPin/CommentPin.js +260 -0
  26. package/dist/CommentPin/index.d.ts +9 -0
  27. package/dist/CommentPin/index.d.ts.map +1 -0
  28. package/dist/CommentPin/index.js +11 -0
  29. package/dist/Comments/CommentActions.d.ts +55 -0
  30. package/dist/Comments/CommentActions.d.ts.map +1 -0
  31. package/dist/Comments/CommentActions.js +472 -0
  32. package/dist/Comments/CommentComposer.d.ts +108 -0
  33. package/dist/Comments/CommentComposer.d.ts.map +1 -0
  34. package/dist/Comments/CommentComposer.js +507 -0
  35. package/dist/Comments/CommentMarkdown.d.ts +15 -0
  36. package/dist/Comments/CommentMarkdown.d.ts.map +1 -0
  37. package/dist/Comments/CommentMarkdown.js +136 -0
  38. package/dist/Comments/CommentParts.d.ts +238 -0
  39. package/dist/Comments/CommentParts.d.ts.map +1 -0
  40. package/dist/Comments/CommentParts.js +823 -0
  41. package/dist/Comments/CommentRow.d.ts +20 -0
  42. package/dist/Comments/CommentRow.d.ts.map +1 -0
  43. package/dist/Comments/CommentRow.js +693 -0
  44. package/dist/Comments/CommentThread.d.ts +66 -0
  45. package/dist/Comments/CommentThread.d.ts.map +1 -0
  46. package/dist/Comments/CommentThread.js +169 -0
  47. package/dist/Comments/Comments.d.ts +105 -0
  48. package/dist/Comments/Comments.d.ts.map +1 -0
  49. package/dist/Comments/Comments.js +426 -0
  50. package/dist/Comments/CommentsContext.d.ts +117 -0
  51. package/dist/Comments/CommentsContext.d.ts.map +1 -0
  52. package/dist/Comments/CommentsContext.js +515 -0
  53. package/dist/Comments/i18n.d.ts +18 -0
  54. package/dist/Comments/i18n.d.ts.map +1 -0
  55. package/dist/Comments/i18n.js +34 -0
  56. package/dist/Comments/icons.d.ts +24 -0
  57. package/dist/Comments/icons.d.ts.map +1 -0
  58. package/dist/Comments/icons.js +93 -0
  59. package/dist/Comments/index.d.ts +16 -0
  60. package/dist/Comments/index.d.ts.map +1 -0
  61. package/dist/Comments/index.js +15 -0
  62. package/dist/Comments/types.d.ts +341 -0
  63. package/dist/Comments/types.d.ts.map +1 -0
  64. package/dist/Comments/types.js +100 -0
  65. package/dist/Comments/useCommentThreadDismissal.d.ts +88 -0
  66. package/dist/Comments/useCommentThreadDismissal.d.ts.map +1 -0
  67. package/dist/Comments/useCommentThreadDismissal.js +344 -0
  68. package/dist/Comments/utils.d.ts +44 -0
  69. package/dist/Comments/utils.d.ts.map +1 -0
  70. package/dist/Comments/utils.js +84 -0
  71. package/dist/EmojiPicker/EmojiPicker.d.ts +96 -0
  72. package/dist/EmojiPicker/EmojiPicker.d.ts.map +1 -0
  73. package/dist/EmojiPicker/EmojiPicker.js +451 -0
  74. package/dist/EmojiPicker/emojiData.d.ts +13 -0
  75. package/dist/EmojiPicker/emojiData.d.ts.map +1 -0
  76. package/dist/EmojiPicker/emojiData.js +36 -0
  77. package/dist/EmojiPicker/index.d.ts +9 -0
  78. package/dist/EmojiPicker/index.d.ts.map +1 -0
  79. package/dist/EmojiPicker/index.js +11 -0
  80. package/dist/index.d.ts +3 -1
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +9 -3
  83. package/dist/lab.css +105 -46
  84. package/package.json +8 -6
  85. package/src/Chat/ChatTypingIndicator.tsx +3 -0
  86. package/src/CommentPin/CommentPin.doc.mjs +328 -0
  87. package/src/CommentPin/CommentPin.spec.md +307 -0
  88. package/src/CommentPin/CommentPin.test.tsx +350 -0
  89. package/src/CommentPin/CommentPin.tsx +484 -0
  90. package/src/CommentPin/index.ts +13 -0
  91. package/src/Comments/CommentActions.tsx +542 -0
  92. package/src/Comments/CommentComposer.doc.mjs +177 -0
  93. package/src/Comments/CommentComposer.test.tsx +255 -0
  94. package/src/Comments/CommentComposer.tsx +680 -0
  95. package/src/Comments/CommentMarkdown.tsx +184 -0
  96. package/src/Comments/CommentParts.tsx +937 -0
  97. package/src/Comments/CommentRow.tsx +918 -0
  98. package/src/Comments/CommentThread.doc.mjs +580 -0
  99. package/src/Comments/CommentThread.test.tsx +634 -0
  100. package/src/Comments/CommentThread.tsx +226 -0
  101. package/src/Comments/Comments.doc.mjs +648 -0
  102. package/src/Comments/Comments.test.tsx +1335 -0
  103. package/src/Comments/Comments.tsx +643 -0
  104. package/src/Comments/CommentsContext.tsx +755 -0
  105. package/src/Comments/i18n.ts +40 -0
  106. package/src/Comments/icons.tsx +129 -0
  107. package/src/Comments/index.ts +39 -0
  108. package/src/Comments/types.ts +392 -0
  109. package/src/Comments/useCommentThreadDismissal.ts +426 -0
  110. package/src/Comments/utils.ts +114 -0
  111. package/src/EmojiPicker/EmojiPicker.doc.mjs +186 -0
  112. package/src/EmojiPicker/EmojiPicker.test.tsx +276 -0
  113. package/src/EmojiPicker/EmojiPicker.tsx +574 -0
  114. package/src/EmojiPicker/emojiData.test.ts +32 -0
  115. package/src/EmojiPicker/emojiData.ts +3138 -0
  116. package/src/EmojiPicker/index.ts +13 -0
  117. package/src/index.ts +46 -8
  118. package/blocks/DrawerFullWidthMobile.doc.mjs +0 -21
  119. package/blocks/DrawerFullWidthMobile.tsx +0 -42
  120. package/blocks/DrawerHeaderShowcase.doc.mjs +0 -15
  121. package/blocks/DrawerHeaderShowcase.tsx +0 -73
  122. package/blocks/DrawerHeaderSubtitleEndContent.doc.mjs +0 -22
  123. package/blocks/DrawerHeaderSubtitleEndContent.tsx +0 -49
  124. package/blocks/DrawerHeaderWithCloseButton.doc.mjs +0 -21
  125. package/blocks/DrawerHeaderWithCloseButton.tsx +0 -42
  126. package/blocks/DrawerMobileReveal.doc.mjs +0 -21
  127. package/blocks/DrawerMobileReveal.tsx +0 -43
  128. package/blocks/DrawerScrim.doc.mjs +0 -22
  129. package/blocks/DrawerScrim.tsx +0 -68
  130. package/blocks/DrawerShowcase.doc.mjs +0 -22
  131. package/blocks/DrawerShowcase.tsx +0 -41
  132. package/blocks/DrawerStart.doc.mjs +0 -21
  133. package/blocks/DrawerStart.tsx +0 -41
  134. package/blocks/DrawerWidths.doc.mjs +0 -22
  135. package/blocks/DrawerWidths.tsx +0 -54
  136. package/dist/Drawer/Drawer.d.ts +0 -181
  137. package/dist/Drawer/Drawer.d.ts.map +0 -1
  138. package/dist/Drawer/Drawer.js +0 -388
  139. package/dist/Drawer/DrawerHeader.d.ts +0 -82
  140. package/dist/Drawer/DrawerHeader.d.ts.map +0 -1
  141. package/dist/Drawer/DrawerHeader.js +0 -131
  142. package/dist/Drawer/index.d.ts +0 -11
  143. package/dist/Drawer/index.d.ts.map +0 -1
  144. package/dist/Drawer/index.js +0 -11
  145. package/dist/Drawer/useDrawerDialogPresence.d.ts +0 -42
  146. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +0 -1
  147. package/dist/Drawer/useDrawerDialogPresence.js +0 -235
  148. package/src/Drawer/Drawer.doc.mjs +0 -470
  149. package/src/Drawer/Drawer.spec.md +0 -343
  150. package/src/Drawer/Drawer.test.tsx +0 -1039
  151. package/src/Drawer/Drawer.tsx +0 -618
  152. package/src/Drawer/DrawerHeader.doc.mjs +0 -291
  153. package/src/Drawer/DrawerHeader.test.tsx +0 -89
  154. package/src/Drawer/DrawerHeader.tsx +0 -237
  155. package/src/Drawer/index.ts +0 -13
  156. 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
+ };