@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3

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.
@@ -4,27 +4,11 @@
4
4
  export const doc = {
5
5
  type: 'block',
6
6
  exampleFor: 'Toolbar',
7
- alsoExampleFor: ['OverflowList'],
8
7
  name: 'Toolbar — Table Filter',
9
8
  displayName: 'Toolbar — Table Filter',
10
9
  description:
11
- 'Filter bar above a table: a search box leads the row, each field beside it is a closed trigger that doubles as its own filter chip — the bare field name unset, the whole clause once set — and the clauses fold from the end into a count as the row narrows, followed by a live result count, a clear all, and a column picker. Use to search, filter, and narrow flat rows of records such as jobs, orders, tickets, or users.',
10
+ 'A compact toolbar with a search input, Status and Priority filter selectors, and an overflow menu. Use above a data table to let users search, filter, and access view options.',
12
11
  isReady: true,
13
- aspectRatio: 16 / 7,
14
- componentsUsed: [
15
- 'Toolbar',
16
- 'Selector',
17
- 'TextInput',
18
- 'OverflowList',
19
- 'Popover',
20
- 'Button',
21
- 'Icon',
22
- 'Link',
23
- 'Text',
24
- 'CheckboxList',
25
- 'Layout',
26
- 'Section',
27
- 'Table',
28
- 'EmptyState',
29
- ],
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Toolbar', 'Selector', 'TextInput', 'MoreMenu', 'Layout', 'Table'],
30
14
  };
@@ -2,402 +2,84 @@
2
2
 
3
3
  'use client';
4
4
 
5
- import {useMemo, useState} from 'react';
6
- import * as stylex from '@stylexjs/stylex';
7
- import {
8
- borderVars,
9
- colorVars,
10
- radiusVars,
11
- } from '@astryxdesign/core/theme/tokens.stylex';
5
+ import {useState} from 'react';
12
6
  import {Toolbar} from '@astryxdesign/core/Toolbar';
13
7
  import {Selector} from '@astryxdesign/core/Selector';
14
8
  import {TextInput} from '@astryxdesign/core/TextInput';
15
- import {OverflowList} from '@astryxdesign/core/OverflowList';
16
- import {Popover} from '@astryxdesign/core/Popover';
17
- import {Button} from '@astryxdesign/core/Button';
18
- import {Icon} from '@astryxdesign/core/Icon';
19
- import {Link} from '@astryxdesign/core/Link';
20
- import {Text, Heading} from '@astryxdesign/core/Text';
21
- import {CheckboxList, CheckboxListItem} from '@astryxdesign/core/CheckboxList';
22
- import {Stack, StackItem, HStack, VStack} from '@astryxdesign/core/Layout';
23
- import {Section} from '@astryxdesign/core/Section';
24
- import {Table, pixel, proportional} from '@astryxdesign/core/Table';
25
- import type {TableColumn} from '@astryxdesign/core/Table';
26
- import {EmptyState} from '@astryxdesign/core/EmptyState';
27
- import {Columns3Cog, Search} from 'lucide-react';
28
-
29
- interface Job extends Record<string, unknown> {
30
- id: string;
31
- job: string;
32
- customer: string;
33
- status: string;
34
- priority: string;
35
- }
36
-
37
- const JOBS: Job[] = [
38
- {
39
- id: 'SJ-2136',
40
- job: 'VAV zones not cooling',
41
- customer: 'Northgate Dental',
42
- status: 'In progress',
43
- priority: 'High',
44
- },
45
- {
46
- id: 'SJ-2135',
47
- job: 'Exhaust fan noise',
48
- customer: 'Riverside Club',
49
- status: 'On hold',
50
- priority: 'Normal',
51
- },
52
- {
53
- id: 'SJ-2134',
54
- job: 'Humidifier board fault',
55
- customer: 'Summit Medical',
56
- status: 'Overdue',
57
- priority: 'High',
58
- },
59
- {
60
- id: 'SJ-2133',
61
- job: 'Economizer stuck shut',
62
- customer: 'Northgate Dental',
63
- status: 'Scheduled',
64
- priority: 'Normal',
65
- },
66
- {
67
- id: 'SJ-2101',
68
- job: 'Contactor replacement',
69
- customer: 'Harborview Hotel',
70
- status: 'Completed',
71
- priority: 'Low',
72
- },
73
- ];
74
-
75
- // One entry per filterable field. The closed trigger reads as the bare field
76
- // name unset, and renderValue expands a set one to the whole clause, so each
77
- // Selector doubles as its own filter chip.
78
- const FIELDS = [
79
- {
80
- key: 'status',
81
- label: 'Status',
82
- options: ['Scheduled', 'In progress', 'On hold', 'Overdue', 'Completed'],
83
- },
84
- {key: 'priority', label: 'Priority', options: ['High', 'Normal', 'Low']},
85
- {
86
- key: 'customer',
87
- label: 'Customer',
88
- options: [
89
- 'Northgate Dental',
90
- 'Riverside Club',
91
- 'Summit Medical',
92
- 'Harborview Hotel',
93
- ],
94
- },
95
- ];
96
-
97
- const COLUMNS: TableColumn<Job>[] = [
98
- {key: 'job', header: 'Job', width: proportional(2)},
99
- {key: 'customer', header: 'Customer', width: proportional(2)},
100
- {key: 'status', header: 'Status', width: pixel(110)},
101
- {key: 'priority', header: 'Priority', width: pixel(90)},
102
- ];
103
-
104
- /**
105
- * Width held back inside the clause slot for the readout that sits in it.
106
- *
107
- * The readout lives in the slot the overflow list measures, so without this
108
- * the list would count its width as room for another clause and push it off
109
- * the end. The slot pays for it as padding — the one part of the box the
110
- * measurement already subtracts — and the readout takes it back with a
111
- * matching negative margin, so it draws over the reserve and still lands
112
- * against the last clause rather than at the row's far edge.
113
- */
114
- const READOUT_RESERVE = 116;
115
-
116
- const styles = stylex.create({
117
- toolbarRow: {
118
- // Grow so the row spans the toolbar: the clause slot inside it can only
119
- // measure the width it is actually given.
120
- flexGrow: 1,
121
- minWidth: 0,
122
- },
123
- // Containment, not just a min-width reset: the reset lets flex squeeze this
124
- // slot, but the toolbar's own slot still floors at the width this one
125
- // reports it needs — the clauses at full length. Containment makes that
126
- // report independent of the clauses, so the row settles inside the toolbar
127
- // and the list finally reads a width that narrows. The popover it opens is
128
- // portalled, so nothing the reader needs is inside the clip.
129
- clauseSlot: {
130
- display: 'flex',
131
- alignItems: 'center',
132
- minWidth: 0,
133
- overflow: 'hidden',
134
- containerType: 'inline-size',
135
- // The held-back width, as padding (see READOUT_RESERVE).
136
- paddingInlineEnd: READOUT_RESERVE,
137
- },
138
- // Fixed: the field is the row's anchor, so it keeps one width in every
139
- // state. It still gives before the readout does, but only once the clauses
140
- // have folded away and there is nothing else left to take.
141
- searchSlot: {
142
- flexShrink: 1,
143
- minWidth: 120,
144
- width: 120,
145
- },
146
- // The fill a control switches to once it carries a value, so a set clause is
147
- // legible as engaged at a glance rather than only by reading it. This is the
148
- // token ToggleButton paints for its own pressed state, so a set selector and
149
- // a pressed control land on the same value over the same backdrop instead of
150
- // two neighbouring shades of engaged.
151
- filterFill: {
152
- backgroundColor: colorVars['--color-overlay-pressed'],
153
- },
154
- // A ghost Button has no chrome at all, so the border is what puts the count
155
- // in the same family as the unset selectors it stands in for. Left unfilled
156
- // deliberately: the fill means "this control carries a value", and the count
157
- // carries none of its own — it stands in for clauses that may or may not be
158
- // set.
159
- overflowChip: {
160
- borderWidth: borderVars['--border-width'],
161
- borderStyle: 'solid',
162
- borderColor: colorVars['--color-border-emphasized'],
163
- borderRadius: radiusVars['--radius-element'],
164
- backgroundColor: colorVars['--color-background-surface'],
165
- },
166
- // Shrink-wrapped to the clauses actually on the row. As a flex item the
167
- // list's basis is its unfolded content, which is wider than the slot, so it
168
- // would otherwise settle at the slot's full width and strand the readout at
169
- // the far edge with a gap where the folded clauses used to be. The list
170
- // measures the slot, not itself, so sizing it to its visible children costs
171
- // the fold nothing.
172
- clauseList: {
173
- flexGrow: 0,
174
- flexShrink: 0,
175
- width: 'max-content',
176
- maxWidth: '100%',
177
- },
178
- // Takes back the width the slot held back for it, so the readout draws over
179
- // the reserve and lands against the last clause. The gap to that clause is a
180
- // margin rather than the row's `gap`, because a gap would fall on the
181
- // measured side of the reserve and cost the clauses those pixels.
182
- readout: {
183
- flexShrink: 0,
184
- whiteSpace: 'nowrap',
185
- marginInlineStart: 8,
186
- marginInlineEnd: -READOUT_RESERVE,
187
- },
188
- viewPanel: {
189
- width: 320,
190
- },
191
- });
9
+ import {MoreMenu} from '@astryxdesign/core/MoreMenu';
10
+ import {Stack} from '@astryxdesign/core/Layout';
11
+ import {Table} from '@astryxdesign/core/Table';
12
+ import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';
192
13
 
193
14
  export default function ToolbarTableFilter() {
194
15
  const [search, setSearch] = useState('');
195
- const [clauses, setClauses] = useState<Record<string, string | null>>({});
196
- const [columnKeys, setColumnKeys] = useState(COLUMNS.map(c => c.key));
197
-
198
- const hasFilters =
199
- search !== '' || Object.values(clauses).some(value => value != null);
200
-
201
- const clearAll = () => {
202
- setSearch('');
203
- setClauses({});
204
- };
205
-
206
- // Search and every clause narrow one pass, so the table answers the whole
207
- // bar rather than whichever control moved last.
208
- const results = useMemo(() => {
209
- const query = search.trim().toLowerCase();
210
- return JOBS.filter(
211
- row =>
212
- (query === '' ||
213
- `${row.job} ${row.customer}`.toLowerCase().includes(query)) &&
214
- FIELDS.every(
215
- field =>
216
- clauses[field.key] == null || row[field.key] === clauses[field.key],
217
- ),
218
- );
219
- }, [search, clauses]);
220
-
221
- // One flat array rather than inline JSX: OverflowList hands its renderer the
222
- // indices of the clauses it had to hide, so the row and the folded panel have
223
- // to read from the same list for those indices to mean anything.
224
- const clauseControls = FIELDS.map(field => (
225
- <Selector
226
- key={field.key}
227
- label={`${field.label} filter`}
228
- isLabelHidden
229
- placeholder={field.label}
230
- size="sm"
231
- hasClear
232
- options={field.options}
233
- value={clauses[field.key] ?? null}
234
- xstyle={clauses[field.key] != null ? styles.filterFill : undefined}
235
- onChange={value => setClauses(prev => ({...prev, [field.key]: value}))}
236
- renderValue={option =>
237
- `${field.label} is ${option.label ?? option.value}`
238
- }
239
- />
240
- ));
241
-
242
- const viewOptions = (
243
- <VStack gap={3} xstyle={styles.viewPanel}>
244
- <Heading level={3}>Columns</Heading>
245
- <CheckboxList
246
- label="Displayed columns"
247
- isLabelHidden
248
- density="compact"
249
- value={columnKeys}
250
- onChange={setColumnKeys}>
251
- {COLUMNS.map(column => (
252
- <CheckboxListItem
253
- key={column.key}
254
- value={column.key}
255
- label={String(column.header)}
256
- />
257
- ))}
258
- </CheckboxList>
259
- </VStack>
260
- );
16
+ const [status, setStatus] = useState<string | null>(null);
17
+ const [priority, setPriority] = useState<string | null>(null);
261
18
 
262
19
  return (
263
- // Section, not a bare stack: the toolbar row and the table's first column
264
- // have to start on the same line, and the container publishes the padding
265
- // both read — the toolbar pads to it, and the table bleeds edge to edge
266
- // and compensates its outer cells back to it. Transparent and borderless,
267
- // because whatever hosts this block already paints its own frame.
268
- <Section variant="transparent" padding={2} width="100%" height="100%">
20
+ <Stack direction="vertical" style={{width: '100%'}}>
269
21
  <Toolbar
270
- label="Job filters"
22
+ label="Table filters"
271
23
  size="sm"
272
24
  dividers={['bottom']}
273
25
  startContent={
274
- // One full-width row rather than the toolbar's own two slots: the
275
- // overflow list has to measure the width it actually has, and a slot
276
- // sized by its own contents would feed the list its own output —
277
- // fold a clause, the slot narrows, the list folds again.
278
- <HStack
279
- gap={2}
280
- vAlign="center"
281
- wrap="nowrap"
282
- xstyle={styles.toolbarRow}>
283
- {/* Search leads the row as the broadest filter, and sits outside
284
- the overflow list so it is never the clause that folds — a
285
- hidden search box reads as a missing feature, not a folded
286
- one. */}
287
- <StackItem xstyle={styles.searchSlot}>
288
- <TextInput
289
- label="Search jobs"
290
- isLabelHidden
291
- placeholder="Search"
292
- size="sm"
293
- width="100%"
294
- value={search}
295
- onChange={setSearch}
296
- startIcon={Search}
297
- hasClear
298
- />
299
- </StackItem>
300
- {/* The slot, not the list, carries the sizing: as the row's only
301
- grower it settles at whatever the search box and the trailing
302
- controls leave, and the list reads that. */}
303
- <StackItem size="fill" xstyle={styles.clauseSlot}>
304
- <OverflowList
305
- behavior="observeParent"
306
- gap={2}
307
- xstyle={styles.clauseList}
308
- // No floor on how many stay out: at this width a set clause
309
- // reads as a whole sentence ("Status is Overdue"), and pinning
310
- // one to the row would push the readout off the end. The count
311
- // is what stays, and it opens onto every clause.
312
- minVisibleItems={0}
313
- overflowRenderer={overflowItems => (
314
- <Popover
315
- placement="below"
316
- alignment="start"
317
- width={240}
318
- label={`${overflowItems.length} more filters`}
319
- content={
320
- <VStack gap={2}>
321
- {overflowItems.map(({child, index}) => (
322
- <Stack key={index} direction="vertical">
323
- {child}
324
- </Stack>
325
- ))}
326
- </VStack>
327
- }>
328
- <Button
329
- label={`+${overflowItems.length}`}
330
- tooltip={`${overflowItems.length} more filters`}
331
- variant="ghost"
332
- size="sm"
333
- xstyle={styles.overflowChip}
334
- />
335
- </Popover>
336
- )}>
337
- {clauseControls}
338
- </OverflowList>
339
-
340
- {/* Inside the slot rather than beside it: a sibling would sit
341
- after the slot, and the slot spans the row's leftover, so the
342
- readout would land at the trailing edge however few clauses
343
- were showing. In here it follows the clauses, and the reserve
344
- is what stops the list from spending the width it needs. */}
345
- <HStack
346
- gap={2}
347
- vAlign="center"
348
- wrap="nowrap"
349
- xstyle={styles.readout}>
350
- <Text type="supporting" color="secondary">
351
- {results.length} of {JOBS.length}
352
- </Text>
353
- {hasFilters && (
354
- <>
355
- <Text type="supporting" color="secondary">
356
- •
357
- </Text>
358
- {/* Href-less, so Link renders a button: this resets the
359
- filters, it does not navigate. */}
360
- <Link type="supporting" onClick={clearAll}>
361
- Clear all
362
- </Link>
363
- </>
364
- )}
365
- </HStack>
366
- </StackItem>
367
- {/* View options closes the row: it acts on the table, not on the
368
- filter set, so it stands apart from the clauses. */}
369
- <Popover
370
- placement="below"
371
- alignment="end"
372
- label="View options"
373
- content={viewOptions}>
374
- <Button
375
- label="View options"
376
- tooltip="View options"
377
- isIconOnly
378
- variant="ghost"
379
- size="sm"
380
- icon={<Icon icon={Columns3Cog} size="sm" color="secondary" />}
381
- />
382
- </Popover>
383
- </HStack>
26
+ <>
27
+ <TextInput
28
+ label="Search"
29
+ isLabelHidden
30
+ placeholder="Search..."
31
+ value={search}
32
+ onChange={setSearch}
33
+ startIcon={MagnifyingGlassIcon}
34
+ />
35
+ <Selector
36
+ label="Status"
37
+ isLabelHidden
38
+ placeholder="Status"
39
+ hasClear
40
+ value={status}
41
+ onChange={setStatus}
42
+ options={['Open', 'In progress', 'Done']}
43
+ />
44
+ <Selector
45
+ label="Priority"
46
+ isLabelHidden
47
+ placeholder="Priority"
48
+ hasClear
49
+ value={priority}
50
+ onChange={setPriority}
51
+ options={['High', 'Medium', 'Low']}
52
+ />
53
+ </>
54
+ }
55
+ endContent={
56
+ <MoreMenu
57
+ items={[
58
+ {label: 'Compact view'},
59
+ {label: 'Comfortable view'},
60
+ {label: 'Export CSV'},
61
+ ]}
62
+ />
384
63
  }
385
64
  />
386
- {results.length === 0 ? (
387
- <EmptyState
388
- isCompact
389
- title="No jobs match these filters"
390
- actions={<Button label="Clear all" size="sm" onClick={clearAll} />}
391
- />
392
- ) : (
393
- <Table
394
- idKey="id"
395
- density="compact"
396
- textOverflow="truncate"
397
- columns={COLUMNS.filter(column => columnKeys.includes(column.key))}
398
- data={results}
399
- />
400
- )}
401
- </Section>
65
+ <Table
66
+ idKey="id"
67
+ columns={[
68
+ {key: 'task', header: 'Task'},
69
+ {key: 'status', header: 'Status'},
70
+ {key: 'priority', header: 'Priority'},
71
+ ]}
72
+ data={[
73
+ {id: '1', task: 'Fix login bug', status: 'Open', priority: 'High'},
74
+ {
75
+ id: '2',
76
+ task: 'Update docs',
77
+ status: 'In progress',
78
+ priority: 'Medium',
79
+ },
80
+ {id: '3', task: 'Add unit tests', status: 'Open', priority: 'Low'},
81
+ ]}
82
+ />
83
+ </Stack>
402
84
  );
403
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.6.3-canary.ea2f048",
3
+ "version": "0.6.3",
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",
@@ -100,10 +100,10 @@
100
100
  "zod": "^4.4.3"
101
101
  },
102
102
  "peerDependencies": {
103
- "@astryxdesign/charts": "0.6.3-canary.ea2f048",
104
- "@astryxdesign/core": "0.6.3-canary.ea2f048",
105
- "@astryxdesign/lab": "0.6.3-canary.ea2f048",
106
- "@astryxdesign/theme-neutral": "0.6.3-canary.ea2f048",
103
+ "@astryxdesign/charts": "*",
104
+ "@astryxdesign/core": "*",
105
+ "@astryxdesign/lab": "*",
106
+ "@astryxdesign/theme-neutral": "*",
107
107
  "gpt-tokenizer": "^3.4.0"
108
108
  },
109
109
  "peerDependenciesMeta": {
@@ -121,10 +121,10 @@
121
121
  }
122
122
  },
123
123
  "devDependencies": {
124
- "@astryxdesign/charts": "0.6.3-canary.ea2f048",
125
- "@astryxdesign/core": "0.6.3-canary.ea2f048",
126
- "@astryxdesign/lab": "0.6.3-canary.ea2f048",
127
- "@astryxdesign/theme-neutral": "0.6.3-canary.ea2f048",
124
+ "@astryxdesign/charts": "*",
125
+ "@astryxdesign/core": "*",
126
+ "@astryxdesign/lab": "*",
127
+ "@astryxdesign/theme-neutral": "*",
128
128
  "@heroicons/react": "^2.2.0",
129
129
  "@stylexjs/stylex": "^0.19.0",
130
130
  "@types/babel__core": "^7.20.5",
@@ -1,14 +0,0 @@
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: 'Timer',
7
- name: 'Timer — Formats',
8
- displayName: 'Timer — Formats',
9
- description:
10
- 'Elapsed compact units and stopwatch clock notation shown from the same start time.',
11
- isReady: true,
12
- aspectRatio: 16 / 9,
13
- componentsUsed: ['Timer', 'Layout', 'Text'],
14
- };
@@ -1,34 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- import {useState} from 'react';
6
- import {Timer} from '@astryxdesign/core/Timer';
7
- import {Stack} from '@astryxdesign/core/Layout';
8
- import {Text} from '@astryxdesign/core/Text';
9
-
10
- export default function TimerFormats() {
11
- const [startedAt] = useState(() => Date.now() - 3_753_000);
12
-
13
- return (
14
- <Stack direction="vertical" gap={3}>
15
- <Stack direction="horizontal" gap={3} vAlign="center">
16
- <Text type="supporting" color="secondary">
17
- Elapsed
18
- </Text>
19
- <Timer startTime={startedAt} type="body" color="primary" />
20
- </Stack>
21
- <Stack direction="horizontal" gap={3} vAlign="center">
22
- <Text type="supporting" color="secondary">
23
- Clock
24
- </Text>
25
- <Timer
26
- format="clock"
27
- startTime={startedAt}
28
- type="body"
29
- color="primary"
30
- />
31
- </Stack>
32
- </Stack>
33
- );
34
- }
@@ -1,14 +0,0 @@
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: 'Timer',
7
- name: 'Timer — Inline',
8
- displayName: 'Timer — Inline',
9
- description:
10
- 'A Timer composed into status copy while inheriting the surrounding typography.',
11
- isReady: true,
12
- aspectRatio: 16 / 9,
13
- componentsUsed: ['Timer', 'Text'],
14
- };
@@ -1,14 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- import {Timer} from '@astryxdesign/core/Timer';
6
- import {Text} from '@astryxdesign/core/Text';
7
-
8
- export default function TimerInline() {
9
- return (
10
- <Text type="body" color="primary">
11
- Processing for <Timer type="inherit" color="inherit" />
12
- </Text>
13
- );
14
- }
@@ -1,13 +0,0 @@
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: 'Timer',
7
- name: 'Timer',
8
- displayName: 'Timer',
9
- isReady: true,
10
- aspectRatio: 16 / 9,
11
- isShowcase: true,
12
- componentsUsed: ['Timer', 'Layout', 'Text'],
13
- };
@@ -1,47 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- import {useState} from 'react';
6
- import {Timer} from '@astryxdesign/core/Timer';
7
- import {Stack} from '@astryxdesign/core/Layout';
8
- import {Text} from '@astryxdesign/core/Text';
9
-
10
- export default function TimerShowcase() {
11
- const [startedAt] = useState(() => Date.now() - 3_753_000);
12
-
13
- return (
14
- <Stack direction="vertical" gap={4}>
15
- <Text type="large" color="primary" weight="semibold">
16
- Processing
17
- </Text>
18
- <Stack direction="horizontal" gap={6} vAlign="center">
19
- <Stack direction="vertical" gap={1}>
20
- <Text type="supporting" color="secondary">
21
- Elapsed
22
- </Text>
23
- <Timer
24
- startTime={startedAt}
25
- type="body"
26
- size="xl"
27
- color="primary"
28
- weight="semibold"
29
- />
30
- </Stack>
31
- <Stack direction="vertical" gap={1}>
32
- <Text type="supporting" color="secondary">
33
- Clock
34
- </Text>
35
- <Timer
36
- format="clock"
37
- startTime={startedAt}
38
- type="body"
39
- size="xl"
40
- color="primary"
41
- weight="semibold"
42
- />
43
- </Stack>
44
- </Stack>
45
- </Stack>
46
- );
47
- }
@@ -1,14 +0,0 @@
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: 'Timer',
7
- name: 'Timer — Typography',
8
- displayName: 'Timer — Typography',
9
- description:
10
- 'Timer using its Timestamp-matched default typography and an emphasized override.',
11
- isReady: true,
12
- aspectRatio: 16 / 9,
13
- componentsUsed: ['Timer', 'Layout', 'Text'],
14
- };
@@ -1,31 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- import {useState} from 'react';
6
- import {Timer} from '@astryxdesign/core/Timer';
7
- import {Stack} from '@astryxdesign/core/Layout';
8
- import {Text} from '@astryxdesign/core/Text';
9
-
10
- export default function TimerTypography() {
11
- const [startedAt] = useState(() => Date.now() - 128_000);
12
-
13
- return (
14
- <Stack direction="vertical" gap={3}>
15
- <Text type="supporting" color="secondary">
16
- Default
17
- </Text>
18
- <Timer startTime={startedAt} />
19
- <Text type="supporting" color="secondary">
20
- Emphasized
21
- </Text>
22
- <Timer
23
- startTime={startedAt}
24
- type="body"
25
- size="xl"
26
- color="primary"
27
- weight="semibold"
28
- />
29
- </Stack>
30
- );
31
- }