lucent-ui 0.4.2 → 0.6.0
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/dist/index.cjs +166 -26
- package/dist/index.d.ts +88 -2
- package/dist/index.js +2374 -1250
- package/dist-server/src/components/atoms/CodeBlock/CodeBlock.manifest.js +80 -0
- package/dist-server/src/components/atoms/Slider/Slider.manifest.js +98 -0
- package/dist-server/src/components/atoms/Table/Table.manifest.js +98 -0
- package/dist-server/src/components/molecules/DataTable/DataTable.manifest.js +26 -4
- package/dist-server/src/manifest/examples/button.manifest.js +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'code-block',
|
|
3
|
+
name: 'CodeBlock',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'A styled code display with optional tabs, a language label, copy-to-clipboard, and an AI prompt variant.',
|
|
8
|
+
designIntent: 'Use CodeBlock for static code snippets, install commands, API examples, and AI prompt sharing. ' +
|
|
9
|
+
'The tabs prop switches between related snippets (e.g. pnpm / npm / yarn). ' +
|
|
10
|
+
'The prompt variant renders a single-line truncated display suited to AI tool prompts — ' +
|
|
11
|
+
'the full text is always copied even when visually clipped. ' +
|
|
12
|
+
'Zero-dependency — no syntax highlighting library is bundled.',
|
|
13
|
+
props: [
|
|
14
|
+
{
|
|
15
|
+
name: 'code',
|
|
16
|
+
type: 'string',
|
|
17
|
+
required: false,
|
|
18
|
+
description: 'Code string — used in single (non-tabbed) mode.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'language',
|
|
22
|
+
type: 'string',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'Language label shown in the header (non-tabbed mode only). Purely cosmetic.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'tabs',
|
|
28
|
+
type: 'array',
|
|
29
|
+
required: false,
|
|
30
|
+
description: 'Tabbed mode. Each entry has { label, code, language?, icon? }. ' +
|
|
31
|
+
'Switching tabs resets the copied state.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'variant',
|
|
35
|
+
type: 'enum',
|
|
36
|
+
required: false,
|
|
37
|
+
default: 'code',
|
|
38
|
+
enumValues: ['code', 'prompt'],
|
|
39
|
+
description: '"code" renders a <pre><code> block with horizontal scroll. ' +
|
|
40
|
+
'"prompt" renders a single-line truncated span suited to AI prompts.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'helperText',
|
|
44
|
+
type: 'string',
|
|
45
|
+
required: false,
|
|
46
|
+
description: 'Descriptive text rendered between the tab bar and the code area.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'showCopyButton',
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
required: false,
|
|
52
|
+
default: 'true',
|
|
53
|
+
description: 'Renders a copy-to-clipboard button. ' +
|
|
54
|
+
'Shows a "Copied!" confirmation for 2 s on success.',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
usageExamples: [
|
|
58
|
+
{
|
|
59
|
+
title: 'Single snippet',
|
|
60
|
+
code: `<CodeBlock language="tsx" code={\`<Button variant="primary">Save</Button>\`} />`,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: 'Tabbed install commands',
|
|
64
|
+
code: `<CodeBlock tabs={[\n { label: 'pnpm', code: 'pnpm add lucent-ui', language: 'bash' },\n { label: 'npm', code: 'npm install lucent-ui', language: 'bash' },\n]} />`,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: 'AI prompt',
|
|
68
|
+
code: `<CodeBlock\n variant="prompt"\n helperText="Paste into Claude:"\n tabs={[\n { label: 'Claude', icon: '♦', code: '"Add a Button with variant=\\"primary\\"."' },\n { label: 'Cursor', icon: '↖', code: '@lucent-ui Add a primary Button.' },\n ]}\n/>`,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
compositionGraph: [],
|
|
72
|
+
accessibility: {
|
|
73
|
+
role: 'region',
|
|
74
|
+
ariaAttributes: ['aria-label (copy button)'],
|
|
75
|
+
keyboardInteractions: [
|
|
76
|
+
'Tab — focuses the copy button',
|
|
77
|
+
'Enter / Space — copies the code',
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'slider',
|
|
3
|
+
name: 'Slider',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'A range input styled with Lucent tokens for selecting a numeric value within a bounded range.',
|
|
8
|
+
designIntent: 'Use Slider for continuous or stepped numeric inputs where the relative position matters — ' +
|
|
9
|
+
'volume, brightness, border radius, spacing scale. Pair with showValue when the exact ' +
|
|
10
|
+
'number is meaningful to the user. For precise numeric entry, use Input with type="number" ' +
|
|
11
|
+
'instead. Disabled state uses muted colours without opacity hacks.',
|
|
12
|
+
props: [
|
|
13
|
+
{
|
|
14
|
+
name: 'min',
|
|
15
|
+
type: 'number',
|
|
16
|
+
required: false,
|
|
17
|
+
default: '0',
|
|
18
|
+
description: 'Minimum value.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'max',
|
|
22
|
+
type: 'number',
|
|
23
|
+
required: false,
|
|
24
|
+
default: '100',
|
|
25
|
+
description: 'Maximum value.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'step',
|
|
29
|
+
type: 'number',
|
|
30
|
+
required: false,
|
|
31
|
+
default: '1',
|
|
32
|
+
description: 'Increment step between values.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'value',
|
|
36
|
+
type: 'number',
|
|
37
|
+
required: false,
|
|
38
|
+
description: 'Controlled current value. Pair with onChange.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'defaultValue',
|
|
42
|
+
type: 'number',
|
|
43
|
+
required: false,
|
|
44
|
+
description: 'Initial value for uncontrolled usage. Defaults to the midpoint of min/max.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'onChange',
|
|
48
|
+
type: 'function',
|
|
49
|
+
required: false,
|
|
50
|
+
description: 'Called on every value change.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'label',
|
|
54
|
+
type: 'string',
|
|
55
|
+
required: false,
|
|
56
|
+
description: 'Visible label rendered above the track.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'showValue',
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
required: false,
|
|
62
|
+
default: 'false',
|
|
63
|
+
description: 'Displays the current numeric value to the right of the label.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'size',
|
|
67
|
+
type: 'enum',
|
|
68
|
+
required: false,
|
|
69
|
+
default: 'md',
|
|
70
|
+
description: 'Controls track thickness and thumb diameter.',
|
|
71
|
+
enumValues: ['sm', 'md', 'lg'],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'disabled',
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
required: false,
|
|
77
|
+
default: 'false',
|
|
78
|
+
description: 'Prevents interaction and dims the control.',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
usageExamples: [
|
|
82
|
+
{ title: 'Basic', code: `<Slider label="Volume" showValue />` },
|
|
83
|
+
{ title: 'Controlled', code: `<Slider label="Opacity" min={0} max={1} step={0.01} value={opacity} onChange={e => setOpacity(Number(e.target.value))} showValue />` },
|
|
84
|
+
{ title: 'Sizes', code: `<Slider size="sm" label="Small" />\n<Slider size="md" label="Medium" />\n<Slider size="lg" label="Large" />` },
|
|
85
|
+
{ title: 'Disabled', code: `<Slider label="Locked" disabled defaultValue={40} />` },
|
|
86
|
+
],
|
|
87
|
+
compositionGraph: [],
|
|
88
|
+
accessibility: {
|
|
89
|
+
role: 'slider',
|
|
90
|
+
ariaAttributes: ['aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-disabled'],
|
|
91
|
+
keyboardInteractions: [
|
|
92
|
+
'ArrowRight / ArrowUp — increase value by step',
|
|
93
|
+
'ArrowLeft / ArrowDown — decrease value by step',
|
|
94
|
+
'Home — jump to min',
|
|
95
|
+
'End — jump to max',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'table',
|
|
3
|
+
name: 'Table',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'A lightweight, token-styled HTML table primitive with compound sub-components. ' +
|
|
8
|
+
'Distinct from DataTable — no sorting, filtering, or pagination.',
|
|
9
|
+
designIntent: 'Use Table for static or lightly dynamic tabular data where full DataTable features ' +
|
|
10
|
+
'are not needed — props tables, changelog entries, comparison grids, reference docs. ' +
|
|
11
|
+
'The compound API (Table.Head, Table.Body, Table.Row, Table.Cell) maps directly to ' +
|
|
12
|
+
'semantic HTML so screen readers get the full table structure. ' +
|
|
13
|
+
'Horizontal overflow is handled automatically by a scroll wrapper.',
|
|
14
|
+
props: [
|
|
15
|
+
{
|
|
16
|
+
name: 'striped',
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
required: false,
|
|
19
|
+
default: 'false',
|
|
20
|
+
description: 'Applies alternating bgMuted backgrounds to even tbody rows.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'Table.Head',
|
|
24
|
+
type: 'component',
|
|
25
|
+
required: false,
|
|
26
|
+
description: 'Renders <thead> with bgMuted background. Accepts Table.Row children.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Table.Body',
|
|
30
|
+
type: 'component',
|
|
31
|
+
required: false,
|
|
32
|
+
description: 'Renders <tbody>. Accepts Table.Row children.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Table.Foot',
|
|
36
|
+
type: 'component',
|
|
37
|
+
required: false,
|
|
38
|
+
description: 'Renders <tfoot> with bgMuted background.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Table.Row',
|
|
42
|
+
type: 'component',
|
|
43
|
+
required: false,
|
|
44
|
+
description: 'Renders <tr> with a hover highlight. Accepts Table.Cell children.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Table.Cell',
|
|
48
|
+
type: 'component',
|
|
49
|
+
required: false,
|
|
50
|
+
description: 'Renders <td> by default or <th scope="col"> when as="th". ' +
|
|
51
|
+
'Header cells are semibold + secondary colour; data cells are regular + primary.',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
usageExamples: [
|
|
55
|
+
{
|
|
56
|
+
title: 'Basic',
|
|
57
|
+
code: `<Table>
|
|
58
|
+
<Table.Head>
|
|
59
|
+
<Table.Row>
|
|
60
|
+
<Table.Cell as="th">Name</Table.Cell>
|
|
61
|
+
<Table.Cell as="th">Role</Table.Cell>
|
|
62
|
+
</Table.Row>
|
|
63
|
+
</Table.Head>
|
|
64
|
+
<Table.Body>
|
|
65
|
+
<Table.Row>
|
|
66
|
+
<Table.Cell>Alice</Table.Cell>
|
|
67
|
+
<Table.Cell>Engineer</Table.Cell>
|
|
68
|
+
</Table.Row>
|
|
69
|
+
</Table.Body>
|
|
70
|
+
</Table>`,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: 'Striped',
|
|
74
|
+
code: `<Table striped>
|
|
75
|
+
<Table.Head>…</Table.Head>
|
|
76
|
+
<Table.Body>…</Table.Body>
|
|
77
|
+
</Table>`,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
title: 'Custom cell content',
|
|
81
|
+
code: `<Table.Cell>
|
|
82
|
+
<Badge variant="success">Active</Badge>
|
|
83
|
+
</Table.Cell>`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
compositionGraph: [
|
|
87
|
+
{ componentId: 'table-head', componentName: 'Table.Head', role: 'head', required: false },
|
|
88
|
+
{ componentId: 'table-body', componentName: 'Table.Body', role: 'body', required: false },
|
|
89
|
+
{ componentId: 'table-foot', componentName: 'Table.Foot', role: 'foot', required: false },
|
|
90
|
+
{ componentId: 'table-row', componentName: 'Table.Row', role: 'row', required: false },
|
|
91
|
+
{ componentId: 'table-cell', componentName: 'Table.Cell', role: 'cell', required: false },
|
|
92
|
+
],
|
|
93
|
+
accessibility: {
|
|
94
|
+
role: 'table',
|
|
95
|
+
ariaAttributes: ['scope="col" on th cells'],
|
|
96
|
+
keyboardInteractions: ['Standard browser table navigation'],
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -4,19 +4,23 @@ export const COMPONENT_MANIFEST = {
|
|
|
4
4
|
tier: 'molecule',
|
|
5
5
|
domain: 'neutral',
|
|
6
6
|
specVersion: '1.0',
|
|
7
|
-
description: 'A sortable, paginated data table with configurable columns, custom cell renderers, and keyboard-accessible pagination controls.',
|
|
7
|
+
description: 'A sortable, filterable, paginated data table with configurable columns, custom cell renderers, and keyboard-accessible pagination controls.',
|
|
8
8
|
designIntent: 'DataTable is generic over row type T so TypeScript consumers get full type safety on column keys and renderers. ' +
|
|
9
9
|
'Sorting is client-side and composable — each column opts in via sortable:true; clicking a sorted column cycles asc → desc → unsorted. ' +
|
|
10
|
+
'Filtering is per-column — each column opts in via filterable:true, which adds a dropdown button above the table. ' +
|
|
11
|
+
'Each dropdown is searchable and multi-select: a search input filters the option list, and each option is a checkbox that toggles membership in the active set. ' +
|
|
12
|
+
'Filtering uses set-membership: a row passes if its column value is included in the selected values array. ' +
|
|
13
|
+
'A "Clear selection" link inside each dropdown clears that column; a "Clear all" button in the bar appears when any filter is active. ' +
|
|
14
|
+
'Filter → sort → paginate is the fixed pipeline order; any filter change resets the page to 0. ' +
|
|
10
15
|
'Pagination is either controlled (page prop + onPageChange) or uncontrolled (internal state). ' +
|
|
11
16
|
'A pageSize of 0 disables pagination entirely, useful when the parent manages windowing. ' +
|
|
12
|
-
'Column filtering is intentionally excluded here (see DataTable Filter issue #52) to keep the API focused. ' +
|
|
13
17
|
'Row hover uses bg-subtle, not a border change, so the visual weight stays low for dense data views.',
|
|
14
18
|
props: [
|
|
15
19
|
{
|
|
16
20
|
name: 'columns',
|
|
17
21
|
type: 'array',
|
|
18
22
|
required: true,
|
|
19
|
-
description: 'Column definitions. Each column has a key, header, optional render function, optional sortable flag, optional width, and optional text align.',
|
|
23
|
+
description: 'Column definitions. Each column has a key, header, optional render function, optional sortable flag, optional filterable flag (renders a text filter input below the header), optional width, and optional text align.',
|
|
20
24
|
},
|
|
21
25
|
{
|
|
22
26
|
name: 'rows',
|
|
@@ -41,7 +45,13 @@ export const COMPONENT_MANIFEST = {
|
|
|
41
45
|
name: 'onPageChange',
|
|
42
46
|
type: 'function',
|
|
43
47
|
required: false,
|
|
44
|
-
description: 'Called with the new page index whenever the page changes (from pagination controls or after a sort reset).',
|
|
48
|
+
description: 'Called with the new page index whenever the page changes (from pagination controls or after a sort/filter reset).',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'onFilterChange',
|
|
52
|
+
type: 'function',
|
|
53
|
+
required: false,
|
|
54
|
+
description: 'Called with the current filter map (Record<string, string[]>) whenever any column filter changes. Keys are column keys; columns with no selection are omitted from the map.',
|
|
45
55
|
},
|
|
46
56
|
{
|
|
47
57
|
name: 'emptyState',
|
|
@@ -66,6 +76,18 @@ export const COMPONENT_MANIFEST = {
|
|
|
66
76
|
{ key: 'status', header: 'Status', render: (row) => <Badge>{row.status}</Badge> },
|
|
67
77
|
]}
|
|
68
78
|
rows={users}
|
|
79
|
+
/>`,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: 'Sortable + filterable table',
|
|
83
|
+
code: `<DataTable
|
|
84
|
+
columns={[
|
|
85
|
+
{ key: 'name', header: 'Name', sortable: true, filterable: true },
|
|
86
|
+
{ key: 'role', header: 'Role', filterable: true },
|
|
87
|
+
{ key: 'status', header: 'Status', render: (row) => <Badge>{row.status}</Badge> },
|
|
88
|
+
]}
|
|
89
|
+
rows={users}
|
|
90
|
+
onFilterChange={(filters) => console.log(filters)}
|
|
69
91
|
/>`,
|
|
70
92
|
},
|
|
71
93
|
{
|
|
@@ -54,6 +54,13 @@ export const ButtonManifest = {
|
|
|
54
54
|
default: 'false',
|
|
55
55
|
description: 'Stretches the button to fill its container width.',
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
name: 'bordered',
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
required: false,
|
|
61
|
+
default: 'true',
|
|
62
|
+
description: 'When false removes the button border entirely, producing a flat look.',
|
|
63
|
+
},
|
|
57
64
|
{
|
|
58
65
|
name: 'leftIcon',
|
|
59
66
|
type: 'ReactNode',
|
|
@@ -106,6 +113,10 @@ export const ButtonManifest = {
|
|
|
106
113
|
title: 'Full-width submit',
|
|
107
114
|
code: `<Button variant="primary" type="submit" fullWidth>Sign in</Button>`,
|
|
108
115
|
},
|
|
116
|
+
{
|
|
117
|
+
title: 'Borderless primary',
|
|
118
|
+
code: `<Button variant="primary" bordered={false}>Flat primary</Button>`,
|
|
119
|
+
},
|
|
109
120
|
],
|
|
110
121
|
compositionGraph: [],
|
|
111
122
|
accessibility: {
|