noecosystem-design 0.2.3 → 0.4.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/README.md +2 -2
- package/apps/storybook/.storybook/preview.tsx +1 -1
- package/apps/storybook/stories/catalog/app-frame.stories.tsx +213 -1
- package/apps/storybook/stories/catalog/checkbox.stories.tsx +7 -2
- package/apps/storybook/stories/catalog/data-table.stories.tsx +226 -3
- package/apps/storybook/stories/catalog/dialog.stories.tsx +2 -2
- package/apps/storybook/stories/catalog/icon-system.stories.tsx +11 -0
- package/apps/storybook/stories/catalog/menu.stories.tsx +3 -3
- package/apps/web/styles.css +0 -1
- package/docs/design-system/brand-theme.md +8 -0
- package/docs/design-system/components.md +38 -0
- package/docs/design-system/inventory.md +1 -0
- package/docs/design-system/visual-qa.md +6 -0
- package/package.json +1 -1
- package/packages/data-ui/package.json +6 -1
- package/packages/data-ui/src/data-table.tsx +2 -99
- package/packages/data-ui/src/pagination.tsx +2 -2
- package/packages/data-ui/src/table/adapters/tanstack.test.ts +116 -0
- package/packages/data-ui/src/table/adapters/tanstack.ts +179 -0
- package/packages/data-ui/src/table/contracts.ts +133 -0
- package/packages/data-ui/src/table/data-table-visual.evidence.browser.test.tsx +157 -0
- package/packages/data-ui/src/table/data-table.browser.test.tsx +180 -0
- package/packages/data-ui/src/table/data-table.tsx +524 -0
- package/packages/design-tokens/src/tailwind.css +5 -0
- package/packages/design-tokens/src/tokens.css +10 -4
- package/packages/design-tokens/src/tokens.json +8 -4
- package/packages/design-tokens/src/tokens.mjs +8 -4
- package/packages/icons/package.json +6 -1
- package/packages/icons/src/brands/nocfo.svg +8 -0
- package/packages/icons/src/nocfo-logo.browser.test.tsx +14 -0
- package/packages/icons/src/nocfo-logo.tsx +23 -0
- package/packages/registry/catalog-index.json +2 -2
- package/packages/registry/manifest.json +60 -18
- package/packages/registry/r/activity-feed.json +1 -1
- package/packages/registry/r/agent-composer.json +7 -1
- package/packages/registry/r/agent-message.json +1 -1
- package/packages/registry/r/agent-plan.json +1 -1
- package/packages/registry/r/agent-workspace.json +1 -1
- package/packages/registry/r/app-frame.json +9 -2
- package/packages/registry/r/approval-request.json +7 -1
- package/packages/registry/r/calendar.json +6 -5
- package/packages/registry/r/chart-frame.json +1 -1
- package/packages/registry/r/chart.json +1 -1
- package/packages/registry/r/dashboard-01.json +22 -2
- package/packages/registry/r/dashboard-02.json +22 -2
- package/packages/registry/r/dashboard-shell.json +1 -1
- package/packages/registry/r/data-table.json +22 -2
- package/packages/registry/r/date-picker.json +6 -5
- package/packages/registry/r/filter-bar.json +1 -1
- package/packages/registry/r/icon-system.json +18 -5
- package/packages/registry/r/inline-cta.json +6 -6
- package/packages/registry/r/metric-card.json +1 -1
- package/packages/registry/r/pagination.json +7 -5
- package/packages/registry/r/run-inspector.json +1 -1
- package/packages/registry/r/sidebar-navigation.json +5 -2
- package/packages/registry/r/tool-call.json +1 -1
- package/packages/registry/r/workflow-canvas.json +1 -1
- package/packages/registry/r/workflow-node.json +1 -1
- package/packages/registry/registry.json +12 -3
- package/packages/registry/search-index.json +23 -8
- package/packages/ui/package.json +5 -1
- package/packages/ui/src/app-frame.browser.test.tsx +6 -0
- package/packages/ui/src/app-frame.tsx +9 -2
- package/packages/ui/src/badge.tsx +4 -4
- package/packages/ui/src/calendar.tsx +1 -1
- package/packages/ui/src/checkbox.tsx +1 -1
- package/packages/ui/src/code.tsx +3 -1
- package/packages/ui/src/field.tsx +1 -1
- package/packages/ui/src/file-uploader.tsx +1 -1
- package/packages/ui/src/progress-steps.tsx +1 -1
- package/packages/ui/src/sidebar-navigation.tsx +11 -1
- package/scripts/generate-manifest.mjs +71 -0
- package/vitest.config.ts +5 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { useId, useMemo, useState } from 'react';
|
|
2
|
+
import type * as React from 'react';
|
|
3
|
+
import { ArrowDown2, ArrowUp2 } from '@noe/icons';
|
|
4
|
+
import { TechnicalValue } from '@noe/ui/bidi';
|
|
5
|
+
import { Button } from '@noe/ui/button';
|
|
6
|
+
import { Checkbox } from '@noe/ui/checkbox';
|
|
7
|
+
import { cn } from '@noe/ui/lib/cn';
|
|
8
|
+
import { Skeleton } from '@noe/ui/skeleton';
|
|
9
|
+
import { functionalUpdate, useTable } from '@tanstack/react-table';
|
|
10
|
+
import type {
|
|
11
|
+
ColumnFiltersState,
|
|
12
|
+
OnChangeFn,
|
|
13
|
+
RowSelectionState,
|
|
14
|
+
SortingState,
|
|
15
|
+
} from '@tanstack/react-table';
|
|
16
|
+
|
|
17
|
+
import { Pagination } from '../pagination';
|
|
18
|
+
import {
|
|
19
|
+
fromTanStackFilters,
|
|
20
|
+
fromTanStackSelection,
|
|
21
|
+
fromTanStackSorting,
|
|
22
|
+
noeTableFeatures,
|
|
23
|
+
toTanStackColumns,
|
|
24
|
+
toTanStackFilters,
|
|
25
|
+
toTanStackSelection,
|
|
26
|
+
toTanStackSorting,
|
|
27
|
+
} from './adapters/tanstack';
|
|
28
|
+
import type { DataColumn, DataSort, FilterSpec, PageSpec, RowId, SelectionMode } from './contracts';
|
|
29
|
+
|
|
30
|
+
export type {
|
|
31
|
+
DataColumn,
|
|
32
|
+
DataColumnSortFn,
|
|
33
|
+
DataQuery,
|
|
34
|
+
DataQueryInput,
|
|
35
|
+
DataSort,
|
|
36
|
+
DataSortDirection,
|
|
37
|
+
FilterOperator,
|
|
38
|
+
FilterSpec,
|
|
39
|
+
PageSpec,
|
|
40
|
+
RowId,
|
|
41
|
+
SelectionMode,
|
|
42
|
+
SelectionState,
|
|
43
|
+
TableStateAdapter,
|
|
44
|
+
} from './contracts';
|
|
45
|
+
export { createMemoryTableStateAdapter, toDataQuery } from './contracts';
|
|
46
|
+
|
|
47
|
+
export interface DataTablePaginationStatusInfo {
|
|
48
|
+
/** One-based index of the first row on the page. */
|
|
49
|
+
startIndex: number;
|
|
50
|
+
/** One-based index of the last row on the page. */
|
|
51
|
+
endIndex: number;
|
|
52
|
+
/** Total row count across pages. */
|
|
53
|
+
total: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface DataTableSelectionInfo {
|
|
57
|
+
selectedIds: RowId[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type DataTableFeatureProps<Row extends { id: string }> = {
|
|
61
|
+
/** Controlled NOE sorting state. */
|
|
62
|
+
sorting?: readonly DataSort[];
|
|
63
|
+
/** Initial sorting state for uncontrolled usage. */
|
|
64
|
+
defaultSorting?: readonly DataSort[];
|
|
65
|
+
onSortingChange?: (sorting: DataSort[]) => void;
|
|
66
|
+
/** Server owns row order; the table only reflects sorting state. */
|
|
67
|
+
manualSorting?: boolean;
|
|
68
|
+
} & (
|
|
69
|
+
| {
|
|
70
|
+
filters?: undefined;
|
|
71
|
+
defaultFilters?: undefined;
|
|
72
|
+
onFiltersChange?: undefined;
|
|
73
|
+
manualFiltering?: undefined;
|
|
74
|
+
}
|
|
75
|
+
| {
|
|
76
|
+
/** Controlled NOE filter state. */
|
|
77
|
+
filters: readonly FilterSpec[];
|
|
78
|
+
defaultFilters?: undefined;
|
|
79
|
+
onFiltersChange: (filters: FilterSpec[]) => void;
|
|
80
|
+
manualFiltering?: boolean;
|
|
81
|
+
}
|
|
82
|
+
| {
|
|
83
|
+
filters?: undefined;
|
|
84
|
+
/** Initial filter state for uncontrolled usage. */
|
|
85
|
+
defaultFilters?: readonly FilterSpec[];
|
|
86
|
+
onFiltersChange?: (filters: FilterSpec[]) => void;
|
|
87
|
+
manualFiltering?: boolean;
|
|
88
|
+
}
|
|
89
|
+
) &
|
|
90
|
+
(
|
|
91
|
+
| {
|
|
92
|
+
page?: undefined;
|
|
93
|
+
onPageChange?: undefined;
|
|
94
|
+
previousPageLabel?: undefined;
|
|
95
|
+
nextPageLabel?: undefined;
|
|
96
|
+
paginationLabel?: undefined;
|
|
97
|
+
paginationStatus?: undefined;
|
|
98
|
+
totalRowCount?: undefined;
|
|
99
|
+
manualPagination?: undefined;
|
|
100
|
+
}
|
|
101
|
+
| {
|
|
102
|
+
/** Controlled zero-based page state. */
|
|
103
|
+
page: PageSpec;
|
|
104
|
+
onPageChange: (page: PageSpec) => void;
|
|
105
|
+
/** Accessible names for the icon-only pagination controls. */
|
|
106
|
+
previousPageLabel: string;
|
|
107
|
+
nextPageLabel: string;
|
|
108
|
+
paginationLabel?: string;
|
|
109
|
+
paginationStatus?: (info: DataTablePaginationStatusInfo) => React.ReactNode;
|
|
110
|
+
/** Server-side total; required for accurate status in manual mode. */
|
|
111
|
+
totalRowCount?: number;
|
|
112
|
+
/** Server owns row slicing; `rows` holds exactly the current page. */
|
|
113
|
+
manualPagination?: boolean;
|
|
114
|
+
}
|
|
115
|
+
) &
|
|
116
|
+
(
|
|
117
|
+
| {
|
|
118
|
+
selection?: undefined;
|
|
119
|
+
defaultSelection?: undefined;
|
|
120
|
+
onSelectionChange?: undefined;
|
|
121
|
+
selectionMode?: undefined;
|
|
122
|
+
selectAllLabel?: undefined;
|
|
123
|
+
rowSelectionLabel?: undefined;
|
|
124
|
+
selectedCountLabel?: undefined;
|
|
125
|
+
renderSelectionActions?: undefined;
|
|
126
|
+
}
|
|
127
|
+
| {
|
|
128
|
+
/** Controlled selected row ids. */
|
|
129
|
+
selection?: readonly RowId[];
|
|
130
|
+
/** Initial selection for uncontrolled usage. */
|
|
131
|
+
defaultSelection?: readonly RowId[];
|
|
132
|
+
onSelectionChange?: (ids: RowId[]) => void;
|
|
133
|
+
selectionMode?: SelectionMode;
|
|
134
|
+
/** Accessible name for the select-all header control. */
|
|
135
|
+
selectAllLabel: string;
|
|
136
|
+
/** Accessible name factory for row selection controls. */
|
|
137
|
+
rowSelectionLabel?: (row: Row) => string;
|
|
138
|
+
selectedCountLabel?: (count: number) => React.ReactNode;
|
|
139
|
+
renderSelectionActions?: (info: DataTableSelectionInfo) => React.ReactNode;
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
export type DataTableProps<Row extends { id: string }> = {
|
|
144
|
+
columns: DataColumn<Row>[];
|
|
145
|
+
rows: Row[];
|
|
146
|
+
caption: string;
|
|
147
|
+
empty: React.ReactNode;
|
|
148
|
+
className?: string;
|
|
149
|
+
loading?: boolean;
|
|
150
|
+
/** Skeleton row count while `loading` is true. Default: 5. */
|
|
151
|
+
loadingRows?: number;
|
|
152
|
+
/** Visual rhythm: `regular` (default) or `compact` for dense workspaces. */
|
|
153
|
+
density?: 'compact' | 'regular';
|
|
154
|
+
} & DataTableFeatureProps<Row>;
|
|
155
|
+
|
|
156
|
+
/** Pagination state that keeps every row visible when paging is disabled. */
|
|
157
|
+
const ALL_ROWS_PAGINATION = { pageIndex: 0, pageSize: Number.MAX_SAFE_INTEGER };
|
|
158
|
+
|
|
159
|
+
export function DataTable<Row extends { id: string }>({
|
|
160
|
+
columns,
|
|
161
|
+
rows,
|
|
162
|
+
caption,
|
|
163
|
+
empty,
|
|
164
|
+
className,
|
|
165
|
+
loading = false,
|
|
166
|
+
loadingRows = 5,
|
|
167
|
+
density = 'regular',
|
|
168
|
+
sorting,
|
|
169
|
+
defaultSorting,
|
|
170
|
+
onSortingChange,
|
|
171
|
+
manualSorting,
|
|
172
|
+
filters,
|
|
173
|
+
defaultFilters,
|
|
174
|
+
onFiltersChange,
|
|
175
|
+
manualFiltering,
|
|
176
|
+
page,
|
|
177
|
+
onPageChange,
|
|
178
|
+
previousPageLabel,
|
|
179
|
+
nextPageLabel,
|
|
180
|
+
paginationLabel,
|
|
181
|
+
paginationStatus,
|
|
182
|
+
totalRowCount,
|
|
183
|
+
manualPagination,
|
|
184
|
+
selection,
|
|
185
|
+
defaultSelection,
|
|
186
|
+
onSelectionChange,
|
|
187
|
+
selectionMode = 'multiple',
|
|
188
|
+
selectAllLabel,
|
|
189
|
+
rowSelectionLabel,
|
|
190
|
+
selectedCountLabel,
|
|
191
|
+
renderSelectionActions,
|
|
192
|
+
}: DataTableProps<Row>) {
|
|
193
|
+
const captionId = useId();
|
|
194
|
+
const selectionEnabled = selectAllLabel !== undefined;
|
|
195
|
+
const paginationEnabled = page !== undefined;
|
|
196
|
+
|
|
197
|
+
const [internalSorting, setInternalSorting] = useState<SortingState>(() =>
|
|
198
|
+
toTanStackSorting(defaultSorting ?? []),
|
|
199
|
+
);
|
|
200
|
+
const [internalFilters, setInternalFilters] = useState<ColumnFiltersState>(() =>
|
|
201
|
+
toTanStackFilters(defaultFilters ?? []),
|
|
202
|
+
);
|
|
203
|
+
const [internalSelection, setInternalSelection] = useState<RowSelectionState>(() =>
|
|
204
|
+
toTanStackSelection(defaultSelection ?? []),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const controlledSorting = useMemo(
|
|
208
|
+
() => (sorting !== undefined ? toTanStackSorting(sorting) : undefined),
|
|
209
|
+
[sorting],
|
|
210
|
+
);
|
|
211
|
+
const controlledFilters = useMemo(
|
|
212
|
+
() => (filters !== undefined ? toTanStackFilters(filters) : undefined),
|
|
213
|
+
[filters],
|
|
214
|
+
);
|
|
215
|
+
const controlledSelection = useMemo(
|
|
216
|
+
() => (selection !== undefined ? toTanStackSelection(selection) : undefined),
|
|
217
|
+
[selection],
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const sortingState = controlledSorting ?? internalSorting;
|
|
221
|
+
const filtersState = controlledFilters ?? internalFilters;
|
|
222
|
+
const selectionState = controlledSelection ?? internalSelection;
|
|
223
|
+
const paginationState = useMemo(
|
|
224
|
+
() =>
|
|
225
|
+
page !== undefined ? { pageIndex: page.index, pageSize: page.size } : ALL_ROWS_PAGINATION,
|
|
226
|
+
[page],
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const tanstackColumns = useMemo(() => toTanStackColumns(columns), [columns]);
|
|
230
|
+
|
|
231
|
+
const handleSortingChange: OnChangeFn<SortingState> = (updater) => {
|
|
232
|
+
const next = functionalUpdate(updater, sortingState);
|
|
233
|
+
setInternalSorting(next);
|
|
234
|
+
onSortingChange?.(fromTanStackSorting(next));
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const handleFiltersChange: OnChangeFn<ColumnFiltersState> = (updater) => {
|
|
238
|
+
const next = functionalUpdate(updater, filtersState);
|
|
239
|
+
setInternalFilters(next);
|
|
240
|
+
onFiltersChange?.(fromTanStackFilters(next));
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const handleSelectionChange: OnChangeFn<RowSelectionState> = (updater) => {
|
|
244
|
+
const next = functionalUpdate(updater, selectionState);
|
|
245
|
+
setInternalSelection(next);
|
|
246
|
+
onSelectionChange?.(fromTanStackSelection(next));
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const table = useTable({
|
|
250
|
+
features: noeTableFeatures,
|
|
251
|
+
columns: tanstackColumns,
|
|
252
|
+
data: rows,
|
|
253
|
+
getRowId: (row: Row) => row.id,
|
|
254
|
+
state: {
|
|
255
|
+
sorting: sortingState,
|
|
256
|
+
columnFilters: filtersState,
|
|
257
|
+
rowSelection: selectionState,
|
|
258
|
+
pagination: paginationState,
|
|
259
|
+
},
|
|
260
|
+
onSortingChange: handleSortingChange,
|
|
261
|
+
onColumnFiltersChange: handleFiltersChange,
|
|
262
|
+
onRowSelectionChange: handleSelectionChange,
|
|
263
|
+
enableMultiSort: false,
|
|
264
|
+
autoResetPageIndex: false,
|
|
265
|
+
...(manualSorting !== undefined ? { manualSorting } : {}),
|
|
266
|
+
...(manualFiltering !== undefined ? { manualFiltering } : {}),
|
|
267
|
+
...(manualPagination !== undefined ? { manualPagination } : {}),
|
|
268
|
+
...(totalRowCount !== undefined ? { rowCount: totalRowCount } : {}),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const modelRows = table.getRowModel().rows;
|
|
272
|
+
const selectedIds =
|
|
273
|
+
selection !== undefined ? [...selection] : fromTanStackSelection(selectionState);
|
|
274
|
+
|
|
275
|
+
const toggleRowSelection = (rowId: RowId) => {
|
|
276
|
+
const isSelected = selectedIds.includes(rowId);
|
|
277
|
+
if (selectionMode === 'single') {
|
|
278
|
+
handleSelectionChange(isSelected ? {} : toTanStackSelection([rowId]));
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
handleSelectionChange(
|
|
282
|
+
toTanStackSelection(
|
|
283
|
+
isSelected ? selectedIds.filter((id) => id !== rowId) : [...selectedIds, rowId],
|
|
284
|
+
),
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const allRowsSelected = table.getIsAllRowsSelected();
|
|
289
|
+
const someRowsSelected = table.getIsSomeRowsSelected();
|
|
290
|
+
|
|
291
|
+
const pageSize = page?.size ?? ALL_ROWS_PAGINATION.pageSize;
|
|
292
|
+
const pageIndex = page?.index ?? 0;
|
|
293
|
+
const totalRows =
|
|
294
|
+
manualPagination === true
|
|
295
|
+
? (totalRowCount ?? pageIndex * pageSize + modelRows.length)
|
|
296
|
+
: table.getSortedRowModel().rows.length;
|
|
297
|
+
const canPreviousPage = paginationEnabled && pageIndex > 0;
|
|
298
|
+
const canNextPage = paginationEnabled
|
|
299
|
+
? manualPagination === true
|
|
300
|
+
? totalRowCount !== undefined
|
|
301
|
+
? (pageIndex + 1) * pageSize < totalRowCount
|
|
302
|
+
: modelRows.length === pageSize
|
|
303
|
+
: pageIndex + 1 < table.getPageCount()
|
|
304
|
+
: false;
|
|
305
|
+
const paginationInfo: DataTablePaginationStatusInfo | undefined = paginationEnabled
|
|
306
|
+
? {
|
|
307
|
+
startIndex: totalRows === 0 ? 0 : pageIndex * pageSize + 1,
|
|
308
|
+
endIndex: Math.min(totalRows, (pageIndex + 1) * pageSize),
|
|
309
|
+
total: totalRows,
|
|
310
|
+
}
|
|
311
|
+
: undefined;
|
|
312
|
+
|
|
313
|
+
const cellPadding = density === 'compact' ? 'px-3 py-1.5' : 'px-4 py-2.5';
|
|
314
|
+
|
|
315
|
+
if (!loading && modelRows.length === 0) {
|
|
316
|
+
return (
|
|
317
|
+
<div
|
|
318
|
+
className={cn(
|
|
319
|
+
'flex min-h-32 items-center justify-center rounded-lg border border-dashed border-border bg-surface-sunken/30 p-6 text-center text-sm text-muted-foreground',
|
|
320
|
+
className,
|
|
321
|
+
)}
|
|
322
|
+
data-slot="data-table-empty"
|
|
323
|
+
>
|
|
324
|
+
{empty}
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const showSelectionBar =
|
|
330
|
+
selectionEnabled &&
|
|
331
|
+
selectedIds.length > 0 &&
|
|
332
|
+
(selectedCountLabel !== undefined || renderSelectionActions !== undefined);
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div className={cn('flex flex-col gap-3', className)} data-slot="data-table-root">
|
|
336
|
+
{showSelectionBar ? (
|
|
337
|
+
<div
|
|
338
|
+
className="flex items-center gap-3 rounded-md border border-border bg-surface-sunken/40 px-4 py-2.5 text-sm"
|
|
339
|
+
data-slot="data-table-selection-bar"
|
|
340
|
+
>
|
|
341
|
+
{selectedCountLabel ? (
|
|
342
|
+
<span className="tabular-nums text-muted-foreground">
|
|
343
|
+
{selectedCountLabel(selectedIds.length)}
|
|
344
|
+
</span>
|
|
345
|
+
) : null}
|
|
346
|
+
{renderSelectionActions ? (
|
|
347
|
+
<div className="ms-auto flex items-center gap-2">
|
|
348
|
+
{renderSelectionActions({ selectedIds })}
|
|
349
|
+
</div>
|
|
350
|
+
) : null}
|
|
351
|
+
</div>
|
|
352
|
+
) : null}
|
|
353
|
+
{/* WCAG 2.1: keyboard-accessible scrollable container for overflow tables. */}
|
|
354
|
+
<section
|
|
355
|
+
aria-labelledby={captionId}
|
|
356
|
+
className="overflow-x-auto rounded-lg border border-border"
|
|
357
|
+
data-slot="data-table-scroll"
|
|
358
|
+
// biome-ignore lint/a11y/noNoninteractiveTabindex: scrollable region focus pattern
|
|
359
|
+
tabIndex={0}
|
|
360
|
+
>
|
|
361
|
+
<table
|
|
362
|
+
className="w-full min-w-full border-collapse text-sm"
|
|
363
|
+
data-density={density}
|
|
364
|
+
data-slot="data-table"
|
|
365
|
+
>
|
|
366
|
+
<caption className="sr-only" id={captionId}>
|
|
367
|
+
{caption}
|
|
368
|
+
</caption>
|
|
369
|
+
<thead>
|
|
370
|
+
<tr className="border-b border-border bg-surface-sunken/60">
|
|
371
|
+
{selectionEnabled && selectionMode === 'multiple' ? (
|
|
372
|
+
<th className={cn('w-10', cellPadding)} data-slot="data-table-select-header">
|
|
373
|
+
<Checkbox
|
|
374
|
+
aria-label={selectAllLabel}
|
|
375
|
+
checked={allRowsSelected}
|
|
376
|
+
onChange={() => table.toggleAllRowsSelected(!allRowsSelected)}
|
|
377
|
+
ref={(element) => {
|
|
378
|
+
if (element) element.indeterminate = someRowsSelected && !allRowsSelected;
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
</th>
|
|
382
|
+
) : null}
|
|
383
|
+
{columns.map((column) => {
|
|
384
|
+
const tanstackColumn = table.getColumn(column.key);
|
|
385
|
+
const canSort = tanstackColumn?.getCanSort() ?? false;
|
|
386
|
+
const sorted = tanstackColumn?.getIsSorted() ?? false;
|
|
387
|
+
return (
|
|
388
|
+
<th
|
|
389
|
+
aria-sort={
|
|
390
|
+
sorted === 'asc'
|
|
391
|
+
? 'ascending'
|
|
392
|
+
: sorted === 'desc'
|
|
393
|
+
? 'descending'
|
|
394
|
+
: canSort
|
|
395
|
+
? 'none'
|
|
396
|
+
: undefined
|
|
397
|
+
}
|
|
398
|
+
className={cn(
|
|
399
|
+
'whitespace-nowrap text-xs font-medium text-muted-foreground',
|
|
400
|
+
cellPadding,
|
|
401
|
+
column.align === 'end' ? 'text-end' : 'text-start',
|
|
402
|
+
)}
|
|
403
|
+
data-align={column.align ?? 'start'}
|
|
404
|
+
key={column.key}
|
|
405
|
+
scope="col"
|
|
406
|
+
>
|
|
407
|
+
{canSort && tanstackColumn ? (
|
|
408
|
+
<Button
|
|
409
|
+
className="-ms-2 text-muted-foreground hover:text-foreground"
|
|
410
|
+
data-slot="data-table-sort-button"
|
|
411
|
+
onClick={(event) => tanstackColumn.getToggleSortingHandler()?.(event)}
|
|
412
|
+
size="sm"
|
|
413
|
+
variant="ghost"
|
|
414
|
+
>
|
|
415
|
+
{column.header}
|
|
416
|
+
{sorted === 'desc' ? (
|
|
417
|
+
<ArrowDown2 className="size-3.5" />
|
|
418
|
+
) : (
|
|
419
|
+
<ArrowUp2 className={cn('size-3.5', sorted === false && 'opacity-40')} />
|
|
420
|
+
)}
|
|
421
|
+
</Button>
|
|
422
|
+
) : (
|
|
423
|
+
column.header
|
|
424
|
+
)}
|
|
425
|
+
</th>
|
|
426
|
+
);
|
|
427
|
+
})}
|
|
428
|
+
</tr>
|
|
429
|
+
</thead>
|
|
430
|
+
<tbody>
|
|
431
|
+
{loading
|
|
432
|
+
? Array.from(
|
|
433
|
+
{ length: loadingRows },
|
|
434
|
+
(_, index) => `data-table-loading-${index}`,
|
|
435
|
+
).map((rowKey) => (
|
|
436
|
+
<tr
|
|
437
|
+
className="border-b border-border last:border-b-0"
|
|
438
|
+
data-slot="data-table-loading-row"
|
|
439
|
+
key={rowKey}
|
|
440
|
+
>
|
|
441
|
+
{selectionEnabled ? (
|
|
442
|
+
<td className={cellPadding}>
|
|
443
|
+
<Skeleton className="size-4" />
|
|
444
|
+
</td>
|
|
445
|
+
) : null}
|
|
446
|
+
{columns.map((column) => (
|
|
447
|
+
<td className={cellPadding} key={column.key}>
|
|
448
|
+
<Skeleton className="h-4 w-full max-w-32" />
|
|
449
|
+
</td>
|
|
450
|
+
))}
|
|
451
|
+
</tr>
|
|
452
|
+
))
|
|
453
|
+
: modelRows.map((tanstackRow) => {
|
|
454
|
+
const row = tanstackRow.original;
|
|
455
|
+
const isSelected = tanstackRow.getIsSelected();
|
|
456
|
+
return (
|
|
457
|
+
<tr
|
|
458
|
+
className="border-b border-border last:border-b-0 hover:bg-muted/50"
|
|
459
|
+
data-selected={isSelected || undefined}
|
|
460
|
+
key={tanstackRow.id}
|
|
461
|
+
>
|
|
462
|
+
{selectionEnabled ? (
|
|
463
|
+
<td className={cellPadding} data-slot="data-table-select-cell">
|
|
464
|
+
<Checkbox
|
|
465
|
+
aria-label={rowSelectionLabel?.(row)}
|
|
466
|
+
checked={isSelected}
|
|
467
|
+
onChange={() => toggleRowSelection(tanstackRow.id)}
|
|
468
|
+
/>
|
|
469
|
+
</td>
|
|
470
|
+
) : null}
|
|
471
|
+
{columns.map((column) => {
|
|
472
|
+
const content = column.render
|
|
473
|
+
? column.render(row)
|
|
474
|
+
: String(row[column.key] ?? '');
|
|
475
|
+
const value = column.technical ? (
|
|
476
|
+
<TechnicalValue>{content}</TechnicalValue>
|
|
477
|
+
) : (
|
|
478
|
+
content
|
|
479
|
+
);
|
|
480
|
+
return (
|
|
481
|
+
<td
|
|
482
|
+
className={cn(
|
|
483
|
+
'whitespace-nowrap text-foreground',
|
|
484
|
+
cellPadding,
|
|
485
|
+
column.align === 'end' ? 'text-end' : 'text-start',
|
|
486
|
+
)}
|
|
487
|
+
data-align={column.align ?? 'start'}
|
|
488
|
+
key={column.key}
|
|
489
|
+
>
|
|
490
|
+
{value}
|
|
491
|
+
</td>
|
|
492
|
+
);
|
|
493
|
+
})}
|
|
494
|
+
</tr>
|
|
495
|
+
);
|
|
496
|
+
})}
|
|
497
|
+
</tbody>
|
|
498
|
+
</table>
|
|
499
|
+
</section>
|
|
500
|
+
{paginationEnabled && paginationInfo ? (
|
|
501
|
+
<Pagination
|
|
502
|
+
canNext={canNextPage}
|
|
503
|
+
canPrevious={canPreviousPage}
|
|
504
|
+
label={paginationLabel ?? caption}
|
|
505
|
+
nextLabel={nextPageLabel}
|
|
506
|
+
onNext={
|
|
507
|
+
canNextPage ? () => onPageChange({ index: pageIndex + 1, size: pageSize }) : undefined
|
|
508
|
+
}
|
|
509
|
+
onPrevious={
|
|
510
|
+
canPreviousPage
|
|
511
|
+
? () => onPageChange({ index: pageIndex - 1, size: pageSize })
|
|
512
|
+
: undefined
|
|
513
|
+
}
|
|
514
|
+
previousLabel={previousPageLabel}
|
|
515
|
+
status={
|
|
516
|
+
paginationStatus
|
|
517
|
+
? paginationStatus(paginationInfo)
|
|
518
|
+
: `${paginationInfo.startIndex}–${paginationInfo.endIndex} / ${paginationInfo.total}`
|
|
519
|
+
}
|
|
520
|
+
/>
|
|
521
|
+
) : null}
|
|
522
|
+
</div>
|
|
523
|
+
);
|
|
524
|
+
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "./theme.css";
|
|
3
3
|
@import "./base.css";
|
|
4
|
+
|
|
5
|
+
/* Tailwind v4 resolves explicit sources relative to this stylesheet. Storybook and
|
|
6
|
+
product apps consume workspace source outside their own package roots. */
|
|
7
|
+
@source "../../../packages";
|
|
8
|
+
@source "../../../apps";
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
--noe-primitive-color-ink-500: #565c58;
|
|
14
14
|
--noe-primitive-color-ink-600: #302f2e;
|
|
15
15
|
--noe-primitive-color-ink-700: #252423;
|
|
16
|
-
--noe-primitive-color-ink-800: #
|
|
17
|
-
--noe-primitive-color-ink-850: #
|
|
16
|
+
--noe-primitive-color-ink-800: #2a2a2a;
|
|
17
|
+
--noe-primitive-color-ink-850: #202020;
|
|
18
18
|
--noe-primitive-color-ink-900: #171918;
|
|
19
|
-
--noe-primitive-color-ink-950: #
|
|
20
|
-
--noe-primitive-color-ink-1000: #
|
|
19
|
+
--noe-primitive-color-ink-950: #161616;
|
|
20
|
+
--noe-primitive-color-ink-1000: #111111;
|
|
21
21
|
--noe-primitive-color-mint-100: #98e8cf;
|
|
22
22
|
--noe-primitive-color-mint-200: #abefd9;
|
|
23
23
|
--noe-primitive-color-mint-300: #1dbf98;
|
|
@@ -140,6 +140,8 @@
|
|
|
140
140
|
--noe-color-surface: var(--noe-primitive-color-paper-50);
|
|
141
141
|
--noe-color-surface-raised: var(--noe-primitive-color-paper-0);
|
|
142
142
|
--noe-color-surface-sunken: var(--noe-primitive-color-paper-200);
|
|
143
|
+
--noe-color-sidebar: var(--noe-primitive-color-paper-200);
|
|
144
|
+
--noe-color-sidebar-active: var(--noe-primitive-color-paper-0);
|
|
143
145
|
--noe-color-foreground: var(--noe-primitive-color-ink-900);
|
|
144
146
|
--noe-color-muted: var(--noe-primitive-color-paper-200);
|
|
145
147
|
--noe-color-muted-foreground: var(--noe-primitive-color-ink-500);
|
|
@@ -170,6 +172,8 @@
|
|
|
170
172
|
--noe-color-surface: var(--noe-primitive-color-ink-850);
|
|
171
173
|
--noe-color-surface-raised: var(--noe-primitive-color-ink-800);
|
|
172
174
|
--noe-color-surface-sunken: var(--noe-primitive-color-ink-1000);
|
|
175
|
+
--noe-color-sidebar: var(--noe-primitive-color-ink-850);
|
|
176
|
+
--noe-color-sidebar-active: var(--noe-primitive-color-ink-800);
|
|
173
177
|
--noe-color-foreground: var(--noe-primitive-color-paper-250);
|
|
174
178
|
--noe-color-muted: var(--noe-primitive-color-ink-800);
|
|
175
179
|
--noe-color-muted-foreground: var(--noe-primitive-color-ink-400);
|
|
@@ -201,6 +205,8 @@
|
|
|
201
205
|
--noe-color-surface: var(--noe-primitive-color-ink-850);
|
|
202
206
|
--noe-color-surface-raised: var(--noe-primitive-color-ink-800);
|
|
203
207
|
--noe-color-surface-sunken: var(--noe-primitive-color-ink-1000);
|
|
208
|
+
--noe-color-sidebar: var(--noe-primitive-color-ink-850);
|
|
209
|
+
--noe-color-sidebar-active: var(--noe-primitive-color-ink-800);
|
|
204
210
|
--noe-color-foreground: var(--noe-primitive-color-paper-250);
|
|
205
211
|
--noe-color-muted: var(--noe-primitive-color-ink-800);
|
|
206
212
|
--noe-color-muted-foreground: var(--noe-primitive-color-ink-400);
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"500": "#565c58",
|
|
16
16
|
"600": "#302f2e",
|
|
17
17
|
"700": "#252423",
|
|
18
|
-
"800": "#
|
|
19
|
-
"850": "#
|
|
18
|
+
"800": "#2a2a2a",
|
|
19
|
+
"850": "#202020",
|
|
20
20
|
"900": "#171918",
|
|
21
|
-
"950": "#
|
|
22
|
-
"1000": "#
|
|
21
|
+
"950": "#161616",
|
|
22
|
+
"1000": "#111111"
|
|
23
23
|
},
|
|
24
24
|
"mint": {
|
|
25
25
|
"100": "#98e8cf",
|
|
@@ -106,6 +106,8 @@
|
|
|
106
106
|
"surface": "{primitive.color.paper.50}",
|
|
107
107
|
"surfaceRaised": "{primitive.color.paper.0}",
|
|
108
108
|
"surfaceSunken": "{primitive.color.paper.200}",
|
|
109
|
+
"sidebar": "{primitive.color.paper.200}",
|
|
110
|
+
"sidebarActive": "{primitive.color.paper.0}",
|
|
109
111
|
"foreground": "{primitive.color.ink.900}",
|
|
110
112
|
"muted": "{primitive.color.paper.200}",
|
|
111
113
|
"mutedForeground": "{primitive.color.ink.500}",
|
|
@@ -131,6 +133,8 @@
|
|
|
131
133
|
"surface": "{primitive.color.ink.850}",
|
|
132
134
|
"surfaceRaised": "{primitive.color.ink.800}",
|
|
133
135
|
"surfaceSunken": "{primitive.color.ink.1000}",
|
|
136
|
+
"sidebar": "{primitive.color.ink.850}",
|
|
137
|
+
"sidebarActive": "{primitive.color.ink.800}",
|
|
134
138
|
"foreground": "{primitive.color.paper.250}",
|
|
135
139
|
"muted": "{primitive.color.ink.800}",
|
|
136
140
|
"mutedForeground": "{primitive.color.ink.400}",
|
|
@@ -16,11 +16,11 @@ export const tokens = {
|
|
|
16
16
|
"500": "#565c58",
|
|
17
17
|
"600": "#302f2e",
|
|
18
18
|
"700": "#252423",
|
|
19
|
-
"800": "#
|
|
20
|
-
"850": "#
|
|
19
|
+
"800": "#2a2a2a",
|
|
20
|
+
"850": "#202020",
|
|
21
21
|
"900": "#171918",
|
|
22
|
-
"950": "#
|
|
23
|
-
"1000": "#
|
|
22
|
+
"950": "#161616",
|
|
23
|
+
"1000": "#111111"
|
|
24
24
|
},
|
|
25
25
|
"mint": {
|
|
26
26
|
"100": "#98e8cf",
|
|
@@ -107,6 +107,8 @@ export const tokens = {
|
|
|
107
107
|
"surface": "{primitive.color.paper.50}",
|
|
108
108
|
"surfaceRaised": "{primitive.color.paper.0}",
|
|
109
109
|
"surfaceSunken": "{primitive.color.paper.200}",
|
|
110
|
+
"sidebar": "{primitive.color.paper.200}",
|
|
111
|
+
"sidebarActive": "{primitive.color.paper.0}",
|
|
110
112
|
"foreground": "{primitive.color.ink.900}",
|
|
111
113
|
"muted": "{primitive.color.paper.200}",
|
|
112
114
|
"mutedForeground": "{primitive.color.ink.500}",
|
|
@@ -132,6 +134,8 @@ export const tokens = {
|
|
|
132
134
|
"surface": "{primitive.color.ink.850}",
|
|
133
135
|
"surfaceRaised": "{primitive.color.ink.800}",
|
|
134
136
|
"surfaceSunken": "{primitive.color.ink.1000}",
|
|
137
|
+
"sidebar": "{primitive.color.ink.850}",
|
|
138
|
+
"sidebarActive": "{primitive.color.ink.800}",
|
|
135
139
|
"foreground": "{primitive.color.paper.250}",
|
|
136
140
|
"muted": "{primitive.color.ink.800}",
|
|
137
141
|
"mutedForeground": "{primitive.color.ink.400}",
|
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
"react": "^19.2.0"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
|
-
".": "./src/index.tsx"
|
|
10
|
+
".": "./src/index.tsx",
|
|
11
|
+
"./nocfo-logo": "./src/nocfo-logo.tsx"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"iconsax-react": "0.0.8"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"vitest": "4.1.11",
|
|
18
|
+
"vitest-browser-react": "2.2.0"
|
|
14
19
|
}
|
|
15
20
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="698" height="115" viewBox="0 0 698 115" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<title>NOCFO</title>
|
|
3
|
+
<path d="M636.475 42.5066C670.199 41.9183 699.188 36.762 697.24 78.3513C696.422 95.8057 694.599 107.431 674.435 113.115C655.219 113.967 618.693 118.485 614.59 94.2956C611.226 74.4705 609.538 47.9355 636.475 42.5066ZM688.198 76.031C690.086 45.5042 661.671 51.0544 637.899 51.5583C632.981 52.6653 629.35 53.7832 626.398 58.1775C620.114 67.5366 618.965 95.6618 630.797 101.452C639.35 105.639 662.895 104.297 672.995 103.76C688.139 98.6936 687.394 89.0325 688.198 76.031Z" fill="white"/>
|
|
4
|
+
<path d="M467.517 43.3473C482.372 43.1323 513.647 39.1155 520.943 54.2547C522.502 57.4924 524.8 62.6848 523.304 66.1513C520.707 67.2164 520.514 67.0872 517.584 66.6572C510.654 62.2262 516.468 52.7675 499.371 52.2646C464.447 51.3928 449.226 48.4785 452.936 86.8645C454.824 106.392 466.853 104.769 485.656 104.5C526.417 104.971 504.703 92.1634 520.242 89.2117C523.238 90.3793 522.264 89.5509 523.759 92.4987C523.548 113.208 504.309 114.086 486.432 113.861C470.279 113.659 447.74 114.956 444.598 94.261C441.582 74.4048 439.949 48.4154 467.517 43.3473Z" fill="white"/>
|
|
5
|
+
<path d="M554.993 43.2972C565.284 43.1654 601.126 41.0629 606.755 45.6961C607.862 48.7493 607.864 47.4537 606.659 50.5743C597.297 57.7393 541.771 40.7556 544.87 67.1734C546.493 81.0099 594.502 67.9992 602.929 74.1125C602.981 77.0923 603.463 79.5864 601.237 81.6661C587.929 82.9637 559.376 82.0993 544.925 82.017C544.823 90.3091 546.719 105.331 543.654 112.144C540.944 114.363 542.339 113.847 538.229 113.803L536.945 112.23C535.403 102.746 534.598 62.4579 539.522 52.2873C541.947 47.2793 549.2 44.6295 554.993 43.2972Z" fill="white"/>
|
|
6
|
+
<path d="M56.7057 0.220818C59.4812 -0.0783868 66.655 0.00764426 69.5347 0.0238175C78.3794 0.0954738 87.2231 0.25451 96.0646 0.501376C100.5 0.589655 105.045 0.395802 109.484 0.256308C132.52 -0.467892 156.409 -0.133871 173.733 17.4305C179.379 23.0998 183.544 30.0721 185.86 37.7326C187.257 42.4387 187.682 48.1555 188.036 53.0969C189.467 73.0742 188.685 93.4045 188.559 113.42C181.836 113.656 173.963 113.499 167.156 113.523L167.227 75.7448C167.255 68.6649 167.379 61.7158 167.124 54.636C166.395 34.3692 151.889 21.6885 132.236 19.9335C117.122 18.9447 100.969 19.8066 85.8082 19.9052C66.7354 20.0292 45.5038 16.4282 30.7644 31.6442C24.6706 38.0679 22.4129 45.2014 21.9355 53.7631C20.8234 73.7101 21.3197 93.5171 21.552 113.465C15.0667 113.737 6.74555 113.517 0.174689 113.502C0.0536924 100.976 -0.00444898 88.4497 0.000265171 75.9236C0.023387 65.4894 -0.0850384 49.2196 2.53468 39.4846C4.44796 32.5183 7.8839 26.064 12.5947 20.589C24.2656 7.00099 39.1747 1.54859 56.7057 0.220818Z" fill="white"/>
|
|
7
|
+
<path d="M319.568 0.193154C332.548 0.161706 345.543 -0.281296 358.514 0.29081C365.335 0.582372 372.427 0.7861 379.045 2.5828C396.211 7.24404 410.499 20.151 416.147 37.1297C418.006 42.7191 418.727 48.4357 418.961 54.3387C420.359 89.7317 395.124 112.994 360.341 113.954C346.371 114.201 332.168 113.892 318.17 113.936L293.926 114.03C272.33 113.981 254.719 113.713 238.029 98.2703C216.174 78.0453 215.513 41.0945 235.138 19.2088C247.145 5.81961 262.554 0.908067 279.985 0.192178L281.043 0.157021C293.831 -0.229988 306.771 0.224377 319.568 0.193154ZM329.095 19.2166C313.953 19.1948 298.232 18.934 283.14 19.2752C283.028 19.2772 282.918 19.2805 282.808 19.2859C271.122 19.6424 260.264 22.6589 252.265 31.5525C245.275 39.3273 242.217 48.2684 242.538 58.5779C242.751 68.5089 246.945 77.937 254.18 84.74C267.898 97.6367 287.634 94.6477 304.868 94.7137C322.102 94.7799 339.509 95.1774 356.733 94.7576C386.396 94.2072 404.261 71.1977 395.264 42.6101C391.991 32.2128 380.566 23.3761 370.269 20.8816C362.924 19.1026 354.048 19.1626 346.523 19.1844L329.095 19.2166Z" fill="white"/>
|
|
8
|
+
</svg>
|