@astryxdesign/cli 0.1.1-canary.2b52cdc → 0.1.1-canary.36fb9fb
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/docs/working-with-ai.doc.mjs +6 -6
- package/package.json +7 -7
- package/src/codemods/__tests__/registry.test.mjs +1 -0
- package/src/codemods/registry.mjs +1 -0
- package/src/codemods/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +120 -0
- package/src/codemods/transforms/v0.1.2/index.mjs +19 -0
- package/src/codemods/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +136 -0
- package/src/commands/upgrade.mjs +1 -1
- package/src/lib/xle/browser.d.ts +11 -2
- package/templates/blocks/components/Card/ClickableCardWithNestedButton.doc.mjs +1 -1
- package/templates/blocks/components/Card/SelectableCardMulti.doc.mjs +1 -1
- package/templates/blocks/components/ChatDictationButton/ChatDictationButtonShowcase.tsx +3 -3
- package/templates/blocks/components/FileInput/FileInputShowcase.tsx +4 -2
- package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +14 -0
- package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.tsx +43 -0
- package/templates/blocks/components/MultiSelector/MultiSelectorShowcase.tsx +6 -2
- package/templates/blocks/components/NumberInput/NumberInputShowcase.tsx +6 -2
- package/templates/blocks/components/RadioList/RadioListShowcase.tsx +8 -1
- package/templates/blocks/components/SegmentedControl/SegmentedControlShowcase.tsx +3 -1
- package/templates/blocks/components/Selector/SelectorShowcase.tsx +4 -1
- package/templates/blocks/components/Tab/TabShowcase.tsx +3 -1
- package/templates/blocks/components/TabList/TabListShowcase.tsx +3 -1
- package/templates/blocks/components/TabMenu/TabMenuShowcase.tsx +3 -1
- package/templates/blocks/components/Table/ColumnResizeHookUsage.doc.mjs +14 -0
- package/templates/blocks/components/Table/ColumnResizeHookUsage.tsx +59 -0
- package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +14 -0
- package/templates/blocks/components/Table/StickyColumnsHookUsage.tsx +104 -0
- package/templates/blocks/components/Text/TextColors.tsx +20 -5
- package/templates/blocks/components/TextArea/TextAreaShowcase.tsx +4 -2
- package/templates/blocks/components/TextInput/TextInputShowcase.tsx +4 -2
- package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +25 -5
- package/templates/blocks/components/Timestamp/TimestampColors.tsx +2 -2
- package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +8 -6
- package/templates/blocks/components/Typeahead/TypeaheadShowcase.tsx +6 -2
- package/templates/pages/theme-showcase/page.tsx +8 -19
|
@@ -34,11 +34,11 @@ export const docs = {
|
|
|
34
34
|
type: 'code',
|
|
35
35
|
lang: 'text',
|
|
36
36
|
label: 'Paste this into your AI',
|
|
37
|
-
code: 'Install @astryxdesign/cli and run `npx astryx
|
|
37
|
+
code: 'Install @astryxdesign/cli and run `npx astryx init --features agents` to set up your Astryx context. Read the generated file.',
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
type: 'prose',
|
|
41
|
-
text: 'That\'s it. The
|
|
41
|
+
text: 'That\'s it. The `init --features agents` command generates everything your AI needs (component index, behavioral rules, CLI reference) pulled from your installed version. After a version bump, run it again to update in place.',
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
type: 'prose',
|
|
@@ -48,9 +48,9 @@ export const docs = {
|
|
|
48
48
|
type: 'code',
|
|
49
49
|
lang: 'bash',
|
|
50
50
|
label: 'Manual options',
|
|
51
|
-
code: `npx astryx
|
|
52
|
-
npx astryx
|
|
53
|
-
npx astryx
|
|
51
|
+
code: `npx astryx init --features agents --agent claude # CLAUDE.md
|
|
52
|
+
npx astryx init --features agents --agent cursor # .cursorrules
|
|
53
|
+
npx astryx init --features agents --agent codex # AGENTS.md (Copilot, Codex, etc.)`,
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
},
|
|
@@ -88,7 +88,7 @@ npx astryx agent-docs --agent codex # AGENTS.md (Copilot, Codex, etc.)`,
|
|
|
88
88
|
lang: 'bash',
|
|
89
89
|
label: 'Install as a Cursor user rule',
|
|
90
90
|
code: `mkdir -p ~/.cursor/rules
|
|
91
|
-
npx astryx
|
|
91
|
+
npx astryx init --features agents --agent-docs-path ~/.cursor/rules/xds.mdc`,
|
|
92
92
|
},
|
|
93
93
|
],
|
|
94
94
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.1-canary.
|
|
3
|
+
"version": "0.1.1-canary.36fb9fb",
|
|
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",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"zod": "^4.4.3"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@astryxdesign/core": "0.1.1-canary.
|
|
67
|
-
"@astryxdesign/lab": "0.1.1-canary.
|
|
68
|
-
"@astryxdesign/theme-neutral": "0.1.1-canary.
|
|
66
|
+
"@astryxdesign/core": "0.1.1-canary.36fb9fb",
|
|
67
|
+
"@astryxdesign/lab": "0.1.1-canary.36fb9fb",
|
|
68
|
+
"@astryxdesign/theme-neutral": "0.1.1-canary.36fb9fb",
|
|
69
69
|
"gpt-tokenizer": "^2.0.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@astryxdesign/core": "0.1.1-canary.
|
|
84
|
-
"@astryxdesign/lab": "0.1.1-canary.
|
|
85
|
-
"@astryxdesign/theme-neutral": "0.1.1-canary.
|
|
83
|
+
"@astryxdesign/core": "0.1.1-canary.36fb9fb",
|
|
84
|
+
"@astryxdesign/lab": "0.1.1-canary.36fb9fb",
|
|
85
|
+
"@astryxdesign/theme-neutral": "0.1.1-canary.36fb9fb",
|
|
86
86
|
"gpt-tokenizer": "^2.0.0"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
@@ -18,6 +18,7 @@ const registry = new Map([
|
|
|
18
18
|
['0.0.14', () => import('./transforms/v0.0.14/index.mjs')],
|
|
19
19
|
['0.0.15', () => import('./transforms/v0.0.15/index.mjs')],
|
|
20
20
|
['0.1.0', () => import('./transforms/v0.1.0/index.mjs')],
|
|
21
|
+
['0.1.2', () => import('./transforms/v0.1.2/index.mjs')],
|
|
21
22
|
]);
|
|
22
23
|
|
|
23
24
|
// Re-export from the shared utility so registry callers and other consumers
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
|
|
5
|
+
async function applyTransform(source) {
|
|
6
|
+
const {default: transform} =
|
|
7
|
+
await import('../rename-text-color-active-to-accent.mjs');
|
|
8
|
+
const jscodeshift = (await import('jscodeshift')).default;
|
|
9
|
+
const j = jscodeshift.withParser('tsx');
|
|
10
|
+
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
11
|
+
const file = {source, path: 'test.tsx'};
|
|
12
|
+
const result = transform(file, api);
|
|
13
|
+
return result ?? source;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('rename-text-color-active-to-accent', () => {
|
|
17
|
+
it('renames Text color="active" to color="accent"', async () => {
|
|
18
|
+
const input = `<Text color="active">Hi</Text>`;
|
|
19
|
+
const output = await applyTransform(input);
|
|
20
|
+
expect(output).toContain("'accent'");
|
|
21
|
+
expect(output).not.toContain('active');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renames Heading color="active" to color="accent"', async () => {
|
|
25
|
+
const input = `<Heading level={2} color="active">Title</Heading>`;
|
|
26
|
+
const output = await applyTransform(input);
|
|
27
|
+
expect(output).toContain("'accent'");
|
|
28
|
+
expect(output).not.toContain('active');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renames Link color="active" to color="accent"', async () => {
|
|
32
|
+
const input = `<Link href="/x" color="active">Go</Link>`;
|
|
33
|
+
const output = await applyTransform(input);
|
|
34
|
+
expect(output).toContain("'accent'");
|
|
35
|
+
expect(output).not.toContain('active');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renames Timestamp color="active" to color="accent"', async () => {
|
|
39
|
+
const input = `<Timestamp value={d} color="active" />`;
|
|
40
|
+
const output = await applyTransform(input);
|
|
41
|
+
expect(output).toContain("'accent'");
|
|
42
|
+
expect(output).not.toContain('active');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("renames expression-container color={'active'}", async () => {
|
|
46
|
+
const input = `<Text color={'active'}>Hi</Text>`;
|
|
47
|
+
const output = await applyTransform(input);
|
|
48
|
+
expect(output).toContain("'accent'");
|
|
49
|
+
expect(output).not.toContain("'active'");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renames active inside a ternary color expression', async () => {
|
|
53
|
+
const input = `<Text color={isOn ? 'active' : 'secondary'}>Hi</Text>`;
|
|
54
|
+
const output = await applyTransform(input);
|
|
55
|
+
expect(output).toContain("'accent'");
|
|
56
|
+
expect(output).toContain("'secondary'");
|
|
57
|
+
expect(output).not.toContain("'active'");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('does not change other color values', async () => {
|
|
61
|
+
const input = `<Text color="secondary">Hi</Text>`;
|
|
62
|
+
const output = await applyTransform(input);
|
|
63
|
+
expect(output).toBe(input);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('does not touch color="active" on non-target components', async () => {
|
|
67
|
+
const input = `<StatusDot color="active" />`;
|
|
68
|
+
const output = await applyTransform(input);
|
|
69
|
+
expect(output).toBe(input);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('does not touch unrelated "active" string literals', async () => {
|
|
73
|
+
const input = `const node = {id: 'active', label: 'Active Users'};`;
|
|
74
|
+
const output = await applyTransform(input);
|
|
75
|
+
expect(output).toBe(input);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not touch a status enum value named active', async () => {
|
|
79
|
+
const input = `const filters = {status: 'active'};`;
|
|
80
|
+
const output = await applyTransform(input);
|
|
81
|
+
expect(output).toBe(input);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('renames object color prop in files importing a target component', async () => {
|
|
85
|
+
const input = `import {Text} from '@xds/core/Text';
|
|
86
|
+
const cfg = {color: 'active'};`;
|
|
87
|
+
const output = await applyTransform(input);
|
|
88
|
+
expect(output).toContain("color: 'accent'");
|
|
89
|
+
expect(output).not.toContain("'active'");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("renames color: 'active' as const in files importing a target component", async () => {
|
|
93
|
+
const input = `import {Text} from '@xds/core/Text';
|
|
94
|
+
const COLORS = [{color: 'active' as const, description: 'Accent'}];`;
|
|
95
|
+
const output = await applyTransform(input);
|
|
96
|
+
expect(output).toContain("'accent' as const");
|
|
97
|
+
expect(output).not.toContain("'active'");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('does not rename object color prop when no target component is imported', async () => {
|
|
101
|
+
const input = `const cfg = {color: 'active'};`;
|
|
102
|
+
const output = await applyTransform(input);
|
|
103
|
+
expect(output).toBe(input);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('renames active in Storybook color argTypes options', async () => {
|
|
107
|
+
const input = `import {Text} from '@xds/core/Text';
|
|
108
|
+
const meta = {argTypes: {color: {control: 'select', options: ['primary', 'active', 'inherit']}}};`;
|
|
109
|
+
const output = await applyTransform(input);
|
|
110
|
+
expect(output).toContain("'accent'");
|
|
111
|
+
expect(output).not.toContain("'active'");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('returns undefined (no change) when nothing matches', async () => {
|
|
115
|
+
const input = `<Text color="primary">Hi</Text>`;
|
|
116
|
+
const output = await applyTransform(input);
|
|
117
|
+
// applyTransform falls back to source when transform returns undefined
|
|
118
|
+
expect(output).toBe(input);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file v0.1.2 transform manifest
|
|
5
|
+
*
|
|
6
|
+
* Lists all codemods for the v0.1.2 release in the order they should run.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import renameTextColorActiveToAccent, {
|
|
10
|
+
meta as renameTextColorActiveToAccentMeta,
|
|
11
|
+
} from './rename-text-color-active-to-accent.mjs';
|
|
12
|
+
|
|
13
|
+
export default [
|
|
14
|
+
{
|
|
15
|
+
name: 'rename-text-color-active-to-accent',
|
|
16
|
+
transform: renameTextColorActiveToAccent,
|
|
17
|
+
meta: renameTextColorActiveToAccentMeta,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Codemod: Rename Text/Heading/Link/Timestamp color "active" to "accent"
|
|
5
|
+
* @see https://github.com/facebook/astryx/issues/2863
|
|
6
|
+
*
|
|
7
|
+
* The `active` text-color value is renamed to `accent` and now maps to the
|
|
8
|
+
* dedicated `--color-text-accent` token (legible accent ink) instead of
|
|
9
|
+
* `--color-accent` (the interactive/base accent surface). This gives a
|
|
10
|
+
* first-class, type-safe way to render accent-colored text and resolves the
|
|
11
|
+
* gap reported in #2863.
|
|
12
|
+
*
|
|
13
|
+
* Transforms (scoped to Text, Heading, Link, and Timestamp):
|
|
14
|
+
* - <Text color="active" /> → <Text color="accent" />
|
|
15
|
+
* - <Heading color={'active'} /> → <Heading color={'accent'} />
|
|
16
|
+
* - <Text color={cond ? 'active' : x}> → <Text color={cond ? 'accent' : x}>
|
|
17
|
+
* - Storybook argTypes: color: { options: [..., 'active', ...] }
|
|
18
|
+
* → replaces 'active' with 'accent' (files importing a target component)
|
|
19
|
+
* - Object properties: { color: 'active' } / { color: 'active' as const }
|
|
20
|
+
* → { color: 'accent' } (files importing a target component)
|
|
21
|
+
*
|
|
22
|
+
* The transform deliberately does NOT touch bare `'active'` strings that are
|
|
23
|
+
* not a `color` value (e.g. status enums, ids, filter values), so it is safe
|
|
24
|
+
* to run across an entire codebase.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export const meta = {
|
|
28
|
+
title: 'Rename Text/Heading/Link/Timestamp color "active" to "accent"',
|
|
29
|
+
description:
|
|
30
|
+
'Renames the `color="active"` value to `color="accent"` on Text, Heading, ' +
|
|
31
|
+
'Link, and Timestamp. The `accent` value maps to `--color-text-accent` ' +
|
|
32
|
+
'(the dedicated accent text ink). Also updates Storybook color argTypes ' +
|
|
33
|
+
'options and object-literal `color` props in files that import a target component.',
|
|
34
|
+
pr: '#2863',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const OLD_VALUE = 'active';
|
|
38
|
+
const NEW_VALUE = 'accent';
|
|
39
|
+
|
|
40
|
+
/** Components whose `color` prop accepts a TextColor value. */
|
|
41
|
+
const TARGET_COMPONENTS = new Set(['Text', 'Heading', 'Link', 'Timestamp']);
|
|
42
|
+
|
|
43
|
+
export default function transformer(file, api) {
|
|
44
|
+
const j = api.jscodeshift;
|
|
45
|
+
const root = j(file.source);
|
|
46
|
+
let hasChanges = false;
|
|
47
|
+
|
|
48
|
+
/** Rewrite a string-literal node when it equals the old value. */
|
|
49
|
+
function renameStringLiteral(node) {
|
|
50
|
+
if (!node) return false;
|
|
51
|
+
if (
|
|
52
|
+
(node.type === 'StringLiteral' || node.type === 'Literal') &&
|
|
53
|
+
node.value === OLD_VALUE
|
|
54
|
+
) {
|
|
55
|
+
node.value = NEW_VALUE;
|
|
56
|
+
if (node.raw) node.raw = `'${NEW_VALUE}'`;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Recursively rewrite the old value inside ternary/logical expressions. */
|
|
63
|
+
function renameInExpression(node) {
|
|
64
|
+
if (!node) return false;
|
|
65
|
+
let changed = false;
|
|
66
|
+
if (node.type === 'StringLiteral' || node.type === 'Literal') {
|
|
67
|
+
changed = renameStringLiteral(node) || changed;
|
|
68
|
+
} else if (node.type === 'ConditionalExpression') {
|
|
69
|
+
changed = renameInExpression(node.consequent) || changed;
|
|
70
|
+
changed = renameInExpression(node.alternate) || changed;
|
|
71
|
+
} else if (node.type === 'LogicalExpression') {
|
|
72
|
+
changed = renameInExpression(node.left) || changed;
|
|
73
|
+
changed = renameInExpression(node.right) || changed;
|
|
74
|
+
} else if (node.type === 'TSAsExpression') {
|
|
75
|
+
// `'active' as const` → rewrite the inner expression
|
|
76
|
+
changed = renameInExpression(node.expression) || changed;
|
|
77
|
+
}
|
|
78
|
+
return changed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- 1. JSX attribute: color="active" / color={'active'} on target components ---
|
|
82
|
+
root.find(j.JSXOpeningElement).forEach(path => {
|
|
83
|
+
const name = path.node.name;
|
|
84
|
+
const componentName = name.type === 'JSXIdentifier' ? name.name : null;
|
|
85
|
+
if (!componentName || !TARGET_COMPONENTS.has(componentName)) return;
|
|
86
|
+
|
|
87
|
+
path.node.attributes.forEach(attr => {
|
|
88
|
+
if (attr.type !== 'JSXAttribute') return;
|
|
89
|
+
if (!attr.name || attr.name.name !== 'color') return;
|
|
90
|
+
|
|
91
|
+
const value = attr.value;
|
|
92
|
+
if (!value) return;
|
|
93
|
+
|
|
94
|
+
if (value.type === 'StringLiteral' || value.type === 'Literal') {
|
|
95
|
+
if (renameStringLiteral(value)) hasChanges = true;
|
|
96
|
+
} else if (value.type === 'JSXExpressionContainer') {
|
|
97
|
+
if (renameInExpression(value.expression)) hasChanges = true;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Object-property / argTypes transforms only run in files that use a target
|
|
103
|
+
// component — keeps unrelated `{ color: 'active' }` / `'active'` strings safe.
|
|
104
|
+
const importsTarget =
|
|
105
|
+
root
|
|
106
|
+
.find(j.ImportSpecifier)
|
|
107
|
+
.filter(p => TARGET_COMPONENTS.has(p.node.imported?.name))
|
|
108
|
+
.size() > 0;
|
|
109
|
+
|
|
110
|
+
if (importsTarget) {
|
|
111
|
+
const PropertyType = j.ObjectProperty ?? j.Property;
|
|
112
|
+
|
|
113
|
+
// --- 2. Object property: { color: 'active' } / { color: 'active' as const } ---
|
|
114
|
+
root.find(PropertyType, {key: {name: 'color'}}).forEach(path => {
|
|
115
|
+
if (renameInExpression(path.node.value)) hasChanges = true;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// --- 3. Storybook argTypes: color: { options: [..., 'active', ...] } ---
|
|
119
|
+
root.find(PropertyType, {key: {name: 'color'}}).forEach(path => {
|
|
120
|
+
const value = path.node.value;
|
|
121
|
+
if (!value || value.type !== 'ObjectExpression') return;
|
|
122
|
+
|
|
123
|
+
const optionsProp = value.properties.find(
|
|
124
|
+
p => p.key && (p.key.name === 'options' || p.key.value === 'options'),
|
|
125
|
+
);
|
|
126
|
+
if (optionsProp && optionsProp.value.type === 'ArrayExpression') {
|
|
127
|
+
optionsProp.value.elements.forEach(el => {
|
|
128
|
+
if (renameStringLiteral(el)) hasChanges = true;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!hasChanges) return undefined;
|
|
135
|
+
return root.toSource({quote: 'single'});
|
|
136
|
+
}
|
package/src/commands/upgrade.mjs
CHANGED
|
@@ -438,7 +438,7 @@ export function registerUpgrade(program) {
|
|
|
438
438
|
} catch {
|
|
439
439
|
if (!json) {
|
|
440
440
|
p.log.warn(
|
|
441
|
-
`Could not update agent docs. Run \`${getRunPrefix()} astryx
|
|
441
|
+
`Could not update agent docs. Run \`${getRunPrefix()} astryx init --features agents\` to update manually.`,
|
|
442
442
|
);
|
|
443
443
|
}
|
|
444
444
|
}
|
package/src/lib/xle/browser.d.ts
CHANGED
|
@@ -79,8 +79,17 @@ export function hydrateRegistry(json: SerializedRegistry): object;
|
|
|
79
79
|
export function serializeRegistry(registry: object): SerializedRegistry;
|
|
80
80
|
export function parse(source: string, opts?: {form?: string}): unknown;
|
|
81
81
|
export function detectForm(source: string): 'compact' | 'outline';
|
|
82
|
-
export function validate(
|
|
83
|
-
|
|
82
|
+
export function validate(
|
|
83
|
+
doc: unknown,
|
|
84
|
+
registry: object,
|
|
85
|
+
blocks?: XLEBlock[],
|
|
86
|
+
opts?: {loose?: boolean},
|
|
87
|
+
): {errors: XLEIssue[]; warnings: XLEIssue[]};
|
|
88
|
+
export function expand(
|
|
89
|
+
doc: unknown,
|
|
90
|
+
registry: object,
|
|
91
|
+
opts?: {componentName?: string},
|
|
92
|
+
): {code: string; componentsUsed: string[]; states: number; todos: string[]};
|
|
84
93
|
export function toCompact(doc: unknown): string;
|
|
85
94
|
export function toOutline(doc: unknown): string;
|
|
86
95
|
export const ALIAS_TABLE: Record<string, string>;
|
|
@@ -5,7 +5,7 @@ export const doc = {
|
|
|
5
5
|
type: 'block',
|
|
6
6
|
exampleFor: 'ClickableCard',
|
|
7
7
|
name: 'ClickableCardWithNestedButton',
|
|
8
|
-
displayName: 'Clickable Card
|
|
8
|
+
displayName: 'Clickable Card — Nested Button',
|
|
9
9
|
description:
|
|
10
10
|
'A product card that navigates on click but has an independent "Add to cart" button inside.',
|
|
11
11
|
isReady: true,
|
|
@@ -5,7 +5,7 @@ export const doc = {
|
|
|
5
5
|
type: 'block',
|
|
6
6
|
exampleFor: 'SelectableCard',
|
|
7
7
|
name: 'SelectableCardMulti',
|
|
8
|
-
displayName: 'Selectable Card Multi',
|
|
8
|
+
displayName: 'Selectable Card — Multi-select',
|
|
9
9
|
description:
|
|
10
10
|
'Multi-select tag picker using color variant selectable cards with color-matched selection borders.',
|
|
11
11
|
isReady: true,
|
|
@@ -19,7 +19,7 @@ export default function ChatDictationButtonShowcase() {
|
|
|
19
19
|
const dictation = useChatDictation({
|
|
20
20
|
inputRef,
|
|
21
21
|
hasSounds: true,
|
|
22
|
-
onResult:
|
|
22
|
+
onResult: text => {
|
|
23
23
|
console.log('Dictation result:', text);
|
|
24
24
|
},
|
|
25
25
|
});
|
|
@@ -31,7 +31,7 @@ export default function ChatDictationButtonShowcase() {
|
|
|
31
31
|
input.
|
|
32
32
|
</Text>
|
|
33
33
|
<ChatComposer
|
|
34
|
-
onSubmit={
|
|
34
|
+
onSubmit={v => console.log('Submit:', v)}
|
|
35
35
|
input={<ChatComposerInput handleRef={inputRef} />}
|
|
36
36
|
sendActions={<ChatDictationButton dictation={dictation} />}
|
|
37
37
|
/>
|
|
@@ -63,7 +63,7 @@ export default function ChatDictationButtonShowcase() {
|
|
|
63
63
|
</HStack>
|
|
64
64
|
)}
|
|
65
65
|
{!dictation.isSupported && (
|
|
66
|
-
<Text type="supporting" color="
|
|
66
|
+
<Text type="supporting" color="accent">
|
|
67
67
|
SpeechRecognition is not supported in this browser.
|
|
68
68
|
</Text>
|
|
69
69
|
)}
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {FileInput} from '@astryxdesign/core/FileInput';
|
|
6
7
|
|
|
7
8
|
export default function FileInputShowcase() {
|
|
9
|
+
const [value, setValue] = useState<File | File[] | null>(null);
|
|
8
10
|
return (
|
|
9
11
|
<div style={{width: 350}}>
|
|
10
12
|
<FileInput
|
|
11
13
|
label="Upload file"
|
|
12
|
-
value={
|
|
13
|
-
onChange={
|
|
14
|
+
value={value}
|
|
15
|
+
onChange={setValue}
|
|
14
16
|
placeholder="Drag files here or click to browse"
|
|
15
17
|
/>
|
|
16
18
|
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'LinkProvider',
|
|
7
|
+
name: 'Link Provider — Custom Link Component',
|
|
8
|
+
displayName: 'Link Provider — Custom Link Component',
|
|
9
|
+
description:
|
|
10
|
+
'Routes every Astryx link through a custom component that intercepts the click — the hook frameworks like Next.js use for client-side navigation. Click the link to see the custom handler fire instead of a full-page load.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['LinkProvider', 'Link'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import type {AnchorHTMLAttributes} from 'react';
|
|
6
|
+
import {Link, LinkProvider} from '@astryxdesign/core/Link';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A stand-in for a framework router link (e.g. Next.js `Link` or React
|
|
10
|
+
* Router `Link`). Instead of letting the browser do a full-page navigation,
|
|
11
|
+
* it intercepts the click and hands the destination to a custom handler —
|
|
12
|
+
* the same hook real routers use for client-side navigation. Here it just
|
|
13
|
+
* announces the target so the behavior swap is observable in the preview.
|
|
14
|
+
*/
|
|
15
|
+
function RouterLink({
|
|
16
|
+
href,
|
|
17
|
+
onClick,
|
|
18
|
+
children,
|
|
19
|
+
...props
|
|
20
|
+
}: AnchorHTMLAttributes<HTMLAnchorElement>) {
|
|
21
|
+
return (
|
|
22
|
+
<a
|
|
23
|
+
href={href}
|
|
24
|
+
onClick={e => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
onClick?.(e);
|
|
27
|
+
alert(`RouterLink intercepted navigation to: ${href}`);
|
|
28
|
+
}}
|
|
29
|
+
{...props}>
|
|
30
|
+
{children}
|
|
31
|
+
</a>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function LinkProviderCustomLink() {
|
|
36
|
+
return (
|
|
37
|
+
<LinkProvider component={RouterLink}>
|
|
38
|
+
<Link href="/dashboard" isStandalone>
|
|
39
|
+
Go to dashboard
|
|
40
|
+
</Link>
|
|
41
|
+
</LinkProvider>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
3
6
|
import {MultiSelector} from '@astryxdesign/core/MultiSelector';
|
|
4
7
|
|
|
5
8
|
export default function MultiSelectorShowcase() {
|
|
9
|
+
const [value, setValue] = useState<string[]>([]);
|
|
6
10
|
return (
|
|
7
11
|
<div style={{width: 300}}>
|
|
8
12
|
<MultiSelector
|
|
9
13
|
label="Columns"
|
|
10
14
|
options={['Name', 'Email', 'Role', 'Status', 'Created']}
|
|
11
|
-
value={
|
|
12
|
-
onChange={
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={setValue}
|
|
13
17
|
placeholder="Select columns..."
|
|
14
18
|
/>
|
|
15
19
|
</div>
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
3
6
|
import {NumberInput} from '@astryxdesign/core/NumberInput';
|
|
4
7
|
|
|
5
8
|
export default function NumberInputShowcase() {
|
|
9
|
+
const [value, setValue] = useState<number | null>(0);
|
|
6
10
|
return (
|
|
7
11
|
<div style={{width: 300}}>
|
|
8
12
|
<NumberInput
|
|
9
13
|
label="Quantity"
|
|
10
14
|
placeholder="Enter quantity"
|
|
11
|
-
value={
|
|
12
|
-
onChange={
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={setValue}
|
|
13
17
|
/>
|
|
14
18
|
</div>
|
|
15
19
|
);
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
3
6
|
import {RadioList, RadioListItem} from '@astryxdesign/core/RadioList';
|
|
4
7
|
|
|
5
8
|
export default function RadioListShowcase() {
|
|
9
|
+
const [value, setValue] = useState('');
|
|
6
10
|
return (
|
|
7
|
-
<RadioList
|
|
11
|
+
<RadioList
|
|
12
|
+
label="Notification preference"
|
|
13
|
+
value={value}
|
|
14
|
+
onChange={setValue}>
|
|
8
15
|
<RadioListItem label="Email" value="email" />
|
|
9
16
|
<RadioListItem label="SMS" value="sms" />
|
|
10
17
|
<RadioListItem label="Push notification" value="push" />
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {
|
|
6
7
|
SegmentedControl,
|
|
7
8
|
SegmentedControlItem,
|
|
8
9
|
} from '@astryxdesign/core/SegmentedControl';
|
|
9
10
|
|
|
10
11
|
export default function SegmentedControlShowcase() {
|
|
12
|
+
const [value, setValue] = useState('grid');
|
|
11
13
|
return (
|
|
12
|
-
<SegmentedControl value=
|
|
14
|
+
<SegmentedControl value={value} onChange={setValue} label="View mode">
|
|
13
15
|
<SegmentedControlItem value="grid" label="Grid" />
|
|
14
16
|
<SegmentedControlItem value="list" label="List" />
|
|
15
17
|
<SegmentedControlItem value="table" label="Table" />
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {Selector} from '@astryxdesign/core/Selector';
|
|
6
7
|
|
|
7
8
|
export default function SelectorShowcase() {
|
|
9
|
+
const [value, setValue] = useState<string | undefined>();
|
|
8
10
|
return (
|
|
9
11
|
<Selector
|
|
10
12
|
style={{width: 300}}
|
|
11
13
|
label="Fruit"
|
|
12
14
|
options={['Apple', 'Banana', 'Orange', 'Mango', 'Pineapple']}
|
|
13
15
|
placeholder="Select a fruit..."
|
|
14
|
-
|
|
16
|
+
value={value}
|
|
17
|
+
onChange={setValue}
|
|
15
18
|
/>
|
|
16
19
|
);
|
|
17
20
|
}
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {TabList, Tab} from '@astryxdesign/core/TabList';
|
|
6
7
|
import {Badge} from '@astryxdesign/core/Badge';
|
|
7
8
|
|
|
8
9
|
export default function TabShowcase() {
|
|
10
|
+
const [value, setValue] = useState('inbox');
|
|
9
11
|
return (
|
|
10
|
-
<TabList value=
|
|
12
|
+
<TabList value={value} onChange={setValue}>
|
|
11
13
|
<Tab
|
|
12
14
|
value="inbox"
|
|
13
15
|
label="Inbox"
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {TabList, Tab} from '@astryxdesign/core/TabList';
|
|
6
7
|
|
|
7
8
|
export default function TabListShowcase() {
|
|
9
|
+
const [value, setValue] = useState('home');
|
|
8
10
|
return (
|
|
9
|
-
<TabList value=
|
|
11
|
+
<TabList value={value} onChange={setValue}>
|
|
10
12
|
<Tab value="home" label="Home" />
|
|
11
13
|
<Tab value="projects" label="Projects" />
|
|
12
14
|
<Tab value="settings" label="Settings" />
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {TabList, Tab, TabMenu} from '@astryxdesign/core/TabList';
|
|
6
7
|
|
|
7
8
|
export default function TabMenuShowcase() {
|
|
9
|
+
const [value, setValue] = useState('settings');
|
|
8
10
|
return (
|
|
9
|
-
<TabList value=
|
|
11
|
+
<TabList value={value} onChange={setValue}>
|
|
10
12
|
<Tab value="overview" label="Overview" />
|
|
11
13
|
<Tab value="activity" label="Activity" />
|
|
12
14
|
<TabMenu
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'useTableColumnResize',
|
|
7
|
+
name: 'useTableColumnResize — Draggable Columns',
|
|
8
|
+
displayName: 'useTableColumnResize — Draggable Columns',
|
|
9
|
+
description:
|
|
10
|
+
'A Table using useTableColumnResize. Drag the right edge of any column header to resize; widths are committed on release. The last proportional column flexes to fill remaining space.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: ['Table'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {
|
|
7
|
+
Table,
|
|
8
|
+
useTableColumnResize,
|
|
9
|
+
proportional,
|
|
10
|
+
pixel,
|
|
11
|
+
} from '@astryxdesign/core/Table';
|
|
12
|
+
|
|
13
|
+
interface User extends Record<string, unknown> {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
email: string;
|
|
17
|
+
role: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const users: User[] = [
|
|
21
|
+
{
|
|
22
|
+
id: '1',
|
|
23
|
+
name: 'Alice Johnson',
|
|
24
|
+
email: 'alice@example.com',
|
|
25
|
+
role: 'Engineer',
|
|
26
|
+
},
|
|
27
|
+
{id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer'},
|
|
28
|
+
{id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'PM'},
|
|
29
|
+
{id: '4', name: 'Diana Prince', email: 'diana@example.com', role: 'Engineer'},
|
|
30
|
+
{id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Analyst'},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const columns = [
|
|
34
|
+
{key: 'name', header: 'Name'},
|
|
35
|
+
{key: 'email', header: 'Email', width: proportional(2)},
|
|
36
|
+
{key: 'role', header: 'Role', width: pixel(120)},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export default function ColumnResizeHookUsage() {
|
|
40
|
+
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
|
41
|
+
|
|
42
|
+
const columnResize = useTableColumnResize({
|
|
43
|
+
columnWidths,
|
|
44
|
+
columns,
|
|
45
|
+
onColumnResizeEnd: updates => {
|
|
46
|
+
setColumnWidths(prev => ({...prev, ...updates}));
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Table
|
|
52
|
+
data={users}
|
|
53
|
+
columns={columns}
|
|
54
|
+
idKey="id"
|
|
55
|
+
hasHover
|
|
56
|
+
plugins={{columnResize}}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'useTableStickyColumns',
|
|
7
|
+
name: 'useTableStickyColumns — Pinned Columns',
|
|
8
|
+
displayName: 'useTableStickyColumns — Pinned Columns',
|
|
9
|
+
description:
|
|
10
|
+
'A Table using useTableStickyColumns to pin the Name column to the start edge and Status to the end edge. Scroll horizontally — pinned columns stay in view with a soft shadow over the scrolling content.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 16 / 9,
|
|
13
|
+
componentsUsed: ['Table'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {Table, useTableStickyColumns, pixel} from '@astryxdesign/core/Table';
|
|
6
|
+
|
|
7
|
+
interface Employee extends Record<string, unknown> {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
email: string;
|
|
11
|
+
team: string;
|
|
12
|
+
role: string;
|
|
13
|
+
location: string;
|
|
14
|
+
startDate: string;
|
|
15
|
+
status: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const employees: Employee[] = [
|
|
19
|
+
{
|
|
20
|
+
id: '1',
|
|
21
|
+
name: 'Alice Johnson',
|
|
22
|
+
email: 'alice@example.com',
|
|
23
|
+
team: 'Design Systems',
|
|
24
|
+
role: 'Staff Engineer',
|
|
25
|
+
location: 'San Francisco',
|
|
26
|
+
startDate: '2019-03-12',
|
|
27
|
+
status: 'Active',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: '2',
|
|
31
|
+
name: 'Bob Smith',
|
|
32
|
+
email: 'bob@example.com',
|
|
33
|
+
team: 'Design Systems',
|
|
34
|
+
role: 'Senior Designer',
|
|
35
|
+
location: 'New York',
|
|
36
|
+
startDate: '2020-07-01',
|
|
37
|
+
status: 'Active',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: '3',
|
|
41
|
+
name: 'Charlie Brown',
|
|
42
|
+
email: 'charlie@example.com',
|
|
43
|
+
team: 'Platform',
|
|
44
|
+
role: 'Engineering Manager',
|
|
45
|
+
location: 'London',
|
|
46
|
+
startDate: '2017-11-20',
|
|
47
|
+
status: 'On leave',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: '4',
|
|
51
|
+
name: 'Diana Prince',
|
|
52
|
+
email: 'diana@example.com',
|
|
53
|
+
team: 'Platform',
|
|
54
|
+
role: 'Staff Engineer',
|
|
55
|
+
location: 'Remote',
|
|
56
|
+
startDate: '2021-01-15',
|
|
57
|
+
status: 'Active',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: '5',
|
|
61
|
+
name: 'Eve Davis',
|
|
62
|
+
email: 'eve@example.com',
|
|
63
|
+
team: 'Growth',
|
|
64
|
+
role: 'Product Engineer',
|
|
65
|
+
location: 'Berlin',
|
|
66
|
+
startDate: '2022-05-30',
|
|
67
|
+
status: 'Active',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// Wide fixed-width columns so the table overflows and scrolls horizontally,
|
|
72
|
+
// keeping the pinned Name (start) and Status (end) columns in view.
|
|
73
|
+
const columns = [
|
|
74
|
+
{key: 'name', header: 'Name', width: pixel(180)},
|
|
75
|
+
{key: 'email', header: 'Email', width: pixel(220)},
|
|
76
|
+
{key: 'team', header: 'Team', width: pixel(180)},
|
|
77
|
+
{key: 'role', header: 'Role', width: pixel(200)},
|
|
78
|
+
{key: 'location', header: 'Location', width: pixel(160)},
|
|
79
|
+
{key: 'startDate', header: 'Start date', width: pixel(140)},
|
|
80
|
+
{key: 'status', header: 'Status', width: pixel(140)},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export default function StickyColumnsHookUsage() {
|
|
84
|
+
const stickyColumns = useTableStickyColumns<Employee>({
|
|
85
|
+
startKeys: ['name'],
|
|
86
|
+
endKeys: ['status'],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
// Constrain the width so the Table's own horizontal scroll container is
|
|
91
|
+
// narrower than its columns — that scroll container is what sticky columns
|
|
92
|
+
// pin against. Without a width cap the table renders full-width and never
|
|
93
|
+
// scrolls internally, so nothing sticks.
|
|
94
|
+
<div style={{width: 560, maxWidth: '100%'}}>
|
|
95
|
+
<Table
|
|
96
|
+
data={employees}
|
|
97
|
+
columns={columns}
|
|
98
|
+
idKey="id"
|
|
99
|
+
hasHover
|
|
100
|
+
plugins={{stickyColumns}}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -6,11 +6,26 @@ import {Text} from '@astryxdesign/core/Text';
|
|
|
6
6
|
import {Stack} from '@astryxdesign/core/Stack';
|
|
7
7
|
|
|
8
8
|
const COLORS = [
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
{
|
|
9
|
+
{
|
|
10
|
+
color: 'primary' as const,
|
|
11
|
+
description: 'Primary — Default for headings and body text',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
color: 'secondary' as const,
|
|
15
|
+
description: 'Secondary — Supporting details and metadata',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
color: 'accent' as const,
|
|
19
|
+
description: 'Accent — Links, emphasis, and accent-colored text',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
color: 'disabled' as const,
|
|
23
|
+
description: 'Disabled — Unavailable or inactive content',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
color: 'placeholder' as const,
|
|
27
|
+
description: 'Placeholder — Empty field hints',
|
|
28
|
+
},
|
|
14
29
|
];
|
|
15
30
|
|
|
16
31
|
export default function TextColors() {
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {TextArea} from '@astryxdesign/core/TextArea';
|
|
6
7
|
|
|
7
8
|
export default function TextAreaShowcase() {
|
|
9
|
+
const [value, setValue] = useState('');
|
|
8
10
|
return (
|
|
9
11
|
<div style={{width: 400}}>
|
|
10
12
|
<TextArea
|
|
11
13
|
label="Description"
|
|
12
|
-
value=
|
|
13
|
-
onChange={
|
|
14
|
+
value={value}
|
|
15
|
+
onChange={setValue}
|
|
14
16
|
placeholder="Enter a description..."
|
|
15
17
|
/>
|
|
16
18
|
</div>
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
6
7
|
|
|
7
8
|
export default function TextInputShowcase() {
|
|
9
|
+
const [value, setValue] = useState('');
|
|
8
10
|
return (
|
|
9
11
|
<div style={{width: 300}}>
|
|
10
12
|
<TextInput
|
|
11
13
|
label="Name"
|
|
12
|
-
value=
|
|
13
|
-
onChange={
|
|
14
|
+
value={value}
|
|
15
|
+
onChange={setValue}
|
|
14
16
|
placeholder="Enter your name"
|
|
15
17
|
/>
|
|
16
18
|
</div>
|
|
@@ -8,10 +8,30 @@ import {Stack} from '@astryxdesign/core/Layout';
|
|
|
8
8
|
import {Text} from '@astryxdesign/core/Text';
|
|
9
9
|
|
|
10
10
|
const ATTACHMENTS = [
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
{
|
|
12
|
+
id: 1,
|
|
13
|
+
src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-1.png',
|
|
14
|
+
alt: 'River through a valley',
|
|
15
|
+
label: 'valley.jpg',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 2,
|
|
19
|
+
src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-2.png',
|
|
20
|
+
alt: 'Foggy mountain peak',
|
|
21
|
+
label: 'mountain.jpg',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 3,
|
|
25
|
+
src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-3.png',
|
|
26
|
+
alt: 'Golden retriever puppy',
|
|
27
|
+
label: 'puppy.jpg',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 4,
|
|
31
|
+
src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-4.png',
|
|
32
|
+
alt: 'Bridge at sunset',
|
|
33
|
+
label: 'bridge.jpg',
|
|
34
|
+
},
|
|
15
35
|
];
|
|
16
36
|
|
|
17
37
|
export default function ThumbnailGallery() {
|
|
@@ -38,7 +58,7 @@ export default function ThumbnailGallery() {
|
|
|
38
58
|
))}
|
|
39
59
|
</Stack>
|
|
40
60
|
{selected != null && (
|
|
41
|
-
<Text type="supporting" color="
|
|
61
|
+
<Text type="supporting" color="accent">
|
|
42
62
|
Previewing: {selected}
|
|
43
63
|
</Text>
|
|
44
64
|
)}
|
|
@@ -31,9 +31,9 @@ export default function TimestampColors() {
|
|
|
31
31
|
</Stack>
|
|
32
32
|
<Stack direction="vertical" gap={1}>
|
|
33
33
|
<Text type="supporting" color="secondary">
|
|
34
|
-
|
|
34
|
+
Accent
|
|
35
35
|
</Text>
|
|
36
|
-
<Timestamp value={DATE} format="date_time" color="
|
|
36
|
+
<Timestamp value={DATE} format="date_time" color="accent" />
|
|
37
37
|
</Stack>
|
|
38
38
|
</Stack>
|
|
39
39
|
);
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
+
import {useState} from 'react';
|
|
5
6
|
import {Tokenizer} from '@astryxdesign/core/Tokenizer';
|
|
6
|
-
import type {SearchSource} from '@astryxdesign/core/Typeahead';
|
|
7
|
+
import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
|
|
7
8
|
|
|
8
9
|
const source: SearchSource = {
|
|
9
10
|
search: () => [],
|
|
@@ -11,16 +12,17 @@ const source: SearchSource = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export default function TokenizerShowcase() {
|
|
15
|
+
const [value, setValue] = useState<SearchableItem[]>([
|
|
16
|
+
{id: '1', label: 'Design'},
|
|
17
|
+
{id: '2', label: 'Engineering'},
|
|
18
|
+
]);
|
|
14
19
|
return (
|
|
15
20
|
<Tokenizer
|
|
16
21
|
label="Tags"
|
|
17
22
|
placeholder="Search..."
|
|
18
23
|
searchSource={source}
|
|
19
|
-
value={
|
|
20
|
-
|
|
21
|
-
{id: '2', label: 'Engineering'},
|
|
22
|
-
]}
|
|
23
|
-
onChange={() => {}}
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={setValue}
|
|
24
26
|
style={{width: 400}}
|
|
25
27
|
/>
|
|
26
28
|
);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
3
6
|
import {Typeahead} from '@astryxdesign/core/Typeahead';
|
|
4
7
|
import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
|
|
5
8
|
|
|
@@ -21,14 +24,15 @@ const fruitSource: SearchSource = {
|
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
export default function TypeaheadShowcase() {
|
|
27
|
+
const [value, setValue] = useState<SearchableItem | null>(null);
|
|
24
28
|
return (
|
|
25
29
|
<div style={{width: 320}}>
|
|
26
30
|
<Typeahead
|
|
27
31
|
label="Fruit"
|
|
28
32
|
placeholder="Search fruits..."
|
|
29
33
|
searchSource={fruitSource}
|
|
30
|
-
value={
|
|
31
|
-
onChange={
|
|
34
|
+
value={value}
|
|
35
|
+
onChange={setValue}
|
|
32
36
|
/>
|
|
33
37
|
</div>
|
|
34
38
|
);
|
|
@@ -143,12 +143,6 @@ const styles: Record<string, CSSProperties> = {
|
|
|
143
143
|
textAlign: 'center' as const,
|
|
144
144
|
maxWidth: 560,
|
|
145
145
|
},
|
|
146
|
-
// Hero headline painted with the theme's accent-text ink. Text has no
|
|
147
|
-
// 'accent' color value, so we set the dedicated --color-text-accent token
|
|
148
|
-
// directly (distinct from --color-accent / the 'active' color).
|
|
149
|
-
heroHeadline: {
|
|
150
|
-
color: 'var(--color-text-accent)',
|
|
151
|
-
},
|
|
152
146
|
centerText: {
|
|
153
147
|
textAlign: 'center',
|
|
154
148
|
},
|
|
@@ -438,7 +432,7 @@ function StorePreview({
|
|
|
438
432
|
<VStack gap={10} style={{...styles.content, ...styles.contentFluid}}>
|
|
439
433
|
<Center>
|
|
440
434
|
<VStack gap={4} hAlign="center" style={styles.heroText}>
|
|
441
|
-
<Text type="display-2"
|
|
435
|
+
<Text type="display-2" color="accent">
|
|
442
436
|
Little joys,
|
|
443
437
|
<br />
|
|
444
438
|
everywhere you go
|
|
@@ -462,10 +456,7 @@ function StorePreview({
|
|
|
462
456
|
/>
|
|
463
457
|
</AspectRatio>
|
|
464
458
|
<div style={inlineStyles.cardBody}>
|
|
465
|
-
<VStack
|
|
466
|
-
gap={2}
|
|
467
|
-
hAlign="center"
|
|
468
|
-
style={styles.cardStack}>
|
|
459
|
+
<VStack gap={2} hAlign="center" style={styles.cardStack}>
|
|
469
460
|
<HStack>
|
|
470
461
|
<Badge label={p.badge} variant={p.badgeVariant} />
|
|
471
462
|
</HStack>
|
|
@@ -475,7 +466,10 @@ function StorePreview({
|
|
|
475
466
|
<Text
|
|
476
467
|
type="supporting"
|
|
477
468
|
color="secondary"
|
|
478
|
-
style={{
|
|
469
|
+
style={{
|
|
470
|
+
...styles.cardDescription,
|
|
471
|
+
...styles.centerText,
|
|
472
|
+
}}>
|
|
479
473
|
{p.description}
|
|
480
474
|
</Text>
|
|
481
475
|
<HStack gap={2} vAlign="center" hAlign="center">
|
|
@@ -746,9 +740,7 @@ function ChatCard() {
|
|
|
746
740
|
|
|
747
741
|
<ChatMessage sender="assistant">
|
|
748
742
|
<VStack gap={3}>
|
|
749
|
-
<Text type="body">
|
|
750
|
-
Here’s everything I have on order #1043:
|
|
751
|
-
</Text>
|
|
743
|
+
<Text type="body">Here’s everything I have on order #1043:</Text>
|
|
752
744
|
<Card padding={3}>
|
|
753
745
|
<VStack gap={1}>
|
|
754
746
|
<Item
|
|
@@ -1107,10 +1099,7 @@ function InventoryCard({
|
|
|
1107
1099
|
).length;
|
|
1108
1100
|
return (
|
|
1109
1101
|
<Card padding={0} style={styles.inventoryCard}>
|
|
1110
|
-
<HStack
|
|
1111
|
-
hAlign="between"
|
|
1112
|
-
vAlign="center"
|
|
1113
|
-
style={styles.inventoryHeader}>
|
|
1102
|
+
<HStack hAlign="between" vAlign="center" style={styles.inventoryHeader}>
|
|
1114
1103
|
<Heading level={2}>Inventory</Heading>
|
|
1115
1104
|
<Button
|
|
1116
1105
|
label="Add item"
|