neba 1.3.0 → 1.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 +4 -6
- package/dist/components/data-table/DataTable.d.ts +323 -0
- package/dist/components/data-table/DataTable.js +1 -0
- package/dist/components/data-table/index.d.ts +2 -0
- package/dist/components/data-table/index.js +1 -0
- package/dist/components/empty/Empty.d.ts +69 -0
- package/dist/components/empty/Empty.js +1 -0
- package/dist/components/empty/index.d.ts +2 -0
- package/dist/components/empty/index.js +1 -0
- package/dist/components/mockup/Mockup.d.ts +151 -0
- package/dist/components/mockup/Mockup.js +1 -0
- package/dist/components/mockup/index.d.ts +2 -0
- package/dist/components/mockup/index.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/internal/data-table.d.ts +170 -0
- package/dist/internal/data-table.js +1 -0
- package/dist/internal/i18n.d.ts +51 -0
- package/dist/internal/i18n.js +1 -1
- package/dist/internal/mockup.d.ts +193 -0
- package/dist/internal/mockup.js +1 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind.css +26 -11
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -38,8 +38,6 @@ Everything is documented at **[neba.cdget.com](https://neba.cdget.com)**, where
|
|
|
38
38
|
| [**Color**](https://neba.cdget.com/design/color) | The token families, and how to theme them. |
|
|
39
39
|
| [**Changelog**](https://neba.cdget.com/changelog) | What changed in each release, and the setup changes worth acting on. |
|
|
40
40
|
|
|
41
|
-
Also available in Korean / 한국어 문서: **[neba.cdget.com/ko/](https://neba.cdget.com/ko/)**
|
|
42
|
-
|
|
43
41
|
## Installation
|
|
44
42
|
|
|
45
43
|
```bash
|
|
@@ -113,17 +111,17 @@ Placement props are logical, not physical — `start`/`end` rather than `left`/`
|
|
|
113
111
|
|
|
114
112
|
**Inputs** — Button, IconButton, ButtonGroup, SegmentedButton, TextField, NumberField, OtpField, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, Menu (with submenus, checkbox and radio items), ContextMenu, FilePicker, Pagination, ColorPicker, DatePicker, TimePicker, DateTimePicker, DateRangePicker
|
|
115
113
|
|
|
116
|
-
**Surfaces** — Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill, Spoiler, ChatBubble, Drawer, Popover
|
|
114
|
+
**Surfaces** — Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill, Spoiler, ChatBubble, Drawer, Popover, Mockup
|
|
117
115
|
|
|
118
|
-
**Display** — Typography, TextLink, Blockquote, Highlight, Divider, Chip, Badge, Avatar, Icon, Shortcut, Statistic, List, Table, Timeline, Breadcrumb, TreeView
|
|
116
|
+
**Display** — Typography, TextLink, Blockquote, Highlight, Divider, Chip, Badge, Avatar, Icon, Shortcut, Statistic, List, Table, DataTable, Timeline, Breadcrumb, TreeView
|
|
119
117
|
|
|
120
|
-
**Feedback** — Alert, Dialog, Toast, Tooltip, Overlay, Skeleton, ProgressLinear, ProgressCircular, ProgressBox
|
|
118
|
+
**Feedback** — Alert, Dialog, Toast, Tooltip, Overlay, Skeleton, Empty, ProgressLinear, ProgressCircular, ProgressBox
|
|
121
119
|
|
|
122
120
|
**Layout** — Container, Grid (with GridContainer), Panes, AspectRatio
|
|
123
121
|
|
|
124
122
|
**Transitions** — AnimateFade, AnimateGrow, AnimateZoom, AnimateSlide, AnimateRotate, AnimateBlink, AnimateAppear, AnimateTyping, AnimateLighting, AnimateMarquee, AnimateHeadline
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
**Recently added** — DataTable, Empty, Mockup, the eleven `Animate*` wrappers, ColorPicker, Drawer, Popover, Skeleton, AspectRatio
|
|
127
125
|
|
|
128
126
|
Each one has its own page — live previews, every prop, and the variations worth seeing — under [**All components**](https://neba.cdget.com/components/).
|
|
129
127
|
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaAlign, NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/** Which way a column runs when it is sorted. */
|
|
4
|
+
export type DataTableSortDirection = 'asc' | 'desc';
|
|
5
|
+
/** One key of the sort, and its direction. A sort is a list of these. */
|
|
6
|
+
export interface DataTableSort {
|
|
7
|
+
/** The column's `key`. */
|
|
8
|
+
key: string;
|
|
9
|
+
direction: DataTableSortDirection;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* How many rows may be chosen at once.
|
|
13
|
+
*
|
|
14
|
+
* `none` is the default: a table that highlights a row under the pointer but
|
|
15
|
+
* cannot be selected is a table that has promised something it does not do.
|
|
16
|
+
*/
|
|
17
|
+
export type DataTableSelectionMode = 'none' | 'single' | 'multiple';
|
|
18
|
+
/**
|
|
19
|
+
* Which rows carry the tint, counted the way a reader counts them — `odd` is
|
|
20
|
+
* the first, the third and the fifth.
|
|
21
|
+
*/
|
|
22
|
+
export type DataTableStripe = 'odd' | 'even';
|
|
23
|
+
/**
|
|
24
|
+
* How the rows are handed out.
|
|
25
|
+
*
|
|
26
|
+
* - `scroll` — all of them, in one scrolling body. With a `height` set this is
|
|
27
|
+
* the virtualized mode, and the one to reach for at any size.
|
|
28
|
+
* - `pages` — a slice at a time, with a footer to step through them. Right when
|
|
29
|
+
* the row's position in the whole set is information (a ledger, a log), and
|
|
30
|
+
* the only option when the rows are being fetched a page at a time.
|
|
31
|
+
*/
|
|
32
|
+
export type DataTablePaging = 'scroll' | 'pages';
|
|
33
|
+
/** The three things the table does to the rows before drawing them. */
|
|
34
|
+
export type DataTableStage = 'sort' | 'filter' | 'pages';
|
|
35
|
+
/**
|
|
36
|
+
* A column: its heading, how wide it is, how to get a value out of a row and
|
|
37
|
+
* how to draw one.
|
|
38
|
+
*
|
|
39
|
+
* The split between `value` and `render` is the whole shape of this type. A
|
|
40
|
+
* `render` decides what a reader sees; a `value` decides what the sort and the
|
|
41
|
+
* search see. Most columns need neither — the cell is `row[key]` and that is
|
|
42
|
+
* what is compared and matched. A column that draws a Chip needs `render`, and
|
|
43
|
+
* it needs `value` as well the moment it is sortable, because a React element
|
|
44
|
+
* has no order.
|
|
45
|
+
*/
|
|
46
|
+
export interface DataTableColumn<Row> {
|
|
47
|
+
/**
|
|
48
|
+
* Identifies the column — to `sort`, to `columnWidths`, and unless `value` or
|
|
49
|
+
* `render` says otherwise, it names the property to read off each row.
|
|
50
|
+
*/
|
|
51
|
+
key: string;
|
|
52
|
+
/** The heading. Defaults to the `key`, which is usually not what you want. */
|
|
53
|
+
label?: React.ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* The heading above this column and its neighbours.
|
|
56
|
+
*
|
|
57
|
+
* Adjacent columns carrying the same string are drawn under one merged cell
|
|
58
|
+
* in a second header row; a column with no `group` spans both rows. Merging
|
|
59
|
+
* by adjacency rather than by a separate model is deliberate — a group is a
|
|
60
|
+
* run of columns, and a table where "Address" covers columns 2, 5 and 9 is a
|
|
61
|
+
* table whose columns are in the wrong order.
|
|
62
|
+
*/
|
|
63
|
+
group?: string;
|
|
64
|
+
/**
|
|
65
|
+
* How wide, in pixels. Columns that do not say share whatever is left.
|
|
66
|
+
*
|
|
67
|
+
* Pixels rather than any CSS length, because the width is a number the resize
|
|
68
|
+
* drag does arithmetic on. A column that has been dragged keeps the dragged
|
|
69
|
+
* width until it is double-clicked.
|
|
70
|
+
*/
|
|
71
|
+
width?: number;
|
|
72
|
+
/** How narrow a drag may make it. @default 48 */
|
|
73
|
+
minWidth?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Which edge the cells line up against. Numbers want `end` so their digits
|
|
76
|
+
* line up in a column.
|
|
77
|
+
* @default 'start'
|
|
78
|
+
*/
|
|
79
|
+
align?: NebaAlign;
|
|
80
|
+
/** The heading's own alignment, when it should differ from the cells'. */
|
|
81
|
+
headerAlign?: NebaAlign;
|
|
82
|
+
/** Whether this column can be sorted. Defaults to the table's `sortable`. */
|
|
83
|
+
sortable?: boolean;
|
|
84
|
+
/** Whether this column can be dragged wider. Defaults to `resizable`. */
|
|
85
|
+
resizable?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Whether the search looks in this column.
|
|
88
|
+
* @default true
|
|
89
|
+
*/
|
|
90
|
+
searchable?: boolean;
|
|
91
|
+
/** Leaves the column out without removing it from the list. */
|
|
92
|
+
hidden?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* The value behind the cell: what is sorted, and what the search is matched
|
|
95
|
+
* against. Defaults to `row[key]`.
|
|
96
|
+
*/
|
|
97
|
+
value?: (row: Row) => unknown;
|
|
98
|
+
/**
|
|
99
|
+
* Orders two rows by this column, for a value the default comparison cannot
|
|
100
|
+
* rank — a status that goes `draft`, `review`, `live` rather than
|
|
101
|
+
* alphabetically. Always written ascending; the table reverses it.
|
|
102
|
+
*/
|
|
103
|
+
compare?: (a: Row, b: Row) => number;
|
|
104
|
+
/**
|
|
105
|
+
* Draws the cell. `index` is the row's place in the sorted, filtered order,
|
|
106
|
+
* counted from `0` across every page — so `(row, index) => index + 1` is a
|
|
107
|
+
* running row number.
|
|
108
|
+
*/
|
|
109
|
+
render?: (row: Row, index: number) => React.ReactNode;
|
|
110
|
+
}
|
|
111
|
+
export interface DataTableProps<Row> extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'children' | 'onSelect' | 'defaultValue'> {
|
|
112
|
+
/** The columns, in the order they appear. */
|
|
113
|
+
headers: readonly DataTableColumn<Row>[];
|
|
114
|
+
/** The rows. */
|
|
115
|
+
items: readonly Row[];
|
|
116
|
+
/**
|
|
117
|
+
* A stable identity per row, and the value `selected` is a list of.
|
|
118
|
+
*
|
|
119
|
+
* Defaults to the row's index in `items`, which is enough for a table that
|
|
120
|
+
* only ever displays. The moment rows can be chosen, sorted or filtered it is
|
|
121
|
+
* required in practice: an index identifies a position, and every one of
|
|
122
|
+
* those three changes which row is in it.
|
|
123
|
+
*/
|
|
124
|
+
getRowKey?: (row: Row, index: number) => React.Key;
|
|
125
|
+
/**
|
|
126
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
127
|
+
* @default 0
|
|
128
|
+
*/
|
|
129
|
+
elevation?: NebaElevation;
|
|
130
|
+
/**
|
|
131
|
+
* How tall the scrolling body is — a number is pixels, a string any CSS
|
|
132
|
+
* length. **This is what turns virtual scrolling on**: rows can only be left
|
|
133
|
+
* out of the DOM if something bounds the height they would have taken.
|
|
134
|
+
*/
|
|
135
|
+
height?: number | string;
|
|
136
|
+
/** The same, as a ceiling: the table is as tall as its rows, up to this. */
|
|
137
|
+
maxHeight?: number | string;
|
|
138
|
+
/**
|
|
139
|
+
* How tall one row is, in pixels. Defaults to the `size`/`density` ladder.
|
|
140
|
+
*
|
|
141
|
+
* Every row is this tall and cells never wrap — which is what makes the
|
|
142
|
+
* scroll position arithmetic rather than a measurement. Raise it for a table
|
|
143
|
+
* whose cells hold an Avatar or two lines of text.
|
|
144
|
+
*/
|
|
145
|
+
rowHeight?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Tints every other row. `true` means `even` — the second, the fourth, the
|
|
148
|
+
* sixth — and the parity is counted over the whole set, so it does not change
|
|
149
|
+
* when the rows are scrolled or sorted.
|
|
150
|
+
* @default false
|
|
151
|
+
*/
|
|
152
|
+
striped?: boolean | DataTableStripe;
|
|
153
|
+
/**
|
|
154
|
+
* Lights the row under the pointer.
|
|
155
|
+
* @default true
|
|
156
|
+
*/
|
|
157
|
+
hoverable?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Pins the header while the body scrolls.
|
|
160
|
+
* @default true
|
|
161
|
+
*/
|
|
162
|
+
stickyHeader?: boolean;
|
|
163
|
+
/** Shown above the table, and read out as its accessible name. */
|
|
164
|
+
caption?: React.ReactNode;
|
|
165
|
+
/** The name the grid is announced by, when there is no `caption`. */
|
|
166
|
+
label?: string;
|
|
167
|
+
/** What to show instead of rows when there are none. */
|
|
168
|
+
empty?: React.ReactNode;
|
|
169
|
+
/**
|
|
170
|
+
* Leaves the rows that are off screen out of the DOM. Needs a `height` or a
|
|
171
|
+
* `maxHeight` to have anything to measure against; without one every row is
|
|
172
|
+
* rendered whatever this says.
|
|
173
|
+
* @default true
|
|
174
|
+
*/
|
|
175
|
+
virtual?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* How many rows are kept rendered past each edge of the viewport, so a fast
|
|
178
|
+
* scroll does not show a band of nothing.
|
|
179
|
+
* @default 8
|
|
180
|
+
*/
|
|
181
|
+
overscan?: number;
|
|
182
|
+
/**
|
|
183
|
+
* Makes every column sortable. A column overrides it either way with its own
|
|
184
|
+
* `sortable`.
|
|
185
|
+
* @default false
|
|
186
|
+
*/
|
|
187
|
+
sortable?: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Whether more than one column can be sorted at a time. With `multiple`, a
|
|
190
|
+
* Shift-click adds a column to the sort instead of replacing it.
|
|
191
|
+
* @default 'single'
|
|
192
|
+
*/
|
|
193
|
+
sortMode?: 'single' | 'multiple';
|
|
194
|
+
/** The sort. Use with `onSortChange` for a controlled one. */
|
|
195
|
+
sort?: readonly DataTableSort[];
|
|
196
|
+
/** What it starts as, for an uncontrolled one. */
|
|
197
|
+
defaultSort?: readonly DataTableSort[];
|
|
198
|
+
onSortChange?: (sort: DataTableSort[]) => void;
|
|
199
|
+
/**
|
|
200
|
+
* Lets the headers be dragged wider or narrower. A double-click on the handle
|
|
201
|
+
* gives the column its original width back.
|
|
202
|
+
* @default false
|
|
203
|
+
*/
|
|
204
|
+
resizable?: boolean;
|
|
205
|
+
/** The widths, keyed by column. Use with `onColumnWidthsChange`. */
|
|
206
|
+
columnWidths?: Readonly<Record<string, number>>;
|
|
207
|
+
/** What they start as, for an uncontrolled table. */
|
|
208
|
+
defaultColumnWidths?: Readonly<Record<string, number>>;
|
|
209
|
+
onColumnWidthsChange?: (widths: Record<string, number>) => void;
|
|
210
|
+
/**
|
|
211
|
+
* How many rows may be chosen.
|
|
212
|
+
* @default 'none'
|
|
213
|
+
*/
|
|
214
|
+
selectionMode?: DataTableSelectionMode;
|
|
215
|
+
/** The chosen rows, as their keys. Use with `onSelectedChange`. */
|
|
216
|
+
selected?: readonly React.Key[];
|
|
217
|
+
/** Which start chosen, for an uncontrolled table. */
|
|
218
|
+
defaultSelected?: readonly React.Key[];
|
|
219
|
+
/** The keys, and the rows behind them — including rows on other pages. */
|
|
220
|
+
onSelectedChange?: (selected: React.Key[], rows: Row[]) => void;
|
|
221
|
+
/**
|
|
222
|
+
* Adds a column of ticks, and one in the header that chooses every displayed
|
|
223
|
+
* row at once. The rows stay clickable either way; this is for a table where
|
|
224
|
+
* choosing is the task rather than something done on the way past.
|
|
225
|
+
* @default false
|
|
226
|
+
*/
|
|
227
|
+
checkboxes?: boolean;
|
|
228
|
+
/** Fires on every press of a row, before the selection changes. */
|
|
229
|
+
onRowClick?: (row: Row, index: number, event: React.MouseEvent<HTMLTableRowElement>) => void;
|
|
230
|
+
/** Fires on a double-click, and on Enter. Opening the row is what this is. */
|
|
231
|
+
onRowActivate?: (row: Row, index: number) => void;
|
|
232
|
+
/**
|
|
233
|
+
* Whether the rows arrive all at once or a page at a time.
|
|
234
|
+
* @default 'scroll'
|
|
235
|
+
*/
|
|
236
|
+
paging?: DataTablePaging;
|
|
237
|
+
/** The current page, 1-based. Use with `onPageChange`. */
|
|
238
|
+
page?: number;
|
|
239
|
+
/** @default 1 */
|
|
240
|
+
defaultPage?: number;
|
|
241
|
+
onPageChange?: (page: number) => void;
|
|
242
|
+
/** How many rows a page holds. Use with `onPageSizeChange`. */
|
|
243
|
+
pageSize?: number;
|
|
244
|
+
/** @default 25 */
|
|
245
|
+
defaultPageSize?: number;
|
|
246
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
247
|
+
/**
|
|
248
|
+
* What the footer's page-size Select offers. An empty list drops the control.
|
|
249
|
+
* @default [10, 25, 50, 100]
|
|
250
|
+
*/
|
|
251
|
+
pageSizeOptions?: readonly number[];
|
|
252
|
+
/**
|
|
253
|
+
* The bar under the table: how many rows there are, how many are chosen, and
|
|
254
|
+
* the pages. On by default whenever `paging` is `pages`.
|
|
255
|
+
*/
|
|
256
|
+
footer?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* The query every searchable column is matched against. Use with
|
|
259
|
+
* `onSearchChange` for a controlled field.
|
|
260
|
+
*/
|
|
261
|
+
search?: string;
|
|
262
|
+
/** What it starts as, for an uncontrolled one. */
|
|
263
|
+
defaultSearch?: string;
|
|
264
|
+
onSearchChange?: (search: string) => void;
|
|
265
|
+
/** Draws the search field above the table. @default false */
|
|
266
|
+
searchable?: boolean;
|
|
267
|
+
/** Overrides the field's placeholder and its accessible name. */
|
|
268
|
+
searchPlaceholder?: string;
|
|
269
|
+
/**
|
|
270
|
+
* A filter of your own, applied after the search. Return `false` to drop a
|
|
271
|
+
* row.
|
|
272
|
+
*/
|
|
273
|
+
filter?: (row: Row, index: number) => boolean;
|
|
274
|
+
/** Content at the end of the bar the search field sits in. */
|
|
275
|
+
toolbar?: React.ReactNode;
|
|
276
|
+
/**
|
|
277
|
+
* Which stages the caller has already done — for a table whose rows come from
|
|
278
|
+
* a server a page at a time. `true` is all three.
|
|
279
|
+
*
|
|
280
|
+
* With `pages` in the list, `items` is taken to be one page and `rowCount`
|
|
281
|
+
* says how many rows there are in total.
|
|
282
|
+
* @default false
|
|
283
|
+
*/
|
|
284
|
+
manual?: boolean | readonly DataTableStage[];
|
|
285
|
+
/** How many rows there are in total, when the table is not doing the paging. */
|
|
286
|
+
rowCount?: number;
|
|
287
|
+
/**
|
|
288
|
+
* The language the table's own words are in — the search field's placeholder,
|
|
289
|
+
* the ticks' labels, the footer's count.
|
|
290
|
+
*
|
|
291
|
+
* It is also what the default sort compares strings with. Pass it whenever
|
|
292
|
+
* the markup is rendered on a server: without it the comparison follows the
|
|
293
|
+
* runtime's own locale, and a server that disagrees with the browser about
|
|
294
|
+
* that produces two different row orders for the same table.
|
|
295
|
+
*/
|
|
296
|
+
locale?: string;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* A table for a lot of rows.
|
|
300
|
+
*
|
|
301
|
+
* [Table](../display/table) draws a grid; this one is a place to work. The
|
|
302
|
+
* difference shows up in three decisions and everything else follows from them.
|
|
303
|
+
*
|
|
304
|
+
* **The rows are all the same height, and it is a number.** That is what lets
|
|
305
|
+
* the body render thirty rows out of two hundred thousand and still put the
|
|
306
|
+
* scrollbar in the right place — the offset of a row is its index times a
|
|
307
|
+
* constant, so nothing has to be measured on a scroll frame. It is also why
|
|
308
|
+
* cells truncate instead of wrapping: a cell that decides its own height would
|
|
309
|
+
* make every arithmetic answer above a guess.
|
|
310
|
+
*
|
|
311
|
+
* **Selecting is the file manager's, not the form's.** A click chooses a row
|
|
312
|
+
* and drops the rest, Ctrl adds one, Shift takes the run between, a drag takes
|
|
313
|
+
* the run under the pointer, and the arrow keys do all three with the same
|
|
314
|
+
* modifiers. Ticks are available and are not the default: a column of
|
|
315
|
+
* checkboxes says the task is choosing, and on most tables it is not.
|
|
316
|
+
*
|
|
317
|
+
* **The three stages are one pipeline, and the caller can take over any of
|
|
318
|
+
* them.** Search, then sort, then cut a page out — done here over `items` by
|
|
319
|
+
* default, and skipped stage by stage through `manual` for a table whose server
|
|
320
|
+
* is doing it. Both paths render the same, which is what stops a table that
|
|
321
|
+
* starts local and grows remote from becoming a second component.
|
|
322
|
+
*/
|
|
323
|
+
export declare function DataTable<Row>({ headers, items, getRowKey, variant, size, color, density, elevation, height, maxHeight, rowHeight: rowHeightProp, striped, hoverable, stickyHeader, caption, label, empty, virtual, overscan, sortable, sortMode, sort: sortProp, defaultSort, onSortChange, resizable, columnWidths, defaultColumnWidths, onColumnWidthsChange, selectionMode, selected, defaultSelected, onSelectedChange, checkboxes, onRowClick, onRowActivate, paging, page: pageProp, defaultPage, onPageChange, pageSize: pageSizeProp, defaultPageSize, onPageSizeChange, pageSizeOptions, footer, search: searchProp, defaultSearch, onSearchChange, searchable, searchPlaceholder, filter, toolbar, manual, rowCount, locale, className, style, ...boxProps }: DataTableProps<Row>): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import*as r from"react";import{Box as a}from"../box/Box";import{Checkbox as l}from"../checkbox/Checkbox";import{Pagination as o}from"../pagination/Pagination";import{Select as s}from"../select/Select";import{TextField as i}from"../text-field/TextField";import{compareValues as c,dataHeaderHeights as u,dataRowHeights as d,dataTickWidths as p,defaultColumnWidth as m,keysBetween as h,minColumnWidth as f,nextSort as g,pageBounds as v,searchText as y,sortRows as b,virtualWindow as k}from"../../internal/data-table";import{fillMessage as w,useMessages as x}from"../../internal/i18n";import{ChevronIcon as C}from"../../internal/icons";import{controlTextLeadingClasses as S,cx as M,hasContent as N,metaTextClasses as K,paddingXValues as z,srOnlyClasses as $}from"../../internal/styles";const E=["[--n-row:transparent]","[transition:background-color_var(--neba-duration)_var(--neba-ease)]"].join(" "),_={"--n-stripe":"color-mix(in oklab, var(--neba-fg) 4%, transparent)"},R=["group/sort flex w-full min-w-0 cursor-pointer items-center gap-1","text-inherit [outline:none]","[transition:color_var(--neba-duration)_var(--neba-ease)]","hover:text-(--n-accent)","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-1","[&_svg]:pointer-events-none [&_svg]:size-[1.15em] [&_svg]:shrink-0"].join(" "),D=["absolute inset-y-0 z-10 w-2 cursor-col-resize select-none","end-0 translate-x-1/2 rtl:-translate-x-1/2","after:absolute after:inset-y-1 after:start-1/2 after:w-px","after:[background:transparent]","after:[transition:background_var(--neba-duration)_var(--neba-ease)]","hover:after:[background:var(--n-accent)]"].join(" ");function L(){return t("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e("circle",{cx:"7.25",cy:"7.25",r:"4.75",stroke:"currentColor",strokeWidth:"1.5"}),e("path",{d:"m10.75 10.75 2.75 2.75",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]})}function P(e){return"number"==typeof e?`${e}px`:e}export function DataTable({headers:A,items:T,getRowKey:j,variant:H="outline",size:B="sm",color:I="primary",density:W="compact",elevation:F=0,height:O,maxHeight:q,rowHeight:U,striped:X=!1,hoverable:Y=!0,stickyHeader:V=!0,caption:G,label:J,empty:Q,virtual:Z=!0,overscan:ee=8,sortable:te=!1,sortMode:ne="single",sort:re,defaultSort:ae,onSortChange:le,resizable:oe=!1,columnWidths:se,defaultColumnWidths:ie,onColumnWidthsChange:ce,selectionMode:ue="none",selected:de,defaultSelected:pe,onSelectedChange:me,checkboxes:he=!1,onRowClick:fe,onRowActivate:ge,paging:ve="scroll",page:ye,defaultPage:be=1,onPageChange:ke,pageSize:we,defaultPageSize:xe=25,onPageSizeChange:Ce,pageSizeOptions:Se=[10,25,50,100],footer:Me,search:Ne,defaultSearch:Ke,onSearchChange:ze,searchable:$e=!1,searchPlaceholder:Ee,filter:_e,toolbar:Re,manual:De=!1,rowCount:Le,locale:Pe,className:Ae,style:Te,...je}){const He=x(Pe),Be=r.useId(),Ie=U??d[W][B],We=u[W][B],Fe=z[W][B],Oe=r.useMemo(()=>new Set(!0===De?["sort","filter","pages"]:De||[]),[De]),qe=r.useMemo(()=>A.filter(e=>!e.hidden),[A]),Ue=qe.some(e=>void 0!==e.group),Xe="none"!==ue,Ye="multiple"===ue,Ve=Xe&&he,Ge=r.useMemo(()=>T.map((e,t)=>{const n=j?j(e,t):t;return{row:e,identity:n,key:String(n),origin:t}}),[T,j]),[Je,Qe]=r.useState(Ke??""),Ze=(Ne??Je).trim(),et=r.useMemo(()=>{if(Oe.has("filter"))return Ge;const e=y(Ze),t=qe.filter(e=>!1!==e.searchable);let n=Ge;return""!==e&&t.length>0&&(n=n.filter(n=>t.some(t=>{const r=t.value?t.value(n.row):n.row[t.key];return y(r).includes(e)}))),_e?n.filter(e=>_e(e.row,e.origin)):n},[Ge,qe,Ze,_e,Oe]),tt=r.useMemo(()=>new Intl.Collator(Pe,{numeric:!0,sensitivity:"base"}),[Pe]),[nt,rt]=r.useState(ae??[]),at=re??nt,lt=r.useMemo(()=>Oe.has("sort")?et:b(et,at,e=>{const t=qe.find(t=>t.key===e);if(!t)return null;if(t.compare)return(e,n)=>t.compare(e.row,n.row);const n=t.value?t.value:e=>e[t.key];return(e,t)=>c(n(e.row),n(t.row),tt)}),[et,at,qe,tt,Oe]),[ot,st]=r.useState(be),[it,ct]=r.useState(xe),ut=we??it,dt=Oe.has("pages")?Le??lt.length:lt.length,pt=v(dt,ye??ot,ut),mt=r.useMemo(()=>"pages"!==ve||Oe.has("pages")?lt:lt.slice(pt.start,pt.end),[lt,ve,Oe,pt.start,pt.end]),ht="pages"===ve?pt.start:0,ft=r.useMemo(()=>mt.map(e=>e.key),[mt]),gt=r.useMemo(()=>new Map(Ge.map(e=>[e.key,e])),[Ge]),[vt,yt]=r.useState(pe??[]),bt=de??vt,kt=r.useMemo(()=>new Set(bt.map(String)),[bt]),[wt,xt]=r.useState(null),Ct=r.useRef(null),St=r.useRef({paged:mt,pagedKeys:ft,byKey:gt,selectedKeys:kt,multiple:Ye});St.current={paged:mt,pagedKeys:ft,byKey:gt,selectedKeys:kt,multiple:Ye};const Mt=r.useCallback(e=>{const t=[],n=[];for(const r of e){const e=St.current.byKey.get(r);e&&(t.push(e.identity),n.push(e.row))}void 0===de&&yt(t),me?.(t,n)},[de,me]),Nt=r.useCallback(e=>{Ct.current=e,Mt([e])},[Mt]),Kt=r.useCallback(e=>{const t=St.current.selectedKeys,n=new Set(t);n.has(e)?n.delete(e):n.add(e),Ct.current=e,Mt([...n])},[Mt]),zt=r.useCallback((e,t)=>{const n=St.current.pagedKeys,r=Ct.current??n[0];if(void 0===r)return;const a=h(n,r,e);0!==a.length&&Mt(t?[...new Set([...St.current.selectedKeys,...a])]:a)},[Mt]),$t=r.useRef(null),Et=r.useRef(null),_t=r.useRef(null),[Rt,Dt]=r.useState(0),[Lt,Pt]=r.useState(0),At=void 0!==O||void 0!==q,Tt=Z&&At&&mt.length>0;r.useLayoutEffect(()=>{const e=$t.current;if(!e||!Tt)return;const t=new ResizeObserver(([e])=>{Dt(e.contentRect.height)});return t.observe(e),Dt(e.clientHeight),()=>t.disconnect()},[Tt]);const jt=r.useCallback(e=>{if(!Tt)return;const t=Math.floor(e.currentTarget.scrollTop/Ie)*Ie;Pt(e=>e===t?e:t)},[Tt,Ie]),Ht=Tt?k(Lt,Rt,Ie,mt.length,ee):{start:0,end:mt.length,before:0,after:0},Bt=mt.slice(Ht.start,Ht.end),It=Xe&&null!==wt&&Bt.some(e=>e.key===wt),[Wt,Ft]=r.useState(ie??{}),Ot=se??Wt,qt=r.useCallback(e=>{void 0===se&&Ft(e),ce?.(e)},[se,ce]),Ut=r.useRef(new Map),Xt=r.useCallback(e=>{const t=$t.current;if(!t||!At)return;const n=V?We*(Ue?2:1):0,r=e*Ie;r<t.scrollTop+n?t.scrollTop=Math.max(0,r-n):r+Ie>t.scrollTop+t.clientHeight&&(t.scrollTop=r+Ie-t.clientHeight)},[At,V,We,Ue,Ie]),Yt=(e,t)=>{const n=St.current.paged,r=Math.min(Math.max(e,0),n.length-1),a=n[r];a&&(t.preventDefault(),xt(a.key),Xt(r),t.shiftKey&&St.current.multiple?zt(a.key,!1):t.ctrlKey||t.metaKey||Nt(a.key))};const Vt=r.useCallback(e=>{const t=Et.current,n=St.current.paged;if(!t||0===n.length)return null;const r=t.getBoundingClientRect().top,a=Math.floor((e-r)/Ie);return Math.min(Math.max(a,0),n.length-1)},[Ie]),Gt=r.useRef(null),Jt=r.useCallback(e=>{const t=Vt(e),n=null===t?void 0:St.current.paged[t];n&&(xt(n.key),zt(n.key,!1))},[Vt,zt]),Qt=r.useRef(Jt);Qt.current=Jt;function Zt(e,t){if(Xe&&!t.target.closest('button, a, input, select, textarea, [role="button"]'))if(_t.current?.focus({preventScroll:!0}),xt(e.key),2!==t.button){if(0===t.button)return t.shiftKey&&Ye?(t.preventDefault(),void zt(e.key,t.ctrlKey||t.metaKey)):void((t.ctrlKey||t.metaKey)&&Ye?Kt(e.key):(Nt(e.key),Ye&&(e=>{if(Gt.current)return;const t=e=>{const t=Gt.current,r=$t.current;if(!t)return;if(t.y=e.clientY,Qt.current(t.y),!r||!At)return;const a=r.getBoundingClientRect(),l=a.top+Ie-t.y,o=t.y-(a.bottom-Ie);t.speed=l>0?-Math.min(l,40):o>0?Math.min(o,40):0,0!==t.speed&&null===t.frame&&(t.frame=requestAnimationFrame(n))},n=()=>{const e=Gt.current,t=$t.current;e&&t&&0!==e.speed?(t.scrollTop+=e.speed,Qt.current(e.y),e.frame=requestAnimationFrame(n)):e&&(e.frame=null)},r=()=>{const e=Gt.current;null!=e?.frame&&cancelAnimationFrame(e.frame),Gt.current=null,window.removeEventListener("pointermove",t),window.removeEventListener("pointerup",r),window.removeEventListener("pointercancel",r)};Gt.current={y:e,speed:0,frame:null,stop:r},window.addEventListener("pointermove",t),window.addEventListener("pointerup",r),window.addEventListener("pointercancel",r)})(t.clientY)))}else St.current.selectedKeys.has(e.key)||Nt(e.key)}r.useEffect(()=>()=>Gt.current?.stop(),[]);const en=r.useMemo(()=>ft.reduce((e,t)=>kt.has(t)?e+1:e,0),[ft,kt]),tn=ft.length>0&&en===ft.length,nn=p[B],rn=e=>Ot[e.key]??e.width,an=(Ve?nn:0)+qe.reduce((e,t)=>e+(rn(t)??f),0),ln=qe.length>0&&qe.every(e=>void 0!==rn(e)),on=qe.length+(Ve?1:0)+(ln?1:0),sn=r.useMemo(()=>{const e=[];return qe.forEach((t,n)=>{const r=e[e.length-1];r&&void 0!==r.label&&r.label===t.group?r.span+=1:e.push({label:t.group,span:1,from:n})}),e},[qe]),cn={padding:`0 ${Fe}`,borderBottom:"1px solid var(--n-line)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},un={...cn,height:`${We}px`,backgroundColor:"var(--n-panel-press)"},dn="odd"===X?0:1,pn=r.useMemo(()=>new Intl.NumberFormat(Pe),[Pe]),mn=Me??"pages"===ve,hn=w(He.table.range,{start:pn.format(0===dt?0:("pages"===ve?pt.start:0)+1),end:pn.format("pages"===ve?pt.end:dt),total:pn.format(dt)}),fn=e=>{void 0===ye&&st(e),ke?.(e)},gn=(n,r,a)=>{const l=n.sortable??te,o=n.resizable??oe,s=(i=n.key,at.find(e=>e.key===i));var i;const c=s?at.findIndex(e=>e.key===n.key)+1:0,u=n.headerAlign??n.align??"start";return t("th",{ref:e=>{e?Ut.current.set(n.key,e):Ut.current.delete(n.key)},scope:"col",rowSpan:a,"aria-sort":s?"asc"===s.direction?"ascending":"descending":void 0,className:M("relative font-semibold select-none",s?"text-(--n-accent)":"text-(--neba-muted-fg)",V?"sticky z-20 [backdrop-filter:var(--neba-blur)]":""),style:{...un,top:V?2!==a&&Ue?We:0:void 0,textAlign:u},children:[l?t("button",{type:"button",className:M(R,"end"===u?"justify-end":"center"===u?"justify-center":"justify-start"),onClick:e=>((e,t)=>{const n=g(at,e,t&&"multiple"===ne);void 0===re&&rt(n),le?.(n)})(n.key,e.shiftKey),children:[e("span",{className:"min-w-0 truncate",children:n.label??n.key}),e("span",{"aria-hidden":"true",className:M("flex items-center","desc"===s?.direction?"rotate-0":"rotate-180",s?"":"text-transparent group-hover/sort:text-inherit group-focus-visible/sort:text-inherit"),children:e(C,{})}),c>1?e("span",{"aria-hidden":"true",className:"text-[0.85em] tabular-nums",children:c}):null]}):e("span",{className:"block truncate",children:n.label??n.key}),o&&r<qe.length-1?e("span",{"aria-hidden":"true",className:D,onPointerDown:e=>((e,t)=>{t.preventDefault(),t.stopPropagation();const n=t.currentTarget,r={...Ot};for(const e of qe){const t=Ut.current.get(e.key);t&&void 0===r[e.key]&&(r[e.key]=Math.round(t.getBoundingClientRect().width))}const a="rtl"===getComputedStyle(n).direction,l=t.clientX,o=r[e]??m,s=qe.find(t=>t.key===e)?.minWidth??f;n.setPointerCapture(t.pointerId);const i=t=>{const n=(t.clientX-l)*(a?-1:1);qt({...r,[e]:Math.max(s,Math.round(o+n))})},c=()=>{n.removeEventListener("pointermove",i),n.removeEventListener("pointerup",c),n.removeEventListener("pointercancel",c)};n.addEventListener("pointermove",i),n.addEventListener("pointerup",c),n.addEventListener("pointercancel",c)})(n.key,e),onDoubleClick:()=>(e=>{const t={...Ot};delete t[e],qt(t)})(n.key)}):null]},n.key)},vn=t=>e("span",{className:"flex items-center justify-center",children:t}),yn=t=>e("td",{role:Xe?"gridcell":void 0,style:{...cn,padding:0,overflow:"visible"},children:vn(e(l,{size:B,color:I,checked:kt.has(t.key),"aria-label":He.table.selectRow,onCheckedChange:()=>{Ye?Kt(t.key):Nt(t.key)}}))}),bn=Ye?vn(e(l,{size:B,color:I,checked:tn,indeterminate:en>0&&!tn,"aria-label":He.table.selectAll,onCheckedChange:()=>{if(tn){const e=new Set(ft);Mt([...kt].filter(t=>!e.has(t)))}else Mt([...new Set([...kt,...ft])])}})):null,kn=ln?e("th",{"aria-hidden":"true",className:M(V?"sticky top-0 z-20 [backdrop-filter:var(--neba-blur)]":""),style:{...un,padding:0}}):null,wn=(t,n)=>n>0?e("tr",{"aria-hidden":"true",style:{height:`${n}px`},children:e("td",{colSpan:on,style:{padding:0,border:0}})},t):null;return t(a,{variant:H,size:B,color:I,density:W,elevation:F,padded:!1,className:M("flex flex-col overflow-hidden",Xe?"has-[:focus-visible]:[outline:2px_solid_var(--n-ring)] has-[:focus-visible]:outline-offset-2":"",Ae),style:{..._,...Te},...je,children:[$e||N(Re)?t("div",{className:"flex items-center gap-2",style:{padding:`${Fe} ${Fe}`,borderBottom:"1px solid var(--n-line)"},children:[$e?e(i,{size:B,color:I,density:W,variant:"outline",type:"search",className:"w-full max-w-64",startIcon:e(L,{}),placeholder:Ee??He.table.search,"aria-label":Ee??He.table.search,value:Ne??Je,onChange:e=>{const t=e.target.value;void 0===Ne&&Qe(t),fn(1),ze?.(t)}}):null,N(Re)?e("div",{className:"ms-auto flex items-center gap-2",children:Re}):null]}):null,e("div",{ref:$t,className:"min-h-0 flex-auto overflow-auto overscroll-contain",style:{height:P(O),maxHeight:P(q)},onScroll:jt,children:t("table",{ref:_t,role:Xe?"grid":void 0,"aria-label":J,"aria-multiselectable":Ye||void 0,"aria-rowcount":Tt?mt.length+1:void 0,"aria-activedescendant":It?`${Be}-${wt}`:void 0,tabIndex:Xe?0:void 0,className:M("w-full text-start [outline:none]",S[B],"text-(--neba-fg)",Xe?"select-none":""),style:{tableLayout:"fixed",borderCollapse:"separate",borderSpacing:0,minWidth:`${an}px`},onKeyDown:function(e){if(!Xe||e.target!==e.currentTarget)return;const t=St.current.paged;if(0===t.length)return;const n=null===wt?-1:St.current.pagedKeys.indexOf(wt),r=Math.max(1,Math.floor((Rt||10*Ie)/Ie)-1);switch(e.key){case"ArrowDown":Yt(n+1,e);break;case"ArrowUp":Yt(-1===n?0:n-1,e);break;case"Home":Yt(0,e);break;case"End":Yt(t.length-1,e);break;case"PageDown":Yt(-1===n?0:n+r,e);break;case"PageUp":Yt(-1===n?0:n-r,e);break;case" ":-1!==n&&(e.preventDefault(),(e.ctrlKey||e.metaKey)&&Ye?Kt(t[n].key):Nt(t[n].key));break;case"a":case"A":(e.ctrlKey||e.metaKey)&&Ye&&(e.preventDefault(),Mt(St.current.pagedKeys));break;case"Escape":e.preventDefault(),Mt([]);break;case"Enter":-1!==n&&ge&&(e.preventDefault(),ge(t[n].row,ht+n))}},children:[G?e("caption",{className:M(K[B],"text-(--neba-muted-fg)"),style:{padding:`0.5rem ${Fe}`,textAlign:"start"},children:G}):null,t("colgroup",{children:[Ve?e("col",{style:{width:`${nn}px`}}):null,qe.map(t=>{const n=rn(t);return e("col",{style:void 0===n?void 0:{width:`${n}px`}},t.key)}),ln?e("col",{}):null]}),t("thead",{children:[Ue?t("tr",{style:{height:`${We}px`},children:[Ve?e("th",{scope:"col",rowSpan:2,className:M(V?"sticky top-0 z-20 [backdrop-filter:var(--neba-blur)]":""),style:{...un,padding:0,overflow:"visible"},children:bn}):null,sn.map(t=>void 0===t.label?gn(qe[t.from],t.from,2):e("th",{scope:"colgroup",colSpan:t.span,className:M("font-semibold text-(--neba-muted-fg) select-none",V?"sticky top-0 z-20 [backdrop-filter:var(--neba-blur)]":""),style:{...un,textAlign:"center"},children:t.label},`group-${t.from}`)),ln?r.cloneElement(kn,{rowSpan:2}):null]}):null,t("tr",{style:{height:`${We}px`},children:[Ve&&!Ue?e("th",{scope:"col",className:M(V?"sticky top-0 z-20 [backdrop-filter:var(--neba-blur)]":""),style:{...un,padding:0,overflow:"visible"},children:bn}):null,qe.map((e,t)=>Ue&&void 0===e.group?null:gn(e,t)),ln&&!Ue?kn:null]})]}),e("tbody",{ref:Et,children:0===mt.length?e("tr",{children:e("td",{colSpan:on,className:"text-(--neba-muted-fg)",style:{padding:`2rem ${Fe}`,textAlign:"center"},children:Q??He.empty.title})}):t(n,{children:[wn("before",Ht.before),Bt.map((n,r)=>{const a=Ht.start+r,l=kt.has(n.key),o=wt===n.key;return t("tr",{id:`${Be}-${n.key}`,"aria-selected":Xe?l:void 0,"aria-rowindex":Tt?a+2:void 0,"data-neba-row":n.key,className:M(E,l?"[--n-row:var(--n-soft-press)]":!1!==X&&a%2===dn?"[--n-row:var(--n-stripe)]":"",!l&&Y?"hover:[--n-row:var(--n-soft)]":"",o&&Xe?"[box-shadow:inset_0_0_0_1px_var(--n-ring)]":"",Xe||fe?"cursor-default":""),style:{height:`${Ie}px`,backgroundColor:"var(--n-row)"},onPointerDown:e=>Zt(n,e),onClick:e=>fe?.(n.row,ht+a,e),onDoubleClick:()=>ge?.(n.row,ht+a),children:[Ve?yn(n):null,qe.map(t=>e("td",{role:Xe?"gridcell":void 0,style:{...cn,textAlign:t.align??"start"},children:t.render?t.render(n.row,ht+a):n.row[t.key]},t.key)),ln?e("td",{"aria-hidden":"true",style:{...cn,padding:0}}):null]},n.key)}),wn("after",Ht.after)]})})]})}),mn?t("div",{className:M("flex flex-wrap items-center gap-3",K[B]),style:{padding:`0.375rem ${Fe}`,borderTop:"1px solid var(--n-line)"},children:[e("span",{className:"text-(--neba-muted-fg) tabular-nums",children:hn}),Xe&&kt.size>0?e("span",{className:"text-(--n-accent) tabular-nums",children:w(He.table.selected,{count:pn.format(kt.size)})}):null,t("div",{className:"ms-auto flex items-center gap-2",children:["pages"===ve&&Se.length>0?e(n,{children:e(s,{size:B,color:I,density:W,variant:"outline",label:He.table.rowsPerPage,items:Se.map(e=>({value:e})),value:ut,onValueChange:e=>{const t=Number(e);void 0===we&&ct(t),fn(Math.floor(pt.start/t)+1),Ce?.(t)}})}):null,"pages"===ve?e(o,{size:B,color:I,count:pt.pages,page:pt.page,siblingCount:0,onPageChange:fn}):null]})]}):null,!mn&&Tt?e("span",{className:$,"aria-live":"polite",children:hn}):null]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{DataTable}from"./DataTable";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaElevation, NebaStyleProps, NebaTransition } from '../../types';
|
|
4
|
+
export interface EmptyProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'title'> {
|
|
5
|
+
/**
|
|
6
|
+
* The headline. Defaults to the `locale`'s way of saying that there is
|
|
7
|
+
* nothing here; pass `false` for a state that is a glyph and a sentence with
|
|
8
|
+
* no heading over them.
|
|
9
|
+
*/
|
|
10
|
+
title?: React.ReactNode | false;
|
|
11
|
+
/**
|
|
12
|
+
* The glyph above the headline. Defaults to the empty tray; pass `false` to
|
|
13
|
+
* drop it, or a node — an illustration, a brand mark, an icon from any set —
|
|
14
|
+
* to replace it. An `svg` is sized off the `size` ladder; anything else is
|
|
15
|
+
* left at whatever size it came in at.
|
|
16
|
+
*/
|
|
17
|
+
icon?: React.ReactNode | false;
|
|
18
|
+
/**
|
|
19
|
+
* What to do about it, under the text: a "Create the first one" button, a
|
|
20
|
+
* "Clear filters" link. Several of them sit in a row and wrap together.
|
|
21
|
+
*/
|
|
22
|
+
action?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Which language the default headline is written in — a BCP 47 tag. Ignored
|
|
25
|
+
* once `title` is given, and unsupported tags fall back to English.
|
|
26
|
+
* @default 'en'
|
|
27
|
+
*/
|
|
28
|
+
locale?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Drop shadow depth. `0` (the default) is flat, and it is almost always
|
|
31
|
+
* right: an empty state is a hole in a surface that already exists rather
|
|
32
|
+
* than a sheet of its own.
|
|
33
|
+
* @default 0
|
|
34
|
+
*/
|
|
35
|
+
elevation?: NebaElevation;
|
|
36
|
+
/**
|
|
37
|
+
* An entrance animation, run once on mount: `transition="fade"` for a list
|
|
38
|
+
* that has just come back with nothing. For a trigger or a replay, wrap it in
|
|
39
|
+
* an `Animate*` component instead.
|
|
40
|
+
*/
|
|
41
|
+
transition?: NebaTransition;
|
|
42
|
+
/** Renders something other than a `<div>`: `render={<td colSpan={5} />}`. */
|
|
43
|
+
render?: useRender.RenderProp;
|
|
44
|
+
/** The sentence under the headline: why it is empty, or what to do next. */
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* What stands where content would have been: a glyph, a headline, a sentence
|
|
49
|
+
* and a way out.
|
|
50
|
+
*
|
|
51
|
+
* It is the other half of [Skeleton](../feedback/skeleton). A skeleton is the
|
|
52
|
+
* shape of something on its way; this is the shape of something that is not
|
|
53
|
+
* coming — a search with no matches, an inbox nobody has written to, a folder
|
|
54
|
+
* before the first file. The two are never both right at once, and a list that
|
|
55
|
+
* shows neither has a blank rectangle where its answer should be.
|
|
56
|
+
*
|
|
57
|
+
* The headline is the only text in the library a component invents at full
|
|
58
|
+
* size, and it is defaulted rather than required for one reason: the version
|
|
59
|
+
* that says nothing useful is the version that gets shipped. `Nothing here` in
|
|
60
|
+
* the reader's language is a floor, and every slot above it — the glyph, the
|
|
61
|
+
* sentence, the action — is there to be filled with what is actually missing.
|
|
62
|
+
*
|
|
63
|
+
* `surfaceSlots` is the undyed slot set, so `color` reaches the hairline and
|
|
64
|
+
* the ring and stops there. An empty state that arrives in the accent colour is
|
|
65
|
+
* making a claim about content that does not exist; the family is worth
|
|
66
|
+
* changing only when the emptiness is itself a problem (`color="danger"` on a
|
|
67
|
+
* region that failed to load).
|
|
68
|
+
*/
|
|
69
|
+
export declare const Empty: React.ForwardRefExoticComponent<EmptyProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import*as n from"react";import{useRender as l}from"@base-ui/react/use-render";import{boxPaddingXClasses as a}from"../box/Box";import{transitionProps as o}from"../../internal/animate";import{useMessages as s}from"../../internal/i18n";import{hasContent as i,iconClasses as m,radiusClasses as c,sheetBodyClasses as d,sheetHeaderGapClasses as x,sheetSectionGapClasses as p,sheetTitleClasses as f,surfaceClasses as u,surfaceSlots as h,transitionClasses as y}from"../../internal/styles";const v={default:{xs:"py-5",sm:"py-6",md:"py-8",lg:"py-10",xl:"py-12"},compact:{xs:"py-3",sm:"py-3.5",md:"py-4",lg:"py-5",xl:"py-6"}},b={xs:"text-[1.25rem]",sm:"text-[1.5rem]",md:"text-[1.75rem]",lg:"text-[2.125rem]",xl:"text-[2.5rem]"},g={solid:[u,"text-(--neba-fg) bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[u,"border text-(--neba-fg) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--neba-fg) bg-transparent"};function j(){return t("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e("path",{d:"M1.75 9.75h3l.9 1.6h4.7l.9-1.6h3",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"m1.75 9.75 2.1-6a1.5 1.5 0 0 1 1.42-1h5.46a1.5 1.5 0 0 1 1.42 1l2.1 6v2a1.5 1.5 0 0 1-1.5 1.5h-9.5a1.5 1.5 0 0 1-1.5-1.5Z",stroke:"currentColor",strokeWidth:"1.5",strokeLinejoin:"round"})]})}export const Empty=n.forwardRef(function({variant:n="text",size:u="md",color:N="secondary",density:k="default",elevation:w=0,title:$,icon:B,action:L,locale:C,transition:W,render:z,className:E,style:F,children:M,...R},Z){const q=s(C),A=void 0===$?q.empty.title:$,D=void 0===B?e(j,{}):B,G=o(W),H=i(A),I=["flex w-full flex-col items-center justify-center text-center",a[k][u],v[k][u],c[u],p[u],y,g[n],G.className,E??""].filter(Boolean).join(" ");return l({render:z,ref:Z,props:{role:"status",className:I,style:{...h(N,w),...G.style,...F},children:t(r,{children:[i(D)?e("span",{className:`flex items-center text-(--neba-muted-fg) ${b[u]} ${m}`,children:D}):null,H||i(M)?t("div",{className:`flex max-w-prose flex-col items-center ${x[u]}`,children:[H?e("div",{className:`neba-title font-semibold ${f[u]}`,children:A}):null,i(M)?e("div",{className:`${d[u]} ${H?"text-(--neba-muted-fg)":""}`,children:M}):null]}):null,i(L)?e("div",{className:"flex flex-wrap items-center justify-center gap-2",children:L}):null]}),...R}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Empty}from"./Empty";
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaMockupBezel, NebaMockupDevice, NebaMockupFinish, NebaMockupHardware, NebaMockupNotch, NebaMockupOrientation, NebaMockupOs, NebaMockupResolution } from '../../internal/mockup';
|
|
4
|
+
import type { NebaColor, NebaElevation, NebaSize, NebaTransition } from '../../types';
|
|
5
|
+
export type { NebaMockupBezel, NebaMockupDevice, NebaMockupFinish, NebaMockupHardware, NebaMockupNotch, NebaMockupOrientation, NebaMockupOs, NebaMockupResolution } from '../../internal/mockup';
|
|
6
|
+
export interface MockupProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
7
|
+
/**
|
|
8
|
+
* Which machine this is a picture of. The one prop with no default: a mockup
|
|
9
|
+
* that has not said what it is a mockup of has not said anything.
|
|
10
|
+
*/
|
|
11
|
+
device: NebaMockupDevice;
|
|
12
|
+
/**
|
|
13
|
+
* The system whose chrome is drawn on the screen. A desktop runs `macos`,
|
|
14
|
+
* `windows` or `linux`; a tablet runs `ipados` or `android`; a phone runs `ios`
|
|
15
|
+
* or `android`. Anything else falls back to the device's own default —
|
|
16
|
+
* `macos`, `ipados` and `ios` respectively.
|
|
17
|
+
*/
|
|
18
|
+
os?: NebaMockupOs;
|
|
19
|
+
/**
|
|
20
|
+
* What holds a desktop screen up: a stand under it, or a keyboard in front of
|
|
21
|
+
* it. Ignored on a tablet and a phone, which hold themselves up.
|
|
22
|
+
* @default 'monitor'
|
|
23
|
+
*/
|
|
24
|
+
hardware?: NebaMockupHardware;
|
|
25
|
+
/**
|
|
26
|
+
* How big the device is, on a five-step ladder of real resolutions per device
|
|
27
|
+
* — from a 320-wide phone to a 430-wide one, from a 1024-wide desktop to a
|
|
28
|
+
* 1920-wide one.
|
|
29
|
+
*
|
|
30
|
+
* As on Box, `size` here does not set a height or a type scale. What it sets
|
|
31
|
+
* is the resolution of the screen, which is the only thing about a device
|
|
32
|
+
* there is to scale. `resolution` overrides it outright.
|
|
33
|
+
* @default 'md'
|
|
34
|
+
*/
|
|
35
|
+
size?: NebaSize;
|
|
36
|
+
/**
|
|
37
|
+
* The screen's logical resolution in CSS pixels, when none of the five steps
|
|
38
|
+
* is the machine you mean. This is the viewport the content is laid out
|
|
39
|
+
* against, not the panel's physical pixel count.
|
|
40
|
+
*/
|
|
41
|
+
resolution?: NebaMockupResolution;
|
|
42
|
+
/**
|
|
43
|
+
* Which way a handheld is held. Landscape turns the screen, the bezel and the
|
|
44
|
+
* cut-out together. Ignored on a desktop, whose stand does not turn with it.
|
|
45
|
+
* @default 'portrait'
|
|
46
|
+
*/
|
|
47
|
+
orientation?: NebaMockupOrientation;
|
|
48
|
+
/**
|
|
49
|
+
* How much hardware there is around the screen. `none` is not a thinner
|
|
50
|
+
* bezel — it is no hardware at all, leaving the screen on its own with its
|
|
51
|
+
* corners cut, which is what a mockup that only wants the viewport asks for.
|
|
52
|
+
* `thick` is an older device: narrow sides, a forehead and a chin.
|
|
53
|
+
* @default 'standard'
|
|
54
|
+
*/
|
|
55
|
+
bezel?: NebaMockupBezel;
|
|
56
|
+
/**
|
|
57
|
+
* What the hardware is made of. Fixed colours rather than theme tokens — a
|
|
58
|
+
* graphite phone stays graphite on a page switched to dark.
|
|
59
|
+
* @default 'graphite'
|
|
60
|
+
*/
|
|
61
|
+
finish?: NebaMockupFinish;
|
|
62
|
+
/**
|
|
63
|
+
* The camera cut-out. Hardware rather than chrome, so it is drawn whether or
|
|
64
|
+
* not `systemUi` is on. Defaults to what the device would have: a dynamic
|
|
65
|
+
* island on an iOS phone, a punch hole on an Android one, nothing anywhere
|
|
66
|
+
* else.
|
|
67
|
+
*/
|
|
68
|
+
notch?: NebaMockupNotch;
|
|
69
|
+
/**
|
|
70
|
+
* Draws the system's own bars — a status bar and a home indicator, a menu bar
|
|
71
|
+
* and a dock, a taskbar. Each one takes its own space rather than covering the
|
|
72
|
+
* content, so turning it off gives the screen back to `children` rather than
|
|
73
|
+
* uncovering anything.
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
systemUi?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Whether content taller than the screen scrolls. Off, it is clipped, which is
|
|
79
|
+
* what a still picture of a device wants.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
82
|
+
scroll?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* What is behind the content: any CSS `background` value — a colour, a
|
|
85
|
+
* gradient, a `url()`.
|
|
86
|
+
* @default the page's own surface colour
|
|
87
|
+
*/
|
|
88
|
+
wallpaper?: string;
|
|
89
|
+
/**
|
|
90
|
+
* The clock in the status bar or the taskbar, and the only text the chrome
|
|
91
|
+
* draws. A string rather than a `Date`, because a mockup's clock is a prop of
|
|
92
|
+
* the picture and reading the real one would differ between the server that
|
|
93
|
+
* renders the page and the browser that hydrates it.
|
|
94
|
+
* @default '9:41'
|
|
95
|
+
*/
|
|
96
|
+
time?: string;
|
|
97
|
+
/**
|
|
98
|
+
* The rendered width of the whole device on the page — a number in pixels or
|
|
99
|
+
* any CSS length. The device is laid out at its own resolution and then scaled
|
|
100
|
+
* to whatever this comes to, so the content inside is genuinely a screen's
|
|
101
|
+
* worth rather than a page's worth shrunk.
|
|
102
|
+
* @default '100%'
|
|
103
|
+
*/
|
|
104
|
+
width?: number | string;
|
|
105
|
+
/**
|
|
106
|
+
* The rendered height. Given on its own it decides the size and the width
|
|
107
|
+
* follows the device's proportion, which is what a mockup in a row of fixed
|
|
108
|
+
* height wants.
|
|
109
|
+
*/
|
|
110
|
+
height?: number | string;
|
|
111
|
+
/** Reaches the accent in the chrome — a dock's first icon, a taskbar's. */
|
|
112
|
+
color?: NebaColor;
|
|
113
|
+
/**
|
|
114
|
+
* How far off the page the device sits. Drawn as a silhouette rather than a
|
|
115
|
+
* box, so the shadow follows a lid on a neck on a foot, and it does not shrink
|
|
116
|
+
* with the device.
|
|
117
|
+
* @default 0
|
|
118
|
+
*/
|
|
119
|
+
elevation?: NebaElevation;
|
|
120
|
+
/**
|
|
121
|
+
* An entrance animation, run once on mount: `transition="fade"`, or an object
|
|
122
|
+
* for the details.
|
|
123
|
+
*/
|
|
124
|
+
transition?: NebaTransition;
|
|
125
|
+
/** Renders something other than a `<div>`: `render={<figure />}`. */
|
|
126
|
+
render?: useRender.RenderProp;
|
|
127
|
+
/** What is on the screen. */
|
|
128
|
+
children?: React.ReactNode;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A device with a screen you can put anything on: a phone, a tablet, a monitor
|
|
132
|
+
* or a laptop, with the system's own bars drawn on it.
|
|
133
|
+
*
|
|
134
|
+
* The screen is a viewport at the device's own resolution — a `md` phone is 390
|
|
135
|
+
* by 844 CSS pixels — and the whole device is then scaled once to whatever room
|
|
136
|
+
* it has been given. So the content inside is laid out against a screen rather
|
|
137
|
+
* than against the page: a 390-pixel column wraps where it would wrap on a
|
|
138
|
+
* phone, and the mockup can be 200 pixels wide on the page without the content
|
|
139
|
+
* knowing.
|
|
140
|
+
*
|
|
141
|
+
* That scale is a `transform`, and it is the one place in the library where one
|
|
142
|
+
* is used. The rule it is an exception to is about controls, where a scale
|
|
143
|
+
* resamples a label under the pointer that is pressing it. Nothing here is
|
|
144
|
+
* pressed and the scale never changes on an interaction: it is set once from the
|
|
145
|
+
* space available, which is the only way to draw a 1440-pixel desktop in a
|
|
146
|
+
* paragraph's width at all.
|
|
147
|
+
*
|
|
148
|
+
* The screen is also a container (`neba-screen`), so content inside can answer
|
|
149
|
+
* to the device's width with a container query rather than to the window's.
|
|
150
|
+
*/
|
|
151
|
+
export declare const Mockup: React.ForwardRefExoticComponent<MockupProps & React.RefAttributes<HTMLDivElement>>;
|