@astryxdesign/core 0.1.7-canary.fb9c68c → 0.1.7
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/README.md +15 -24
- package/dist/Collapsible/Collapsible.d.ts +1 -11
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +4 -36
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +3 -4
- package/dist/ContextMenu/index.d.ts +0 -3
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +1 -7
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +4 -8
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -17
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +0 -18
- package/dist/DropdownMenu/index.d.ts +1 -5
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +1 -10
- package/dist/Icon/Icon.d.ts +12 -17
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +20 -28
- package/dist/Outline/Outline.d.ts +3 -69
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +26 -129
- package/dist/Outline/useScrollSpy.d.ts +7 -27
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +70 -202
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +0 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/index.d.ts +0 -2
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +0 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -24
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +7 -18
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +3 -6
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +1 -8
- package/dist/astryx.css +2 -18
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/hct.d.ts +1 -0
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +3 -0
- package/locales/en.json +0 -28
- package/locales/pseudo.json +0 -21
- package/package.json +1 -3
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/Collapsible/Collapsible.doc.mjs +0 -7
- package/src/Collapsible/Collapsible.test.tsx +0 -80
- package/src/Collapsible/Collapsible.tsx +5 -58
- package/src/ContextMenu/ContextMenu.test.tsx +1 -75
- package/src/ContextMenu/ContextMenu.tsx +5 -10
- package/src/ContextMenu/index.ts +0 -15
- package/src/DropdownMenu/DropdownMenu.doc.mjs +0 -23
- package/src/DropdownMenu/DropdownMenu.tsx +6 -11
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -29
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +1 -7
- package/src/DropdownMenu/index.ts +3 -23
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/FieldLabel.doc.mjs +1 -1
- package/src/Icon/Icon.test.tsx +0 -46
- package/src/Icon/Icon.tsx +24 -37
- package/src/NumberInput/NumberInput.doc.mjs +2 -2
- package/src/Outline/Outline.doc.mjs +3 -123
- package/src/Outline/Outline.test.tsx +0 -735
- package/src/Outline/Outline.tsx +33 -184
- package/src/Outline/useScrollSpy.ts +72 -248
- package/src/Selector/SelectorOption.doc.mjs +1 -1
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/TabList/Tab.tsx +1 -5
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +0 -66
- package/src/TabList/TabList.tsx +0 -8
- package/src/TabList/TabMenu.tsx +1 -4
- package/src/Table/Table.doc.mjs +0 -2
- package/src/Table/index.ts +0 -7
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +1 -90
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +5 -37
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +9 -95
- package/src/Table/plugins/sortable/useTableSortable.tsx +7 -23
- package/src/Table/useBaseTablePlugins.ts +2 -6
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/Toolbar/Toolbar.tsx +3 -10
- package/src/theme/Theme.doc.mjs +4 -4
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/hct.ts +4 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +0 -89
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +0 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +0 -170
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +0 -69
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +0 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +0 -70
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +0 -69
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +0 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +0 -178
- package/dist/DropdownMenu/menuItemRoles.d.ts +0 -13
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +0 -1
- package/dist/DropdownMenu/menuItemRoles.js +0 -15
- package/dist/Table/plugins/tree/index.d.ts +0 -5
- package/dist/Table/plugins/tree/index.d.ts.map +0 -1
- package/dist/Table/plugins/tree/index.js +0 -4
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +0 -38
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +0 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +0 -358
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +0 -51
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +0 -1
- package/dist/Table/plugins/tree/useTableTreeState.js +0 -175
- package/scripts/postinstall.mjs +0 -93
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +0 -95
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +0 -237
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +0 -80
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +0 -119
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +0 -80
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +0 -232
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +0 -163
- package/src/DropdownMenu/menuItemRoles.ts +0 -22
- package/src/Table/plugins/tree/index.ts +0 -12
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +0 -163
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +0 -594
- package/src/Table/plugins/tree/useTableTreeData.tsx +0 -495
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +0 -636
- package/src/Table/plugins/tree/useTableTreeState.tsx +0 -253
- package/src/Table/useTableTreeData.doc.mjs +0 -75
- package/src/Table/useTableTreeState.doc.mjs +0 -98
- package/src/postinstall.test.mjs +0 -71
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
4
|
-
import {render,
|
|
4
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
5
5
|
import {useState} from 'react';
|
|
6
6
|
import {Table} from '../../Table';
|
|
7
7
|
import type {TableColumn} from '../../types';
|
|
@@ -153,92 +153,3 @@ describe('useTableRowExpansion', () => {
|
|
|
153
153
|
expect(screen.queryByText('File B1')).not.toBeInTheDocument();
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
|
-
|
|
157
|
-
describe('useTableRowExpansionState cycle guard', () => {
|
|
158
|
-
/** A row whose children array contains the row itself (plus a real child). */
|
|
159
|
-
function makeSelfReferential(): TreeItem[] {
|
|
160
|
-
const x: TreeItem = {id: 'x', name: 'Self', children: []};
|
|
161
|
-
const y: TreeItem = {id: 'y', name: 'Leaf Y', children: []};
|
|
162
|
-
x.children.push(x, y);
|
|
163
|
-
return [x];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** root -> child -> grand, where grand's children point back at root. */
|
|
167
|
-
function makeDeepCycle(): TreeItem[] {
|
|
168
|
-
const root: TreeItem = {id: 'root', name: 'Root', children: []};
|
|
169
|
-
const child: TreeItem = {id: 'child', name: 'Child', children: []};
|
|
170
|
-
const grand: TreeItem = {id: 'grand', name: 'Grand', children: []};
|
|
171
|
-
root.children.push(child);
|
|
172
|
-
child.children.push(grand);
|
|
173
|
-
grand.children.push(root);
|
|
174
|
-
return [root];
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function renderState(baseData: TreeItem[], expandedKeys: Set<string>) {
|
|
178
|
-
const setExpandedKeys = vi.fn();
|
|
179
|
-
const hook = renderHook(() =>
|
|
180
|
-
useTableRowExpansionState<TreeItem>({
|
|
181
|
-
baseData,
|
|
182
|
-
getChildren: item => item.children,
|
|
183
|
-
getRowKey: item => item.id,
|
|
184
|
-
expandedKeys,
|
|
185
|
-
setExpandedKeys,
|
|
186
|
-
}),
|
|
187
|
-
);
|
|
188
|
-
return {hook, setExpandedKeys};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
it('terminates on a self-referential expanded row and flattens each key once', () => {
|
|
192
|
-
const {hook} = renderState(makeSelfReferential(), new Set(['x']));
|
|
193
|
-
const {data, expansionConfig} = hook.result.current;
|
|
194
|
-
expect(data.map(item => item.id)).toEqual(['x', 'y']);
|
|
195
|
-
expect(expansionConfig.getDepth?.(data[0])).toBe(0);
|
|
196
|
-
expect(expansionConfig.getDepth?.(data[1])).toBe(1);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('terminates on a deeper cycle back to the root and flattens each key once', () => {
|
|
200
|
-
const {hook} = renderState(
|
|
201
|
-
makeDeepCycle(),
|
|
202
|
-
new Set(['root', 'child', 'grand']),
|
|
203
|
-
);
|
|
204
|
-
const {data, expansionConfig} = hook.result.current;
|
|
205
|
-
expect(data.map(item => item.id)).toEqual(['root', 'child', 'grand']);
|
|
206
|
-
expect(expansionConfig.getDepth?.(data[0])).toBe(0);
|
|
207
|
-
expect(expansionConfig.getDepth?.(data[1])).toBe(1);
|
|
208
|
-
expect(expansionConfig.getDepth?.(data[2])).toBe(2);
|
|
209
|
-
// The cycle guard keeps isAllExpanded computable — true, not a crash.
|
|
210
|
-
expect(expansionConfig.isAllExpanded).toBe(true);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('terminates when collecting allExpandableKeys on cyclic data with nothing expanded', () => {
|
|
214
|
-
const {hook, setExpandedKeys} = renderState(
|
|
215
|
-
makeDeepCycle(),
|
|
216
|
-
new Set<string>(),
|
|
217
|
-
);
|
|
218
|
-
const {data, expansionConfig} = hook.result.current;
|
|
219
|
-
expect(data.map(item => item.id)).toEqual(['root']);
|
|
220
|
-
expansionConfig.onToggleExpandAll?.(true);
|
|
221
|
-
const nextKeys = setExpandedKeys.mock.calls[0][0] as Set<string>;
|
|
222
|
-
expect(Array.from(nextKeys)).toEqual(['root', 'child', 'grand']);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('re-walks a shared child under each expanded parent (ancestor-path, not visited-set, semantics)', () => {
|
|
226
|
-
const leaf: TreeItem = {id: 'leaf', name: 'Leaf', children: []};
|
|
227
|
-
const shared: TreeItem = {id: 's', name: 'Shared', children: [leaf]};
|
|
228
|
-
const p1: TreeItem = {id: 'p1', name: 'Parent 1', children: [shared]};
|
|
229
|
-
const p2: TreeItem = {id: 'p2', name: 'Parent 2', children: [shared]};
|
|
230
|
-
const {hook} = renderState([p1, p2], new Set(['p1', 'p2', 's']));
|
|
231
|
-
// 's' is on neither parent's ancestor path, so it must flatten under
|
|
232
|
-
// both — the guard only skips true cycles, matching pre-guard behavior
|
|
233
|
-
// for acyclic (DAG-shaped) data.
|
|
234
|
-
const {data} = hook.result.current;
|
|
235
|
-
expect(data.map(item => item.id)).toEqual([
|
|
236
|
-
'p1',
|
|
237
|
-
's',
|
|
238
|
-
'leaf',
|
|
239
|
-
'p2',
|
|
240
|
-
's',
|
|
241
|
-
'leaf',
|
|
242
|
-
]);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
@@ -141,19 +141,12 @@ export function useTableRowExpansionState<T extends Record<string, unknown>>({
|
|
|
141
141
|
|
|
142
142
|
const depthMap = useMemo(() => {
|
|
143
143
|
const map = new Map<string, number>();
|
|
144
|
-
// Ancestor keys on the current walk path — guards against cyclic data.
|
|
145
|
-
const path = new Set<string>();
|
|
146
144
|
function walk(items: T[], depth: number) {
|
|
147
145
|
for (const item of items) {
|
|
148
146
|
const key = getRowKey(item);
|
|
149
|
-
if (path.has(key)) {
|
|
150
|
-
continue; // cyclic edge — skip
|
|
151
|
-
}
|
|
152
147
|
map.set(key, depth);
|
|
153
148
|
if (expandedKeys.has(key)) {
|
|
154
|
-
path.add(key);
|
|
155
149
|
walk(getChildren(item), depth + 1);
|
|
156
|
-
path.delete(key);
|
|
157
150
|
}
|
|
158
151
|
}
|
|
159
152
|
}
|
|
@@ -163,19 +156,12 @@ export function useTableRowExpansionState<T extends Record<string, unknown>>({
|
|
|
163
156
|
|
|
164
157
|
const data = useMemo(() => {
|
|
165
158
|
const result: T[] = [];
|
|
166
|
-
// Ancestor keys on the current walk path — guards against cyclic data.
|
|
167
|
-
const path = new Set<string>();
|
|
168
159
|
function walk(items: T[]) {
|
|
169
160
|
for (const item of items) {
|
|
170
|
-
const key = getRowKey(item);
|
|
171
|
-
if (path.has(key)) {
|
|
172
|
-
continue; // cyclic edge — skip
|
|
173
|
-
}
|
|
174
161
|
result.push(item);
|
|
162
|
+
const key = getRowKey(item);
|
|
175
163
|
if (expandedKeys.has(key)) {
|
|
176
|
-
path.add(key);
|
|
177
164
|
walk(getChildren(item));
|
|
178
|
-
path.delete(key);
|
|
179
165
|
}
|
|
180
166
|
}
|
|
181
167
|
}
|
|
@@ -186,19 +172,11 @@ export function useTableRowExpansionState<T extends Record<string, unknown>>({
|
|
|
186
172
|
// Every expandable key across the whole tree (drives expand-all).
|
|
187
173
|
const allExpandableKeys = useMemo(() => {
|
|
188
174
|
const keys: string[] = [];
|
|
189
|
-
// Ancestor keys on the current walk path — guards against cyclic data.
|
|
190
|
-
const path = new Set<string>();
|
|
191
175
|
function walk(items: T[]) {
|
|
192
176
|
for (const item of items) {
|
|
193
|
-
const key = getRowKey(item);
|
|
194
|
-
if (path.has(key)) {
|
|
195
|
-
continue; // cyclic edge — skip
|
|
196
|
-
}
|
|
197
177
|
if (isExpandable(item)) {
|
|
198
|
-
keys.push(
|
|
199
|
-
path.add(key);
|
|
178
|
+
keys.push(getRowKey(item));
|
|
200
179
|
walk(getChildren(item));
|
|
201
|
-
path.delete(key);
|
|
202
180
|
}
|
|
203
181
|
}
|
|
204
182
|
}
|
|
@@ -421,11 +399,7 @@ export function useTableRowExpansion<T extends Record<string, unknown>>(
|
|
|
421
399
|
<ExpansionChevron
|
|
422
400
|
isExpanded={isExpanded}
|
|
423
401
|
onToggle={() => onToggle(key)}
|
|
424
|
-
ariaLabel={
|
|
425
|
-
isExpanded
|
|
426
|
-
? t('@astryx.tableRowExpansion.collapseRow')
|
|
427
|
-
: t('@astryx.tableRowExpansion.expandRow')
|
|
428
|
-
}
|
|
402
|
+
ariaLabel={isExpanded ? t('@astryx.tableRowExpansion.collapseRow') : t('@astryx.tableRowExpansion.expandRow')}
|
|
429
403
|
/>
|
|
430
404
|
);
|
|
431
405
|
},
|
|
@@ -483,11 +457,7 @@ export function useTableRowExpansion<T extends Record<string, unknown>>(
|
|
|
483
457
|
<ExpansionChevron
|
|
484
458
|
isExpanded={isExpanded}
|
|
485
459
|
onToggle={() => onToggle(key)}
|
|
486
|
-
ariaLabel={
|
|
487
|
-
isExpanded
|
|
488
|
-
? t('@astryx.tableRowExpansion.collapseRow')
|
|
489
|
-
: t('@astryx.tableRowExpansion.expandRow')
|
|
490
|
-
}
|
|
460
|
+
ariaLabel={isExpanded ? t('@astryx.tableRowExpansion.collapseRow') : t('@astryx.tableRowExpansion.expandRow')}
|
|
491
461
|
/>
|
|
492
462
|
) : (
|
|
493
463
|
<span {...stylex.props(expansionStyles.placeholder)} />
|
|
@@ -529,9 +499,7 @@ export function useTableRowExpansion<T extends Record<string, unknown>>(
|
|
|
529
499
|
{...stylex.props(expansionStyles.chevronButton)}
|
|
530
500
|
onClick={() => onToggleExpandAll(!allExpanded)}
|
|
531
501
|
aria-label={
|
|
532
|
-
allExpanded
|
|
533
|
-
? t('@astryx.tableRowExpansion.collapseAllRows')
|
|
534
|
-
: t('@astryx.tableRowExpansion.expandAllRows')
|
|
502
|
+
allExpanded ? t('@astryx.tableRowExpansion.collapseAllRows') : t('@astryx.tableRowExpansion.expandAllRows')
|
|
535
503
|
}>
|
|
536
504
|
<span
|
|
537
505
|
{...stylex.props(
|
|
@@ -12,9 +12,11 @@ import {render, screen, fireEvent} from '@testing-library/react';
|
|
|
12
12
|
import userEvent from '@testing-library/user-event';
|
|
13
13
|
import {useState} from 'react';
|
|
14
14
|
import {Table} from '../../Table';
|
|
15
|
-
import {InternationalizationProvider} from '../../../i18n';
|
|
16
15
|
import type {TableColumn} from '../../types';
|
|
17
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
useTableSortable,
|
|
18
|
+
type TableSortState,
|
|
19
|
+
} from './useTableSortable';
|
|
18
20
|
|
|
19
21
|
// =============================================================================
|
|
20
22
|
// Test Data
|
|
@@ -71,7 +73,9 @@ function SortableTable({
|
|
|
71
73
|
isMultiSortEnabled,
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
return
|
|
76
|
+
return (
|
|
77
|
+
<Table data={data} columns={columns} plugins={{sort: sortPlugin}} />
|
|
78
|
+
);
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
// =============================================================================
|
|
@@ -668,15 +672,11 @@ describe('useTableSortable — context menu actions', () => {
|
|
|
668
672
|
|
|
669
673
|
// Apply ascending → "Clear sort" now appears.
|
|
670
674
|
fireEvent.click(
|
|
671
|
-
screen.getAllByRole('menuitem', {
|
|
672
|
-
name: 'Sort ascending',
|
|
673
|
-
hidden: true,
|
|
674
|
-
})[0],
|
|
675
|
+
screen.getAllByRole('menuitem', {name: 'Sort ascending', hidden: true})[0],
|
|
675
676
|
);
|
|
676
677
|
fireEvent.contextMenu(screen.getByText('Name'));
|
|
677
678
|
expect(
|
|
678
|
-
screen.getAllByRole('menuitem', {name: 'Clear sort', hidden: true})
|
|
679
|
-
.length,
|
|
679
|
+
screen.getAllByRole('menuitem', {name: 'Clear sort', hidden: true}).length,
|
|
680
680
|
).toBeGreaterThan(0);
|
|
681
681
|
});
|
|
682
682
|
|
|
@@ -709,89 +709,3 @@ describe('useTableSortable — context menu actions', () => {
|
|
|
709
709
|
expect(onSortChange).toHaveBeenLastCalledWith([]);
|
|
710
710
|
});
|
|
711
711
|
});
|
|
712
|
-
|
|
713
|
-
// =============================================================================
|
|
714
|
-
// i18n (header-button aria-labels route through the catalog)
|
|
715
|
-
// =============================================================================
|
|
716
|
-
|
|
717
|
-
describe('useTableSortable — i18n', () => {
|
|
718
|
-
it('localizes the unsorted sort-by aria-label via @astryx.table.sort.sortBy', () => {
|
|
719
|
-
render(
|
|
720
|
-
<InternationalizationProvider
|
|
721
|
-
locale="fr"
|
|
722
|
-
overrides={{
|
|
723
|
-
fr: {'@astryx.table.sort.sortBy': 'Trier par {label}'},
|
|
724
|
-
}}>
|
|
725
|
-
<SortableTable />
|
|
726
|
-
</InternationalizationProvider>,
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
expect(
|
|
730
|
-
screen.getByRole('button', {name: 'Trier par Name'}),
|
|
731
|
-
).toBeInTheDocument();
|
|
732
|
-
expect(
|
|
733
|
-
screen.getByRole('button', {name: 'Trier par Age'}),
|
|
734
|
-
).toBeInTheDocument();
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
it('localizes the multi-sort priority aria-label with ICU number args', () => {
|
|
738
|
-
render(
|
|
739
|
-
<InternationalizationProvider
|
|
740
|
-
locale="fr"
|
|
741
|
-
overrides={{
|
|
742
|
-
fr: {
|
|
743
|
-
'@astryx.table.sort.sortedByWithPriority':
|
|
744
|
-
'Trier par {label}, tri {direction}, priorité {rank, number} sur {total, number}',
|
|
745
|
-
'@astryx.table.sort.direction.ascending': 'croissant',
|
|
746
|
-
'@astryx.table.sort.direction.descending': 'décroissant',
|
|
747
|
-
},
|
|
748
|
-
}}>
|
|
749
|
-
<SortableTable
|
|
750
|
-
isMultiSortEnabled
|
|
751
|
-
initialSort={[
|
|
752
|
-
{sortKey: 'name', direction: 'ascending'},
|
|
753
|
-
{sortKey: 'age', direction: 'descending'},
|
|
754
|
-
]}
|
|
755
|
-
/>
|
|
756
|
-
</InternationalizationProvider>,
|
|
757
|
-
);
|
|
758
|
-
|
|
759
|
-
// The overridden direction words deliberately differ from the raw enum
|
|
760
|
-
// values ('ascending'/'descending'), so an implementation interpolating
|
|
761
|
-
// the enum without translating it cannot pass — for either direction.
|
|
762
|
-
expect(
|
|
763
|
-
screen.getByRole('button', {
|
|
764
|
-
name: 'Trier par Name, tri croissant, priorité 1 sur 2',
|
|
765
|
-
}),
|
|
766
|
-
).toBeInTheDocument();
|
|
767
|
-
expect(
|
|
768
|
-
screen.getByRole('button', {
|
|
769
|
-
name: 'Trier par Age, tri décroissant, priorité 2 sur 2',
|
|
770
|
-
}),
|
|
771
|
-
).toBeInTheDocument();
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
it('localizes the sorted aria-label and direction word via their own keys', () => {
|
|
775
|
-
render(
|
|
776
|
-
<InternationalizationProvider
|
|
777
|
-
locale="fr"
|
|
778
|
-
overrides={{
|
|
779
|
-
fr: {
|
|
780
|
-
'@astryx.table.sort.sortedBy': 'Trié par {label}, {direction}',
|
|
781
|
-
'@astryx.table.sort.direction.ascending': 'croissant',
|
|
782
|
-
},
|
|
783
|
-
}}>
|
|
784
|
-
<SortableTable
|
|
785
|
-
initialSort={[{sortKey: 'name', direction: 'ascending'}]}
|
|
786
|
-
/>
|
|
787
|
-
</InternationalizationProvider>,
|
|
788
|
-
);
|
|
789
|
-
|
|
790
|
-
// Both the composed template and the direction word resolve through
|
|
791
|
-
// their own catalog keys; 'croissant' differs from the raw enum value,
|
|
792
|
-
// so neither a hardcoded English frame nor raw-enum interpolation passes.
|
|
793
|
-
expect(
|
|
794
|
-
screen.getByRole('button', {name: 'Trié par Name, croissant'}),
|
|
795
|
-
).toBeInTheDocument();
|
|
796
|
-
});
|
|
797
|
-
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useTableSortable.tsx
|
|
7
|
-
* @input React, types, Icon, theme tokens
|
|
7
|
+
* @input React, types, Icon, theme tokens
|
|
8
8
|
* @output Exports useTableSortable hook and sort-related types
|
|
9
9
|
* @position Sortable plugin; consumed by Table via plugins prop
|
|
10
10
|
*
|
|
@@ -18,7 +18,7 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
18
18
|
import {colorVars, spacingVars, radiusVars} from '../../../theme/tokens.stylex';
|
|
19
19
|
import {Icon} from '../../../Icon';
|
|
20
20
|
import {resolveContextActions} from '../../tableContextMenu';
|
|
21
|
-
import {useTranslator
|
|
21
|
+
import {useTranslator} from '../../../i18n';
|
|
22
22
|
import type {
|
|
23
23
|
TablePlugin,
|
|
24
24
|
HeaderCellRenderProps,
|
|
@@ -184,7 +184,6 @@ function getHeaderLabel<T extends Record<string, unknown>>(
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
function buildAriaLabel<T extends Record<string, unknown>>(
|
|
187
|
-
t: TranslatorFn,
|
|
188
187
|
column: TableColumn<T>,
|
|
189
188
|
direction: TableSortDirection | null,
|
|
190
189
|
rank: number | null,
|
|
@@ -192,21 +191,13 @@ function buildAriaLabel<T extends Record<string, unknown>>(
|
|
|
192
191
|
): string {
|
|
193
192
|
const label = getHeaderLabel(column);
|
|
194
193
|
if (direction == null) {
|
|
195
|
-
return
|
|
194
|
+
return `Sort by ${label}`;
|
|
196
195
|
}
|
|
197
|
-
|
|
198
|
-
direction === 'ascending'
|
|
199
|
-
? t('@astryx.table.sort.direction.ascending')
|
|
200
|
-
: t('@astryx.table.sort.direction.descending');
|
|
196
|
+
let ariaLabel = `Sort by ${label}, sorted ${direction}`;
|
|
201
197
|
if (rank != null && total > 1) {
|
|
202
|
-
|
|
203
|
-
label,
|
|
204
|
-
direction: directionLabel,
|
|
205
|
-
rank,
|
|
206
|
-
total,
|
|
207
|
-
});
|
|
198
|
+
ariaLabel += `, priority ${rank} of ${total}`;
|
|
208
199
|
}
|
|
209
|
-
return
|
|
200
|
+
return ariaLabel;
|
|
210
201
|
}
|
|
211
202
|
|
|
212
203
|
function getNextDirection(
|
|
@@ -236,7 +227,6 @@ function SortHeaderButton<T extends Record<string, unknown>>({
|
|
|
236
227
|
children: ReactNode;
|
|
237
228
|
configRef: React.RefObject<UseTableSortableConfig>;
|
|
238
229
|
}) {
|
|
239
|
-
const t = useTranslator();
|
|
240
230
|
const config = configRef.current;
|
|
241
231
|
const sortKey = resolveSortKey(column) ?? '';
|
|
242
232
|
const entryIndex = config.sort.findIndex(e => e.sortKey === sortKey);
|
|
@@ -254,13 +244,7 @@ function SortHeaderButton<T extends Record<string, unknown>>({
|
|
|
254
244
|
? 'arrowDown'
|
|
255
245
|
: 'arrowsUpDown';
|
|
256
246
|
|
|
257
|
-
const ariaLabel = buildAriaLabel(
|
|
258
|
-
t,
|
|
259
|
-
column,
|
|
260
|
-
direction,
|
|
261
|
-
rank,
|
|
262
|
-
config.sort.length,
|
|
263
|
-
);
|
|
247
|
+
const ariaLabel = buildAriaLabel(column, direction, rank, config.sort.length);
|
|
264
248
|
|
|
265
249
|
const handleClick = (e: React.MouseEvent | React.KeyboardEvent) => {
|
|
266
250
|
const cfg = configRef.current;
|
|
@@ -18,15 +18,12 @@
|
|
|
18
18
|
* Canonical order:
|
|
19
19
|
* 1. columnSettings — column filtering (future: transformColumns)
|
|
20
20
|
* 2. sort — header cell sort controls
|
|
21
|
-
* 3.
|
|
22
|
-
* 4.
|
|
23
|
-
* 5. pagination — pagination controls around the table
|
|
21
|
+
* 3. selection — checkbox column + row selection
|
|
22
|
+
* 4. pagination — pagination controls around the table
|
|
24
23
|
*
|
|
25
24
|
* Rationale:
|
|
26
25
|
* - columnSettings filters columns before sort/selection see them
|
|
27
26
|
* - sort adds header cell UI before selection adds its header column
|
|
28
|
-
* - tree wraps the first *user* column before selection prepends its
|
|
29
|
-
* checkbox column, so the expander never lands in the checkbox column
|
|
30
27
|
* - selection adds its column after sort so the checkbox header
|
|
31
28
|
* doesn't get a sort button
|
|
32
29
|
* - pagination wraps the table in context last (outermost provider)
|
|
@@ -49,7 +46,6 @@ import type {TablePlugin} from './types';
|
|
|
49
46
|
const PLUGIN_ORDER: ReadonlyArray<string> = [
|
|
50
47
|
'columnSettings',
|
|
51
48
|
'sort',
|
|
52
|
-
'tree',
|
|
53
49
|
'selection',
|
|
54
50
|
'pagination',
|
|
55
51
|
];
|
|
@@ -116,7 +116,7 @@ export const docs = {
|
|
|
116
116
|
name: 'startIcon',
|
|
117
117
|
type: 'IconType',
|
|
118
118
|
description:
|
|
119
|
-
'Icon component rendered inside the leading edge of the textarea wrapper. See `astryx docs icons` for valid semantic names.',
|
|
119
|
+
'Icon component rendered inside the leading edge of the textarea wrapper. See `npx astryx docs icons` for valid semantic names.',
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
name: 'hasSpellCheck',
|
|
@@ -106,7 +106,7 @@ export const docs = {
|
|
|
106
106
|
name: 'startIcon',
|
|
107
107
|
type: 'IconType',
|
|
108
108
|
description:
|
|
109
|
-
'SVG icon component displayed at the start of the input. See `astryx docs icons` for valid semantic names.',
|
|
109
|
+
'SVG icon component displayed at the start of the input. See `npx astryx docs icons` for valid semantic names.',
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
name: 'status',
|
package/src/Toolbar/Toolbar.tsx
CHANGED
|
@@ -239,7 +239,6 @@ export function Toolbar({
|
|
|
239
239
|
const hasCenterContent = centerContent != null;
|
|
240
240
|
const hasStartContent = startContent != null;
|
|
241
241
|
const hasEndContent = endContent != null;
|
|
242
|
-
const hasBottomDivider = dividers?.includes('bottom') ?? false;
|
|
243
242
|
|
|
244
243
|
const gapVar = spacingVars[spacingStepToVar[gap]] as string;
|
|
245
244
|
|
|
@@ -317,15 +316,9 @@ export function Toolbar({
|
|
|
317
316
|
orientation === 'vertical' && styles.vertical,
|
|
318
317
|
sizeStyles.base,
|
|
319
318
|
dynamicStyles.gap(gapVar),
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// nested TabList's indicator keeps its own default (hugging the
|
|
324
|
-
// tab) instead of floating down into the toolbar's padding.
|
|
325
|
-
hasBottomDivider &&
|
|
326
|
-
dynamicStyles.tabIndicatorBottom(
|
|
327
|
-
`calc(-1 * (${blockPaddingVarForSize[size]} + 1px))`,
|
|
328
|
-
),
|
|
319
|
+
dynamicStyles.tabIndicatorBottom(
|
|
320
|
+
`calc(-1 * (${blockPaddingVarForSize[size]}${dividers?.includes('bottom') ? ' + 1px' : ''}))`,
|
|
321
|
+
),
|
|
329
322
|
),
|
|
330
323
|
)}
|
|
331
324
|
{...props}>
|
package/src/theme/Theme.doc.mjs
CHANGED
|
@@ -39,12 +39,12 @@ export const docs = {
|
|
|
39
39
|
},
|
|
40
40
|
usage: {
|
|
41
41
|
description:
|
|
42
|
-
'Wraps a subtree with a specific Astryx theme. For static production themes, use `astryx theme build` and import the generated CSS plus built theme object for first-paint and SSR performance. Use runtime `defineTheme()` when themes are dynamic or for prototyping.\n\n`defineTheme` accepts a `tokens` object whose keys are CSS custom property names (always prefixed with `--`). Common token names include `--color-accent`, `--color-background-surface`, `--color-background-body`, `--color-text-primary`, `--color-text-secondary`, `--radius-container`, `--spacing-1` through `--spacing-6`. Values can be a string (same for light/dark) or a `[light, dark]` tuple.\n\nExample:\n```ts\nimport {defineTheme} from \'@astryxdesign/core/theme\';\nconst myTheme = defineTheme({\n name: \'ocean\',\n tokens: {\n \'--color-accent\': [\'#0077B6\', \'#48CAE4\'],\n \'--color-background-surface\': [\'#F0F8FF\', \'#0A1628\'],\n \'--color-text-primary\': [\'#0A1317\', \'#FFFFFF\'],\n \'--radius-container\': \'16px\',\n },\n});\n```',
|
|
42
|
+
'Wraps a subtree with a specific Astryx theme. For static production themes, use `npx astryx theme build` and import the generated CSS plus built theme object for first-paint and SSR performance. Use runtime `defineTheme()` when themes are dynamic or for prototyping.\n\n`defineTheme` accepts a `tokens` object whose keys are CSS custom property names (always prefixed with `--`). Common token names include `--color-accent`, `--color-background-surface`, `--color-background-body`, `--color-text-primary`, `--color-text-secondary`, `--radius-container`, `--spacing-1` through `--spacing-6`. Values can be a string (same for light/dark) or a `[light, dark]` tuple.\n\nExample:\n```ts\nimport {defineTheme} from \'@astryxdesign/core/theme\';\nconst myTheme = defineTheme({\n name: \'ocean\',\n tokens: {\n \'--color-accent\': [\'#0077B6\', \'#48CAE4\'],\n \'--color-background-surface\': [\'#F0F8FF\', \'#0A1628\'],\n \'--color-text-primary\': [\'#0A1317\', \'#FFFFFF\'],\n \'--radius-container\': \'16px\',\n },\n});\n```',
|
|
43
43
|
bestPractices: [
|
|
44
44
|
{
|
|
45
45
|
guidance: true,
|
|
46
46
|
description:
|
|
47
|
-
'Build app themes that are known ahead of time with `astryx theme build`, then import the generated CSS and built theme object.',
|
|
47
|
+
'Build app themes that are known ahead of time with `npx astryx theme build`, then import the generated CSS and built theme object.',
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
guidance: true,
|
|
@@ -90,12 +90,12 @@ export const docs = {
|
|
|
90
90
|
export const docsDense = {
|
|
91
91
|
usage: {
|
|
92
92
|
description:
|
|
93
|
-
'Wraps subtree w/ specific Astryx theme. For static production themes, use `astryx theme build` + generated CSS + built theme object for first-paint/SSR performance. Use runtime `defineTheme()` for dynamic themes or prototyping. Token names always start with `--` (e.g. `--color-accent`, `--color-background-surface`).',
|
|
93
|
+
'Wraps subtree w/ specific Astryx theme. For static production themes, use `npx astryx theme build` + generated CSS + built theme object for first-paint/SSR performance. Use runtime `defineTheme()` for dynamic themes or prototyping. Token names always start with `--` (e.g. `--color-accent`, `--color-background-surface`).',
|
|
94
94
|
bestPractices: [
|
|
95
95
|
{
|
|
96
96
|
guidance: true,
|
|
97
97
|
description:
|
|
98
|
-
'Build app themes known ahead of time w/ `astryx theme build`; import generated CSS + built theme object.',
|
|
98
|
+
'Build app themes known ahead of time w/ `npx astryx theme build`; import generated CSS + built theme object.',
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
guidance: true,
|
package/src/theme/Theme.tsx
CHANGED
|
@@ -127,7 +127,7 @@ function useThemeStyleInjection(theme: DefinedTheme): void {
|
|
|
127
127
|
`For better performance, use the pre-built theme:\n\n` +
|
|
128
128
|
` import {${theme.name}Theme} from '@astryxdesign/theme-${theme.name}/built';\n` +
|
|
129
129
|
` import '@astryxdesign/theme-${theme.name}/theme.css';\n\n` +
|
|
130
|
-
`For custom themes, run \`npx
|
|
130
|
+
`For custom themes, run \`npx astryx theme build <file>\` to generate ` +
|
|
131
131
|
`the built artifacts.`,
|
|
132
132
|
);
|
|
133
133
|
}
|
package/src/theme/defineTheme.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Two distribution modes:
|
|
7
7
|
* - Unbuilt: Theme generates CSS and injects a <style> tag at runtime
|
|
8
|
-
* - Built: `astryx theme build` pre-compiles to a CSS file; Theme just
|
|
8
|
+
* - Built: `npx astryx theme build` pre-compiles to a CSS file; Theme just
|
|
9
9
|
* sets the data-astryx-theme attribute
|
|
10
10
|
*
|
|
11
11
|
* Token values can be:
|
package/src/theme/hct.ts
CHANGED
|
@@ -114,6 +114,10 @@ export function toneToY(tone: number): number {
|
|
|
114
114
|
return labFInv((tone + 16) / 116);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
export function yToTone(y: number): number {
|
|
118
|
+
return 116 * labF(y) - 16;
|
|
119
|
+
}
|
|
120
|
+
|
|
117
121
|
// =============================================================================
|
|
118
122
|
// Hex <-> RGB
|
|
119
123
|
// =============================================================================
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file DropdownMenuCheckboxItem.tsx
|
|
3
|
-
* @input React, stylex, Item + Icon + DropdownMenu context + tokens from core
|
|
4
|
-
* @output DropdownMenuCheckboxItem — a standalone checkable menu item.
|
|
5
|
-
* @position Sub-component; used inside DropdownMenu.
|
|
6
|
-
*
|
|
7
|
-
* A menu item that toggles an independent boolean (role="menuitemcheckbox").
|
|
8
|
-
* Unlike CheckboxInput, there is no nested native <input>: the row itself owns
|
|
9
|
-
* the role and aria-checked, per the WAI-ARIA menuitemcheckbox pattern.
|
|
10
|
-
* Keyboard navigation (arrows/typeahead) and Enter/Space activation come from
|
|
11
|
-
* the parent DropdownMenu's useListFocus + activation path, which matches
|
|
12
|
-
* menuitemcheckbox alongside plain menuitem rows.
|
|
13
|
-
*
|
|
14
|
-
* The square checkbox visual is decorative (aria-hidden) — the row owns the
|
|
15
|
-
* checked state. Its size is derived from the menu's item size (a `sm` menu
|
|
16
|
-
* gets the compact control; `md`/`lg` get the standard one) and it swaps to the
|
|
17
|
-
* inline-end of the row on coarse-pointer (touch) devices via CSS `order`, so
|
|
18
|
-
* it lands where selection toggles are conventionally placed on mobile. The
|
|
19
|
-
* checkmark uses the `check` icon from the active theme's icon registry.
|
|
20
|
-
*/
|
|
21
|
-
import { type ReactNode } from 'react';
|
|
22
|
-
import { type IconType } from '../Icon';
|
|
23
|
-
import type { BaseProps } from '../BaseProps';
|
|
24
|
-
export interface DropdownMenuCheckboxItemProps extends Omit<BaseProps, 'onChange' | 'role' | 'aria-checked' | 'tabIndex'> {
|
|
25
|
-
/**
|
|
26
|
-
* Primary label text identifying the item.
|
|
27
|
-
*/
|
|
28
|
-
label: ReactNode;
|
|
29
|
-
/**
|
|
30
|
-
* Secondary description text displayed below the label.
|
|
31
|
-
*/
|
|
32
|
-
description?: ReactNode;
|
|
33
|
-
/**
|
|
34
|
-
* Icon to display before the label. Accepts a semantic icon name (see
|
|
35
|
-
* `npx astryx docs icons`) or a rendered node.
|
|
36
|
-
*/
|
|
37
|
-
icon?: ReactNode | IconType;
|
|
38
|
-
/**
|
|
39
|
-
* Whether the item is checked. Controlled — pair with `onChange`.
|
|
40
|
-
*/
|
|
41
|
-
value: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Callback fired with the next checked state when the item is toggled.
|
|
44
|
-
*/
|
|
45
|
-
onChange?: (checked: boolean) => void;
|
|
46
|
-
/**
|
|
47
|
-
* Whether the item is disabled. Disabled items stay focusable (via
|
|
48
|
-
* `aria-disabled`) so they remain discoverable by keyboard and assistive
|
|
49
|
-
* technology, but activation is blocked.
|
|
50
|
-
* @default false
|
|
51
|
-
*/
|
|
52
|
-
isDisabled?: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* Whether toggling the item closes the menu. Checkbox items default to
|
|
55
|
-
* staying open so several can be toggled in a single session, unlike radio
|
|
56
|
-
* items which default to closing on selection.
|
|
57
|
-
* @default false
|
|
58
|
-
*/
|
|
59
|
-
hasCloseOnSelect?: boolean;
|
|
60
|
-
/**
|
|
61
|
-
* Content to render after the label and description, such as a keyboard
|
|
62
|
-
* shortcut hint or badge.
|
|
63
|
-
*/
|
|
64
|
-
endContent?: ReactNode;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* A checkable dropdown menu item (role="menuitemcheckbox").
|
|
68
|
-
*
|
|
69
|
-
* Must be used inside a DropdownMenu. Toggles an independent boolean; for a
|
|
70
|
-
* one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* ```
|
|
74
|
-
* * import {DropdownMenuCheckboxItem} from '@astryxdesign/core/DropdownMenu';
|
|
75
|
-
*
|
|
76
|
-
* <DropdownMenu button={{label: 'View'}}>
|
|
77
|
-
* <DropdownMenuCheckboxItem
|
|
78
|
-
* label="Show archived"
|
|
79
|
-
* value={showArchived}
|
|
80
|
-
* onChange={setShowArchived}
|
|
81
|
-
* />
|
|
82
|
-
* </DropdownMenu>
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
export declare function DropdownMenuCheckboxItem({ label, description, icon, value, onChange, isDisabled, hasCloseOnSelect, endContent, xstyle, className, style, ...rest }: DropdownMenuCheckboxItemProps): import("react").JSX.Element;
|
|
86
|
-
export declare namespace DropdownMenuCheckboxItem {
|
|
87
|
-
var displayName: string;
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=DropdownMenuCheckboxItem.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenuCheckboxItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuCheckboxItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAc,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAElD,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAY5D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA6D5C,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CACzD,SAAS,EACT,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAClD;IACC;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,WAAW,EACX,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,UAAkB,EAClB,gBAAwB,EACxB,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,6BAA6B,+BAwD/B;yBArEe,wBAAwB"}
|