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 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
- Recent additions: the **Animate** family eleven wrappers that make anything move (**AnimateFade**, **AnimateGrow**, **AnimateZoom**, **AnimateSlide**, **AnimateRotate**, **AnimateBlink**, plus **AnimateAppear**, **AnimateTyping**, **AnimateLighting**, **AnimateMarquee** and **AnimateHeadline** for the effects that have to know what their children are), each taking the same settings for duration, delay, repeat and what triggers it; the six named effects are also a `transition` prop on the components that display something, and every one of them is switched off entirely by a reduced-motion preference. Alongside them, **ColorPicker** — a saturation square, a hue rail, an optional opacity rail and a grid of swatches, reading and writing hex, `rgb()` and `hsl()` with no colour library underneath. Before those, **Drawer** (a panel on one edge of the window, either floating over the page or fixed in the layout — one component, one `mode` prop apart), **Popover** (a sheet anchored to the control that opened it, holding content that can actually be clicked and typed into), **Skeleton** (the shape of something that has not loaded yet, so the page does not reflow when it arrives) and **AspectRatio** (a box that keeps a proportion whatever width it is given); before those, **ChatBubble** (one message in a conversation, where the avatar, the time, the delivery mark, the media and the link card are each drawn only when given something), **Spoiler** (content covered by a blur rather than hidden, so a reader can see there is something there without reading it by accident), **TextLink** (a link with no surface of its own, and a mark for the ones that take over the window), **Avatar** (falls back to initials, a glyph or a silhouette, so it is never an empty box), **TreeView**, **Breadcrumb**, **Panes** (regions with a draggable bar between each pair) and **OtpField**; before those, **Blockquote**, **Shortcut**, **Highlight**, **SegmentedButton** and **Timeline**, and the four pickers — **DatePicker**, **TimePicker**, **DateTimePicker** and **DateRangePicker** — which take and return a plain `Date` and add no date library to your bundle.
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,2 @@
1
+ export { DataTable } from './DataTable';
2
+ export type { DataTableColumn, DataTablePaging, DataTableProps, DataTableSelectionMode, DataTableSort, DataTableSortDirection, DataTableStage, DataTableStripe } from './DataTable';
@@ -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,2 @@
1
+ export { Empty } from './Empty';
2
+ export type { EmptyProps } from './Empty';
@@ -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>>;