@astryxdesign/cli 0.7.12-canary.ccb4a6d → 0.7.12-canary.da3dbdc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/component/__fixtures__/resolutions/modules.txt +5 -5
- package/api/docs/_adapter.d.mts +22 -0
- package/api/docs/_adapter.mjs +81 -3
- package/api/docs/docs.doc.mjs +5 -5
- package/api/docs/docs.type.d.mts +21 -0
- package/api/docs/docs.type.mjs +11 -0
- package/api/docs/list/list.d.mts +5 -1
- package/api/docs/list/list.mjs +72 -6
- package/api/docs/node/node.mjs +2 -40
- package/api/index.d.mts +1 -0
- package/api/index.mjs +1 -0
- package/api/render/_adapter.mjs +9 -0
- package/api/render/render.doc.mjs +2 -2
- package/api/search/_adapter.d.mts +22 -1
- package/api/search/_adapter.mjs +37 -20
- package/api/search/exportSearchIndex.doc.d.mts +10 -0
- package/api/search/exportSearchIndex.doc.mjs +58 -0
- package/api/search/rankSearchIndex.doc.d.mts +10 -0
- package/api/search/rankSearchIndex.doc.mjs +79 -0
- package/api/search/search-index.d.mts +44 -0
- package/api/search/search-index.mjs +270 -0
- package/api/search/search.type.d.mts +28 -0
- package/api/search/search.type.mjs +17 -0
- package/api/swizzle/_moduleGraph.d.mts +7 -0
- package/api/swizzle/_moduleGraph.mjs +191 -83
- package/api/swizzle/copy/copy.mjs +100 -9
- package/api/swizzle/swizzle.doc.mjs +7 -3
- package/api/swizzle/swizzle.type.d.mts +5 -0
- package/api/swizzle/swizzle.type.mjs +1 -0
- package/api/template/template.mjs +1 -0
- package/assets/discover/official-packages.json +16 -0
- package/assets/docs/browser-support.doc.mjs +2 -2
- package/assets/docs/styling-overview.doc.mjs +6 -2
- package/assets/docs/tree/render-pages.doc.mjs +2 -2
- package/assets/docs/tree/tailwind.doc.mjs +41 -1
- package/assets/docs/tree/type-scale.doc.mjs +19 -2
- package/assets/templates/blocks/components/Calendar/useCalendarConstraintsHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Calendar/useCalendarConstraintsHookUsage.tsx +146 -0
- package/assets/templates/blocks/components/Calendar/useCalendarNavigationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Calendar/useCalendarNavigationHookUsage.tsx +173 -0
- package/assets/templates/blocks/components/Hooks/useInputStatusIconHookUsage.tsx +1 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.tsx +56 -0
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.doc.mjs +2 -2
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.tsx +66 -10
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.tsx +39 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.tsx +83 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.tsx +35 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.tsx +97 -0
- package/assets/templates/pages/table-filter/page.tsx +15 -23
- package/assets/templates/pages/table-inbox/page.tsx +3 -6
- package/assets/templates/themes/butter/butterTheme.ts +2 -2
- package/assets/templates/themes/stone/stoneTheme.ts +1 -1
- package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
- package/clients/cli/commands/docs.doc.mjs +5 -1
- package/clients/cli/commands/docs.mjs +30 -2
- package/clients/cli/commands/swizzle.doc.mjs +4 -0
- package/clients/cli/commands/swizzle.mjs +12 -0
- package/clients/cli/lib/resolve-theme.mjs +16 -7
- package/foundation/discovery/hook-discovery.mjs +31 -8
- package/foundation/doc-compiler/import.d.mts +5 -1
- package/foundation/doc-compiler/import.mjs +40 -6
- package/foundation/fs/import-url.d.mts +20 -0
- package/foundation/fs/import-url.mjs +22 -0
- package/foundation/fs/module-loader.d.mts +24 -3
- package/foundation/fs/module-loader.mjs +69 -47
- package/foundation/render/charts.d.mts +59 -0
- package/foundation/render/charts.mjs +216 -0
- package/foundation/render/embedded-imports.d.mts +7 -7
- package/foundation/render/embedded-imports.mjs +63 -54
- package/foundation/render/page-worker.mjs +339 -52
- package/foundation/render/render-page.mjs +26 -10
- package/foundation/render/ts-paths.d.mts +63 -0
- package/foundation/render/ts-paths.mjs +138 -0
- package/foundation/response/error-codes.doc.mjs +2 -2
- package/foundation/response/error-codes.mjs +3 -2
- package/foundation/text/levenshtein.d.mts +6 -1
- package/foundation/text/levenshtein.mjs +25 -9
- package/foundation/text/suggest-order.d.mts +8 -1
- package/foundation/text/suggest-order.mjs +72 -6
- package/package.json +7 -6
|
@@ -2,29 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
5
6
|
import {useStreamingText} from '@astryxdesign/core/hooks';
|
|
7
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
6
8
|
import {Card} from '@astryxdesign/core/Card';
|
|
7
|
-
import {VStack} from '@astryxdesign/core/Layout';
|
|
8
9
|
import {Text} from '@astryxdesign/core/Text';
|
|
10
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
11
|
+
|
|
12
|
+
const CHUNKS = [
|
|
13
|
+
'Astryx hooks keep behavior reusable ',
|
|
14
|
+
'while components keep visuals consistent. ',
|
|
15
|
+
'Streaming text arrives in bursts; ',
|
|
16
|
+
'the hook smooths it into a steady reveal.',
|
|
17
|
+
];
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
// A fake stream: chunks land in bursts, the way a model streams tokens.
|
|
20
|
+
// Starting again stops the stream in progress, and the last chunk ends it.
|
|
21
|
+
function useFakeStream(onChunk: (chunk: string) => void, onEnd: () => void) {
|
|
22
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
23
|
+
const start = useCallback(() => {
|
|
24
|
+
if (timer.current) {
|
|
25
|
+
clearTimeout(timer.current);
|
|
26
|
+
}
|
|
27
|
+
let index = 0;
|
|
28
|
+
const tick = () => {
|
|
29
|
+
onChunk(CHUNKS[index]);
|
|
30
|
+
index += 1;
|
|
31
|
+
if (index < CHUNKS.length) {
|
|
32
|
+
timer.current = setTimeout(tick, 350);
|
|
33
|
+
} else {
|
|
34
|
+
timer.current = null;
|
|
35
|
+
onEnd();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
timer.current = setTimeout(tick, 350);
|
|
39
|
+
}, [onChunk, onEnd]);
|
|
40
|
+
useEffect(
|
|
41
|
+
() => () => {
|
|
42
|
+
if (timer.current) {
|
|
43
|
+
clearTimeout(timer.current);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[],
|
|
47
|
+
);
|
|
48
|
+
return start;
|
|
49
|
+
}
|
|
12
50
|
|
|
13
51
|
export default function UseStreamingTextHookUsage() {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
52
|
+
const [target, setTarget] = useState('');
|
|
53
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
54
|
+
|
|
55
|
+
const appendChunk = useCallback((chunk: string) => {
|
|
56
|
+
setTarget(previous => previous + chunk);
|
|
57
|
+
}, []);
|
|
58
|
+
// When the last chunk has landed, end the stream so the hook snaps to the
|
|
59
|
+
// full text.
|
|
60
|
+
const endStream = useCallback(() => setIsStreaming(false), []);
|
|
61
|
+
const startStream = useFakeStream(appendChunk, endStream);
|
|
62
|
+
|
|
63
|
+
const regenerate = useCallback(() => {
|
|
64
|
+
setTarget('');
|
|
65
|
+
setIsStreaming(true);
|
|
66
|
+
startStream();
|
|
67
|
+
}, [startStream]);
|
|
68
|
+
|
|
69
|
+
const displayedText = useStreamingText(target, isStreaming, {speed: 'fast'});
|
|
17
70
|
|
|
18
71
|
return (
|
|
19
72
|
<Card width={420} padding={4}>
|
|
20
|
-
<VStack gap={
|
|
73
|
+
<VStack gap={3}>
|
|
21
74
|
<Text type="body" weight="bold">
|
|
22
75
|
Assistant response
|
|
23
76
|
</Text>
|
|
24
77
|
<Text type="body">{displayedText}</Text>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
78
|
+
<Button
|
|
79
|
+
label={isStreaming ? 'Streaming…' : 'Regenerate'}
|
|
80
|
+
variant="secondary"
|
|
81
|
+
size="sm"
|
|
82
|
+
onClick={regenerate}
|
|
83
|
+
/>
|
|
28
84
|
</VStack>
|
|
29
85
|
</Card>
|
|
30
86
|
);
|
|
@@ -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: 'useLinkify',
|
|
7
|
+
name: 'useLinkify — Links in a Comment Thread',
|
|
8
|
+
displayName: 'useLinkify — Links in a Comment Thread',
|
|
9
|
+
description:
|
|
10
|
+
'Plain-text comments where useLinkify turns the URL and the email address into Links, and a custom pattern turns issue references like #123 into tracker links — the patterns option in action. The comment without matches stays plain text.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['Link', 'Layout', 'Text'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useLinkify, type LinkifyPattern} from '@astryxdesign/core/Link';
|
|
6
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
7
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
8
|
+
|
|
9
|
+
const COMMENTS = [
|
|
10
|
+
'See https://example.com/spec or email support@example.com.',
|
|
11
|
+
'Fixed by #123, tracking the regression in #456.',
|
|
12
|
+
'No links here, just a plain note.',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Issue references like #123 link into the tracker; the capture group maps
|
|
16
|
+
// onto its URL scheme, showing the patterns option.
|
|
17
|
+
const ISSUE_PATTERN: LinkifyPattern = {
|
|
18
|
+
pattern: /#(\d+)/g,
|
|
19
|
+
href: match => `https://example.com/issues/${match[1]}`,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function Comment({children}: {children: string}) {
|
|
23
|
+
const nodes = useLinkify(children, {patterns: [ISSUE_PATTERN]});
|
|
24
|
+
return (
|
|
25
|
+
<Text type="body" color="secondary">
|
|
26
|
+
{nodes}
|
|
27
|
+
</Text>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function UseLinkifyHookUsage() {
|
|
32
|
+
return (
|
|
33
|
+
<VStack gap={3} style={{width: '100%', maxWidth: 360}}>
|
|
34
|
+
{COMMENTS.map(comment => (
|
|
35
|
+
<Comment key={comment}>{comment}</Comment>
|
|
36
|
+
))}
|
|
37
|
+
</VStack>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -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: 'usePowerSearchConfig',
|
|
7
|
+
name: 'usePowerSearchConfig — Filtered Directory Table',
|
|
8
|
+
displayName: 'usePowerSearchConfig — Filtered Directory Table',
|
|
9
|
+
description:
|
|
10
|
+
'A people directory with a PowerSearch bar over a Table: typed field definitions feed usePowerSearchConfig, returning the search config and an applyFilters helper that updates Table rows.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['PowerSearch', 'Table', 'Layout'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {VStack} from '@astryxdesign/core/Layout';
|
|
7
|
+
import {
|
|
8
|
+
PowerSearch,
|
|
9
|
+
usePowerSearchConfig,
|
|
10
|
+
} from '@astryxdesign/core/PowerSearch';
|
|
11
|
+
import type {PowerSearchFilter} from '@astryxdesign/core/PowerSearch';
|
|
12
|
+
import {Table} from '@astryxdesign/core/Table';
|
|
13
|
+
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
14
|
+
|
|
15
|
+
const statusValues = [
|
|
16
|
+
{value: 'active', label: 'Active'},
|
|
17
|
+
{value: 'on-leave', label: 'On Leave'},
|
|
18
|
+
{value: 'contractor', label: 'Contractor'},
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const fieldDefs = [
|
|
22
|
+
{key: 'name', type: 'string', label: 'Name'},
|
|
23
|
+
{key: 'status', type: 'enum', label: 'Status', enumValues: statusValues},
|
|
24
|
+
{key: 'startDate', type: 'date', label: 'Start date'},
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
interface Person extends Record<string, unknown> {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
status: string;
|
|
31
|
+
startDate: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const people: Person[] = [
|
|
35
|
+
{id: '1', name: 'Alice Chen', status: 'active', startDate: 1672531200},
|
|
36
|
+
{id: '2', name: 'Bob Dylan', status: 'on-leave', startDate: 1680307200},
|
|
37
|
+
{id: '3', name: 'Charlie Kim', status: 'active', startDate: 1688169600},
|
|
38
|
+
{id: '4', name: 'Dana Scully', status: 'contractor', startDate: 1696118400},
|
|
39
|
+
{id: '5', name: 'Evan Wright', status: 'active', startDate: 1704067200},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const columns: TableColumn<Person>[] = [
|
|
43
|
+
{key: 'name', header: 'Name'},
|
|
44
|
+
{
|
|
45
|
+
key: 'status',
|
|
46
|
+
header: 'Status',
|
|
47
|
+
renderCell: (person: Person) =>
|
|
48
|
+
statusValues.find(s => s.value === person.status)?.label ?? person.status,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'startDate',
|
|
52
|
+
header: 'Start Date',
|
|
53
|
+
renderCell: (person: Person) =>
|
|
54
|
+
new Date(person.startDate * 1000).toLocaleDateString('en-US', {
|
|
55
|
+
timeZone: 'UTC',
|
|
56
|
+
year: 'numeric',
|
|
57
|
+
month: 'short',
|
|
58
|
+
day: 'numeric',
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
export default function UsePowerSearchConfigHookUsage() {
|
|
64
|
+
const [filters, setFilters] = useState<PowerSearchFilter[]>([]);
|
|
65
|
+
const {config, applyFilters} = usePowerSearchConfig(
|
|
66
|
+
fieldDefs,
|
|
67
|
+
'PeopleDirectory',
|
|
68
|
+
);
|
|
69
|
+
const filteredPeople = applyFilters(filters, people);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<VStack gap={4}>
|
|
73
|
+
<PowerSearch
|
|
74
|
+
config={config}
|
|
75
|
+
filters={filters}
|
|
76
|
+
onChange={newFilters => setFilters([...newFilters])}
|
|
77
|
+
placeholder="Filter directory by name, status, start date..."
|
|
78
|
+
resultCount={filteredPeople.length}
|
|
79
|
+
/>
|
|
80
|
+
<Table data={filteredPeople} columns={columns} idKey="id" hasHover />
|
|
81
|
+
</VStack>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -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: 'useTableRowsVirtualization',
|
|
7
|
+
name: 'useTableRowsVirtualization - Fixed Height Rows',
|
|
8
|
+
displayName: 'useTableRowsVirtualization - Fixed Height Rows',
|
|
9
|
+
description:
|
|
10
|
+
'A Table with 1,000 product rows in a fixed-height viewport. Scroll vertically to mount the visible rows. The caller keeps all data and columns; this hook does not virtualize columns.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: ['Table'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Table,
|
|
7
|
+
useTableRowsVirtualization,
|
|
8
|
+
type TableColumn,
|
|
9
|
+
} from '@astryxdesign/core/Table';
|
|
10
|
+
|
|
11
|
+
interface Product extends Record<string, unknown> {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const products: Product[] = Array.from({length: 1000}, (_, index) => ({
|
|
17
|
+
id: `product-${index}`,
|
|
18
|
+
name: `Product ${index + 1}`,
|
|
19
|
+
}));
|
|
20
|
+
const columns: TableColumn<Product>[] = [{key: 'name', header: 'Product'}];
|
|
21
|
+
|
|
22
|
+
export default function RowsVirtualizationHookUsage() {
|
|
23
|
+
const virtualization = useTableRowsVirtualization({
|
|
24
|
+
rowHeight: 56,
|
|
25
|
+
viewportHeight: 320,
|
|
26
|
+
});
|
|
27
|
+
return (
|
|
28
|
+
<Table
|
|
29
|
+
data={products}
|
|
30
|
+
columns={columns}
|
|
31
|
+
idKey="id"
|
|
32
|
+
virtualization={virtualization}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -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: 'useTableColumnSettingsState',
|
|
7
|
+
name: 'useTableColumnSettingsState — Column Visibility Picker',
|
|
8
|
+
displayName: 'useTableColumnSettingsState — Column Visibility Picker',
|
|
9
|
+
description:
|
|
10
|
+
'A Table toolbar with a Columns menu of checkboxes: toggling Email updates active columns, Reset restores defaults, and the config is passed directly to useTableColumnSettings.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: ['Button', 'CheckboxInput', 'Popover', 'Table', 'Layout'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
7
|
+
import {CheckboxInput} from '@astryxdesign/core/CheckboxInput';
|
|
8
|
+
import {HStack, VStack} from '@astryxdesign/core/Layout';
|
|
9
|
+
import {Popover} from '@astryxdesign/core/Popover';
|
|
10
|
+
import {
|
|
11
|
+
Table,
|
|
12
|
+
useTableColumnSettings,
|
|
13
|
+
useTableColumnSettingsState,
|
|
14
|
+
} from '@astryxdesign/core/Table';
|
|
15
|
+
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
16
|
+
|
|
17
|
+
interface Member extends Record<string, unknown> {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
email: string;
|
|
21
|
+
role: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const members: Member[] = [
|
|
25
|
+
{id: '1', name: 'Alice', email: 'alice@example.com', role: 'Engineer'},
|
|
26
|
+
{id: '2', name: 'Bob', email: 'bob@example.com', role: 'Designer'},
|
|
27
|
+
{id: '3', name: 'Charlie', email: 'charlie@example.com', role: 'Manager'},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const allColumns: TableColumn<Member>[] = [
|
|
31
|
+
{key: 'name', header: 'Name'},
|
|
32
|
+
{key: 'email', header: 'Email'},
|
|
33
|
+
{key: 'role', header: 'Role'},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const columnOptions = [
|
|
37
|
+
{key: 'name' as const, label: 'Name', isAlwaysVisible: true},
|
|
38
|
+
{key: 'email' as const, label: 'Email'},
|
|
39
|
+
{key: 'role' as const, label: 'Role'},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const defaultKeys = ['name', 'email', 'role'];
|
|
43
|
+
|
|
44
|
+
export default function UseTableColumnSettingsStateHookUsage() {
|
|
45
|
+
const [activeKeys, setActiveKeys] =
|
|
46
|
+
useState<ReadonlyArray<string>>(defaultKeys);
|
|
47
|
+
|
|
48
|
+
const state = useTableColumnSettingsState({
|
|
49
|
+
columns: columnOptions,
|
|
50
|
+
activeColumnKeys: activeKeys,
|
|
51
|
+
onChangeActiveColumnKeys: setActiveKeys,
|
|
52
|
+
defaultColumnKeys: defaultKeys,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const plugin = useTableColumnSettings<Member>(state.columnSettingsConfig);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<VStack gap={3}>
|
|
59
|
+
<HStack hAlign="start">
|
|
60
|
+
<Popover
|
|
61
|
+
placement="below"
|
|
62
|
+
label="Column settings"
|
|
63
|
+
width={200}
|
|
64
|
+
content={
|
|
65
|
+
<VStack gap={2}>
|
|
66
|
+
{columnOptions.map(col => (
|
|
67
|
+
<CheckboxInput
|
|
68
|
+
key={col.key}
|
|
69
|
+
label={col.label}
|
|
70
|
+
value={state.isColumnActive(col.key)}
|
|
71
|
+
isDisabled={!state.isColumnToggleable(col.key)}
|
|
72
|
+
onChange={() => state.toggleColumn(col.key)}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
<Button
|
|
76
|
+
label="Reset"
|
|
77
|
+
size="sm"
|
|
78
|
+
variant="secondary"
|
|
79
|
+
onClick={state.resetToDefault}>
|
|
80
|
+
Reset
|
|
81
|
+
</Button>
|
|
82
|
+
</VStack>
|
|
83
|
+
}>
|
|
84
|
+
<Button label="Columns" size="sm">
|
|
85
|
+
Columns
|
|
86
|
+
</Button>
|
|
87
|
+
</Popover>
|
|
88
|
+
</HStack>
|
|
89
|
+
<Table
|
|
90
|
+
data={members}
|
|
91
|
+
columns={allColumns}
|
|
92
|
+
idKey="id"
|
|
93
|
+
plugins={{columnSettings: plugin}}
|
|
94
|
+
/>
|
|
95
|
+
</VStack>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -1129,18 +1129,6 @@ const INITIAL_SAVED_VIEWS: SavedView[] = [
|
|
|
1129
1129
|
const COMPACT_SURFACE_QUERY =
|
|
1130
1130
|
'(max-width: 768px), (max-width: 1024px) and (pointer: coarse) and (hover: none)';
|
|
1131
1131
|
|
|
1132
|
-
/**
|
|
1133
|
-
* Top padding for whatever opens a BottomSheet, on the spacing scale.
|
|
1134
|
-
*
|
|
1135
|
-
* The panel's grab pill floats over the content instead of reserving layout
|
|
1136
|
-
* space for itself: it is 4px centered in a 24px band, so it covers the top
|
|
1137
|
-
* 10-14px of the sheet. Content that starts at the usual 16px gutter clears it
|
|
1138
|
-
* by 2px, which does not read as a gap — it reads as the sheet being cut off.
|
|
1139
|
-
* Opening at the full 24px band puts real space under the pill, and every
|
|
1140
|
-
* sheet here uses this one value so they all start on the same line.
|
|
1141
|
-
*/
|
|
1142
|
-
const SHEET_TOP_INSET = 6;
|
|
1143
|
-
|
|
1144
1132
|
/**
|
|
1145
1133
|
* Width the filter row holds back, in px, for the readout that trails the
|
|
1146
1134
|
* clauses — the density toggle, the result count and Clear all.
|
|
@@ -1698,6 +1686,7 @@ const styles = stylex.create({
|
|
|
1698
1686
|
fontSize: typeScaleVars['--text-body-size'],
|
|
1699
1687
|
fontWeight: typeScaleVars['--text-body-weight'],
|
|
1700
1688
|
height: 'auto',
|
|
1689
|
+
minHeight: 0,
|
|
1701
1690
|
lineHeight: typeScaleVars['--text-body-leading'],
|
|
1702
1691
|
paddingBlock: 0,
|
|
1703
1692
|
paddingInline: 0,
|
|
@@ -2052,7 +2041,8 @@ function FilterOverflow({
|
|
|
2052
2041
|
<VStack
|
|
2053
2042
|
gap={2}
|
|
2054
2043
|
padding={isCompactSurface ? 4 : 0}
|
|
2055
|
-
|
|
2044
|
+
// In a sheet the sheet's band under its grab bar is the top inset.
|
|
2045
|
+
paddingBlockStart={0}>
|
|
2056
2046
|
{items.map(({child, index}) => (
|
|
2057
2047
|
<StackItem key={index} xstyle={styles.overflowItem}>
|
|
2058
2048
|
{child}
|
|
@@ -2145,7 +2135,8 @@ function AdaptiveSavedViewForm({
|
|
|
2145
2135
|
<Section
|
|
2146
2136
|
variant="transparent"
|
|
2147
2137
|
padding={4}
|
|
2148
|
-
|
|
2138
|
+
// The sheet's band under its grab bar is the top inset.
|
|
2139
|
+
paddingBlockStart={0}>
|
|
2149
2140
|
<VStack gap={4}>
|
|
2150
2141
|
<HStack gap={2}>
|
|
2151
2142
|
<StackItem size="fill">
|
|
@@ -4090,11 +4081,11 @@ export default function TableFilterTemplate() {
|
|
|
4090
4081
|
label="View options"
|
|
4091
4082
|
height="tall"
|
|
4092
4083
|
// The tab strip compensates against a container inset, so the sheet
|
|
4093
|
-
// publishes none
|
|
4084
|
+
// publishes none; its band under the grab bar is the only top inset,
|
|
4085
|
+
// so the strip's own box starts at the tab row and its scroll arrow
|
|
4086
|
+
// lands on the tabs rather than above them.
|
|
4094
4087
|
padding={0}>
|
|
4095
|
-
{
|
|
4096
|
-
row and its scroll arrow lands on the tabs rather than above them. */}
|
|
4097
|
-
<VStack gap={0} paddingBlockStart={SHEET_TOP_INSET}>
|
|
4088
|
+
<VStack gap={0}>
|
|
4098
4089
|
{/* The heading the popover puts above the pane is dropped here: it
|
|
4099
4090
|
restates the selected tab word for word, and the strip already
|
|
4100
4091
|
marks which one that is. On a phone that is a line of vertical
|
|
@@ -4148,14 +4139,15 @@ export default function TableFilterTemplate() {
|
|
|
4148
4139
|
const detailBody =
|
|
4149
4140
|
activeJob == null ? null : (
|
|
4150
4141
|
<VStack gap={0} xstyle={styles.detailPanel}>
|
|
4151
|
-
{/* The only part of the body that knows which host it is in: the
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
Layout drops the panel entirely
|
|
4142
|
+
{/* The only part of the body that knows which host it is in: in the
|
|
4143
|
+
sheet, the sheet's band under its grab bar is the top inset; the
|
|
4144
|
+
docked panel has no pill and keeps the 16px gutter of the panels
|
|
4145
|
+
beside it. The two never coexist — Layout drops the panel entirely
|
|
4146
|
+
on a compact surface. */}
|
|
4155
4147
|
<Section
|
|
4156
4148
|
variant="transparent"
|
|
4157
4149
|
padding={4}
|
|
4158
|
-
paddingBlockStart={isCompactSurface ?
|
|
4150
|
+
paddingBlockStart={isCompactSurface ? 0 : 4}>
|
|
4159
4151
|
<VStack gap={4}>
|
|
4160
4152
|
<HStack gap={2}>
|
|
4161
4153
|
<StackItem size="fill">
|
|
@@ -1785,12 +1785,9 @@ function ComposeWindow({
|
|
|
1785
1785
|
hasScrim={false}
|
|
1786
1786
|
// The Layout below owns the inset; the sheet adds none of its own.
|
|
1787
1787
|
padding={0}>
|
|
1788
|
-
{/* The
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
sheet buys the handle its own band of space before the content
|
|
1792
|
-
starts. */}
|
|
1793
|
-
<VStack paddingBlockStart={3}>
|
|
1788
|
+
{/* The sheet keeps a band under its grab handle at rest, so the Layout
|
|
1789
|
+
starts below the pill without a band of its own. */}
|
|
1790
|
+
<VStack>
|
|
1794
1791
|
<Layout
|
|
1795
1792
|
// auto, not fill: the sheet is sized by this Layout now rather than
|
|
1796
1793
|
// the other way round. `fill` would stretch it to a height that
|
|
@@ -260,7 +260,7 @@ export const butterTheme = defineTheme({
|
|
|
260
260
|
'--color-tint-hover': [neutral.light[0], neutral.dark[100]],
|
|
261
261
|
|
|
262
262
|
// Typography override
|
|
263
|
-
'--text-supporting-size': '
|
|
263
|
+
'--text-supporting-size': '0.75rem', // 12px at the default root size
|
|
264
264
|
|
|
265
265
|
// Element sizes — slightly taller defaults so the new input padding
|
|
266
266
|
// (--spacing-2 block) has room to breathe.
|
|
@@ -394,8 +394,8 @@ export const butterTheme = defineTheme({
|
|
|
394
394
|
// Radius intentionally not pinned, so buttons keep core's
|
|
395
395
|
// `var(--_button-radius, --radius-element)`: standalone buttons stay 8px,
|
|
396
396
|
// while the chat composer (which sets --_button-radius) rounds them fully.
|
|
397
|
+
// Inline padding only. Button sets its own block padding and height.
|
|
397
398
|
base: {
|
|
398
|
-
paddingBlock: 'var(--spacing-3)',
|
|
399
399
|
paddingInline: 'var(--spacing-4)',
|
|
400
400
|
},
|
|
401
401
|
'variant:secondary': SECONDARY_BUTTON,
|
|
@@ -240,7 +240,7 @@ export const stoneTheme = defineTheme({
|
|
|
240
240
|
'--color-tint-hover': [neutral.light[0], neutral.dark[100]],
|
|
241
241
|
|
|
242
242
|
// Typography override
|
|
243
|
-
'--text-supporting-size': '
|
|
243
|
+
'--text-supporting-size': '0.75rem', // 12px at the default root size
|
|
244
244
|
|
|
245
245
|
// Categorical
|
|
246
246
|
// Light mode pairs pastel stop 90 surfaces with stop 30 foregrounds.
|
|
@@ -236,7 +236,7 @@ export const y2kTheme = defineTheme({
|
|
|
236
236
|
'--color-tint-hover': [neutral.light[15], neutral.dark[95]],
|
|
237
237
|
|
|
238
238
|
// Typography override
|
|
239
|
-
'--text-supporting-size': '
|
|
239
|
+
'--text-supporting-size': '0.75rem', // 12px at the default root size
|
|
240
240
|
|
|
241
241
|
// Categorical colors
|
|
242
242
|
'--color-background-green': [green.light[80], green.dark[80]],
|
|
@@ -46,7 +46,7 @@ export const doc = {
|
|
|
46
46
|
flag: '--depth <levels>',
|
|
47
47
|
param: 'options.depth',
|
|
48
48
|
description:
|
|
49
|
-
'How many levels of the docs tree to read below a namespace: 0 for the namespace alone, 1 for its children (what a read without --depth shows), all for everything below it. With it, --detail sets how much of each doc below shows: brief (one line each, the default), compact (with its sections), or full',
|
|
49
|
+
'How many levels of the docs tree to read below a namespace: 0 for the namespace alone, 1 for its children (what a read without --depth shows), all for everything below it. On the topic list, where a namespace otherwise lists alone, it names the docs below each one by the route that opens them, one line each. With it on a namespace read, --detail sets how much of each doc below shows: brief (one line each, the default), compact (with its sections), or full; the topic list is one line per doc whatever the detail',
|
|
50
50
|
},
|
|
51
51
|
],
|
|
52
52
|
examples: [
|
|
@@ -56,6 +56,10 @@ export const doc = {
|
|
|
56
56
|
{label: 'One section', cli: 'astryx docs theme quick-start'},
|
|
57
57
|
{label: 'The CLI docs tree', cli: 'astryx docs cli'},
|
|
58
58
|
{label: 'Every CLI doc, one line each', cli: 'astryx docs cli --depth all'},
|
|
59
|
+
{
|
|
60
|
+
label: 'Every route in the docs tree, one line each',
|
|
61
|
+
cli: 'astryx docs --depth all',
|
|
62
|
+
},
|
|
59
63
|
{
|
|
60
64
|
label: 'The integration guides as one read',
|
|
61
65
|
cli: 'astryx docs authoring --depth all --detail full',
|
|
@@ -615,6 +615,32 @@ function summarize(result) {
|
|
|
615
615
|
|
|
616
616
|
// ─── Command ─────────────────────────────────────────────────────────────────
|
|
617
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Every namespace of the list and, where a `--depth` read carried them, the
|
|
620
|
+
* docs below it, in reading order and in one flat list: each row's `topic` is
|
|
621
|
+
* the route that opens it, so the depth of a row never changes how a reader
|
|
622
|
+
* uses it, and each names the package that owns it.
|
|
623
|
+
* @param {Array<import('../../../api/docs/docs.type.mjs').DocsListNamespace | import('../../../api/docs/docs.type.mjs').DocsListChild>} rows
|
|
624
|
+
* @returns {Array<{topic: string, description: string, package: string}>}
|
|
625
|
+
*/
|
|
626
|
+
function flattenNamespaces(rows) {
|
|
627
|
+
return rows.flatMap(row => [
|
|
628
|
+
{
|
|
629
|
+
topic: row.topic,
|
|
630
|
+
// An experimental doc is marked here as it is wherever else it is
|
|
631
|
+
// listed, so a reader cannot take a row of this list for a stable doc.
|
|
632
|
+
description:
|
|
633
|
+
'stability' in row &&
|
|
634
|
+
row.stability === 'experimental' &&
|
|
635
|
+
!/^experimental\b/iu.test(row.description)
|
|
636
|
+
? `[experimental] ${row.description}`
|
|
637
|
+
: row.description,
|
|
638
|
+
package: row.package,
|
|
639
|
+
},
|
|
640
|
+
...flattenNamespaces(row.children ?? []),
|
|
641
|
+
]);
|
|
642
|
+
}
|
|
643
|
+
|
|
618
644
|
/**
|
|
619
645
|
* @param {import('commander').Command} program
|
|
620
646
|
*/
|
|
@@ -703,8 +729,10 @@ export function registerDocs(program) {
|
|
|
703
729
|
// The text view mirrors the JSON list, with the docs tree's
|
|
704
730
|
// namespaces (`meta.namespaces`) first under their own heading: that
|
|
705
731
|
// is where the CLI's own docs start, and a namespace reads
|
|
706
|
-
// differently from a topic.
|
|
707
|
-
|
|
732
|
+
// differently from a topic. With --depth each branch is flattened
|
|
733
|
+
// into the same list, so every line is a route a reader can pass
|
|
734
|
+
// straight back to `docs`.
|
|
735
|
+
const namespaces = flattenNamespaces(result.meta?.namespaces ?? []);
|
|
708
736
|
const topics = result.data;
|
|
709
737
|
emit(
|
|
710
738
|
...(namespaces.length > 0
|
|
@@ -22,6 +22,10 @@ export const doc = {
|
|
|
22
22
|
"is copied beside it in the package's folder layout. " +
|
|
23
23
|
'Tests, stories and docs are not copied. It lists the npm packages the copy imports, ' +
|
|
24
24
|
'with the ranges the owner package declares, to install if your project lacks them. ' +
|
|
25
|
+
'An output folder swizzle owns (one the copy ' +
|
|
26
|
+
'creates, an empty one, or one it marked before) gets an astryx-swizzle.json marker naming ' +
|
|
27
|
+
"each package its copies came from, so @astryxdesign/build's Babel, PostCSS and Vite " +
|
|
28
|
+
'integrations, and withAstryx() under webpack, compile the copy below your theme. ' +
|
|
25
29
|
'With no name it lists the swizzlable components. ' +
|
|
26
30
|
'An integration component that replaces a Core component is what that Core name ' +
|
|
27
31
|
'copies; --package @astryxdesign/core copies the original. --list lists Core ' +
|
|
@@ -113,6 +113,18 @@ export function registerSwizzle(program) {
|
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
const {marker} = result.data;
|
|
117
|
+
if (marker) {
|
|
118
|
+
const folder = marker.path.replace(/\/?astryx-swizzle\.json$/, '') || '.';
|
|
119
|
+
out.push(
|
|
120
|
+
text(
|
|
121
|
+
marker.written
|
|
122
|
+
? `Marked ${folder}/ with astryx-swizzle.json: with @astryxdesign/build's Babel, PostCSS and Vite integrations, or withAstryx() under webpack, every module under it compiles below your theme, so theme overrides reach the copy. Keep only swizzled copies there. Turbopack does not read the marker yet.`
|
|
123
|
+
: `Did not mark ${folder}/ with astryx-swizzle.json: it already holds ${(marker.found ?? []).join(', ')} and no astryx-swizzle.json. If it holds only swizzled copies, add ${marker.path} to mark it, so theme overrides reach them; keep the product's own components out of a marked folder.`,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
116
128
|
// StyleX build requirement — swizzled StyleX source throws on import
|
|
117
129
|
// ("Unexpected 'stylex.create' call at runtime") unless the consumer's
|
|
118
130
|
// build runs a StyleX compiler, so the page does not render at all.
|