native-document 1.0.166 → 1.0.168
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/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +8 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: DataTable
|
|
3
|
+
description: Full-featured data table with sorting, filtering, search, pagination, selection, export, and server-side support
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DataTable
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { DataTable } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
DataTable(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { DataTableRender } from 'native-document/ui';
|
|
18
|
+
import {
|
|
19
|
+
PaginationRender,
|
|
20
|
+
ButtonRender,
|
|
21
|
+
DropdownRender, DropdownItemRender
|
|
22
|
+
} from 'native-document/ui';
|
|
23
|
+
|
|
24
|
+
DataTable.use(DataTableRender);
|
|
25
|
+
Pagination.use(PaginationRender);
|
|
26
|
+
Button.use(ButtonRender);
|
|
27
|
+
Dropdown.use(DropdownRender);
|
|
28
|
+
DropdownItem.use(DropdownItemRender);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Columns
|
|
34
|
+
|
|
35
|
+
### `DataTable.column(key, title, props?, callback?)`
|
|
36
|
+
|
|
37
|
+
| Parameter | Type | Description |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `key` | `string` | Data property name |
|
|
40
|
+
| `title` | `string` | Column header label |
|
|
41
|
+
| `props?` | `object` | HTML attributes for the header cell |
|
|
42
|
+
| `callback?` | `(column: Column) => void` | Configure the column via the Column API |
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
DataTable()
|
|
46
|
+
.column('name', 'Name')
|
|
47
|
+
.column('email', 'Email')
|
|
48
|
+
.column('role', 'Role', {}, (col) => col.sortable().center())
|
|
49
|
+
.column('actions', '', {}, (col) =>
|
|
50
|
+
col.render((value, row) =>
|
|
51
|
+
Button('Edit').small().nd.onClick(() => edit(row))
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `Column` API
|
|
57
|
+
|
|
58
|
+
| Method | Parameters | Description |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `.sortable(fn?)` | `fn?: (a, b) => number` | Enable sorting. Optional custom sort function. |
|
|
61
|
+
| `.searchable()` | - | Include in full-text search |
|
|
62
|
+
| `.hidden()` | - | Hidden by default (user can show it) |
|
|
63
|
+
| `.visible(condition)` | `condition: boolean \| Observable<boolean>` | Reactive visibility |
|
|
64
|
+
| `.align(align)` | `'left' \| 'center' \| 'right'` | Text alignment |
|
|
65
|
+
| `.center()` | - | Shorthand for `.align('center')` |
|
|
66
|
+
| `.right()` | - | Shorthand for `.align('right')` |
|
|
67
|
+
| `.pinned(orientation)` | `'left' \| 'right'` | Pin the column |
|
|
68
|
+
| `.pinnedAtLeft()` | - | Shorthand for `.pinned('left')` |
|
|
69
|
+
| `.pinnedAtRight()` | - | Shorthand for `.pinned('right')` |
|
|
70
|
+
| `.header(template)` | `template: NdChild` | Custom header cell content |
|
|
71
|
+
| `.render(fn)` | `fn: (value, row) => NdChild` | Custom cell renderer |
|
|
72
|
+
| `.value(fn)` | `fn: (row) => *` | Transform the cell value before render |
|
|
73
|
+
| `.onClick(handler)` | `handler: (value, row) => void` | Cell click handler |
|
|
74
|
+
| `.colspan(n)` | `n: number` | Cell colspan |
|
|
75
|
+
| `.rowspan(n)` | `n: number` | Cell rowspan |
|
|
76
|
+
| `.props(props)` | `props: object` | HTML attributes for all cells in this column |
|
|
77
|
+
|
|
78
|
+
### `DataTable.group(title, callback)` - Column Groups
|
|
79
|
+
|
|
80
|
+
Group related columns under a shared header:
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
DataTable()
|
|
84
|
+
.column('name', 'Name')
|
|
85
|
+
.group('Address', (group) => {
|
|
86
|
+
group.column('city', 'City')
|
|
87
|
+
group.column('country', 'Country')
|
|
88
|
+
})
|
|
89
|
+
.data(users)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Data
|
|
95
|
+
|
|
96
|
+
| Method | Parameters | Description |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `.data(data)` | `data: ObservableArray` | Reactive data source |
|
|
99
|
+
| `.total(obs)` | `obs: Observable<number>` | Total count for server-side pagination |
|
|
100
|
+
| `.clientSide()` | - | NativeDocument handles sort, filter, and pagination |
|
|
101
|
+
| `.serverSide()` | - | You handle everything via callbacks |
|
|
102
|
+
| `.loading(obs)` | `obs: Observable<boolean>` | Show loading state |
|
|
103
|
+
| `.error(obs)` | `obs: Observable<*>` | Show error state |
|
|
104
|
+
| `.empty(content)` | `content: NdChild` | Content shown when no rows |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Sorting
|
|
109
|
+
|
|
110
|
+
| Method | Parameters | Description |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `.defaultSort(col, dir?)` | `col: string`, `dir?: 'asc' \| 'desc'` | Initial sort. Default dir: `'asc'` |
|
|
113
|
+
| `.multiSort(enabled?)` | `enabled?: boolean` | Allow sorting by multiple columns |
|
|
114
|
+
| `.onSort(handler)` | `handler: (col, dir, allSorts) => void` | Fires when sort changes |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Search & Filter
|
|
119
|
+
|
|
120
|
+
| Method | Parameters | Description |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `.searchable(enabled?)` | `enabled?: boolean` | Enable search input |
|
|
123
|
+
| `.filterable(enabled?)` | `enabled?: boolean` | Enable column filters |
|
|
124
|
+
| `.defaultFilters(filters)` | `filters: object` | Initial filter values |
|
|
125
|
+
| `.onSearch(handler)` | `handler: (query) => void` | Fires on search input |
|
|
126
|
+
| `.onFilter(handler)` | `handler: (filters) => void` | Fires when filters change |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Pagination
|
|
131
|
+
|
|
132
|
+
| Method | Parameters | Description |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `.pagination(pageSize)` | `pageSize: number` | Enable pagination with given page size |
|
|
135
|
+
| `.pageSizes(sizes)` | `sizes: number[]` | Page size options for the user |
|
|
136
|
+
| `.defaultPage(page)` | `page: number` | Initial page (1-indexed) |
|
|
137
|
+
| `.onPage(handler)` | `handler: (page, pageSize) => void` | Fires on page change |
|
|
138
|
+
| `.goToPage(page)` | `page: number` | Navigate to a specific page programmatically |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Selection
|
|
143
|
+
|
|
144
|
+
| Method | Parameters | Description |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `.selectable(enabled?)` | `enabled?: boolean` | Enable row selection |
|
|
147
|
+
| `.multiSelect(enabled?)` | `enabled?: boolean` | Allow selecting multiple rows |
|
|
148
|
+
| `.selectedRows(obs)` | `obs: ObservableArray` | Bind selected rows to an external observable |
|
|
149
|
+
| `.onSelect(handler)` | `handler: (selectedRows) => void` | Fires when selection changes |
|
|
150
|
+
| `.clearSelection()` | - | Clear all selected rows programmatically |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Inline Editing
|
|
155
|
+
|
|
156
|
+
| Method | Parameters | Description |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `.editable(enabled?)` | `enabled?: boolean` | Enable inline cell editing |
|
|
159
|
+
| `.onEdit(handler)` | `handler: (row, col, value) => void` | Fires when a cell is saved |
|
|
160
|
+
| `.onEditCancel(handler)` | `handler: (row, col) => void` | Fires when editing is cancelled |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Export
|
|
165
|
+
|
|
166
|
+
| Method | Parameters | Description |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `.export(label, format, filename?)` | `label: NdChild`, `format: 'csv' \| 'xlsx' \| string`, `filename?: string` | Add an export button |
|
|
169
|
+
| `.exportFileName(name)` | `name: string` | Default filename for all exports |
|
|
170
|
+
| `.onExport(handler)` | `handler: (format, rows) => void` | Custom export handler |
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
DataTable()
|
|
174
|
+
.export('Export CSV', 'csv', 'users.csv')
|
|
175
|
+
.export('Export Excel', 'xlsx', 'users.xlsx')
|
|
176
|
+
.onExport((format, rows) => console.log(format, rows))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Expandable Rows
|
|
182
|
+
|
|
183
|
+
`.expandable(renderFn, expandedIcon?, collapsedIcon?)` - Render additional content below a row when expanded:
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
DataTable()
|
|
187
|
+
.expandable(
|
|
188
|
+
(row) => Div({ class: 'row-detail' }, [
|
|
189
|
+
P(['Email: ', row.email]),
|
|
190
|
+
P(['Joined: ', row.joinedAt])
|
|
191
|
+
]),
|
|
192
|
+
'▼', // expanded icon
|
|
193
|
+
'▶' // collapsed icon
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Master-Detail
|
|
200
|
+
|
|
201
|
+
`.masterDetail(renderFn)` - Render a full-width detail panel below the selected row:
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
DataTable()
|
|
205
|
+
.masterDetail((row) =>
|
|
206
|
+
Div({ class: 'detail-panel' }, [
|
|
207
|
+
H2(row.name),
|
|
208
|
+
P(row.bio),
|
|
209
|
+
DataTable()
|
|
210
|
+
.data(row.orders)
|
|
211
|
+
.column('id', 'Order ID')
|
|
212
|
+
.column('total', 'Total')
|
|
213
|
+
])
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Bulk Actions
|
|
220
|
+
|
|
221
|
+
`.bulkActions(actions)` - Actions applied to all selected rows. Requires `.selectable()`:
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
DataTable()
|
|
225
|
+
.selectable()
|
|
226
|
+
.multiSelect()
|
|
227
|
+
.bulkActions([
|
|
228
|
+
{ label: 'Delete', action: (rows) => deleteAll(rows) },
|
|
229
|
+
{ label: 'Export', action: (rows) => exportRows(rows) },
|
|
230
|
+
{ label: 'Activate', action: (rows) => activateAll(rows) }
|
|
231
|
+
])
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Row Events
|
|
237
|
+
|
|
238
|
+
| Method | Parameters | Description |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `.onRowClick(handler)` | `handler: (row, event) => void` | Row click |
|
|
241
|
+
| `.onRowDoubleClick(handler)` | `handler: (row, event) => void` | Row double-click |
|
|
242
|
+
| `.onRowHover(handler)` | `handler: (row, event) => void` | Row hover |
|
|
243
|
+
| `.rowProps(fn)` | `fn: (row) => object` | Dynamic HTML attributes per row |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Persistence
|
|
248
|
+
|
|
249
|
+
`.persist(key, options?)` - Save and restore sort, filters, search, page, and column visibility to localStorage:
|
|
250
|
+
|
|
251
|
+
| Option | Default | Description |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| `include` | `['sort', 'filters', 'search', 'page', 'pageSize', 'columns']` | Which state to persist |
|
|
254
|
+
| `storage` | `'localStorage'` | Storage backend |
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
DataTable()
|
|
258
|
+
.persist('users-table')
|
|
259
|
+
.persist('users-table', { include: ['sort', 'pageSize'] })
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Other
|
|
265
|
+
|
|
266
|
+
| Method | Parameters | Description |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| `.labels(labels)` | `labels: object` | Override UI labels (search placeholder, empty text, etc.) |
|
|
269
|
+
| `.layout(fn)` | `fn: ($description, instance) => NdChild` | Fully custom table layout |
|
|
270
|
+
| `.refresh()` | - | Re-emit the current state to trigger a server-side reload |
|
|
271
|
+
| `.clearFilters()` | - | Clear all filters and search |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Server-side Example
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
const users = Observable.array([]);
|
|
279
|
+
const total = Observable(0);
|
|
280
|
+
const isLoading = Observable(false);
|
|
281
|
+
|
|
282
|
+
const fetchUsers = async ({ page = 1, pageSize = 20, sort, search } = {}) => {
|
|
283
|
+
isLoading.set(true);
|
|
284
|
+
const res = await API.users.list({ page, pageSize, sort, search });
|
|
285
|
+
users.set(res.data);
|
|
286
|
+
total.set(res.total);
|
|
287
|
+
isLoading.set(false);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
fetchUsers();
|
|
291
|
+
|
|
292
|
+
DataTable()
|
|
293
|
+
.serverSide()
|
|
294
|
+
.data(users)
|
|
295
|
+
.total(total)
|
|
296
|
+
.loading(isLoading)
|
|
297
|
+
.column('name', 'Name', {}, (col) => col.sortable())
|
|
298
|
+
.column('email', 'Email', {}, (col) => col.sortable())
|
|
299
|
+
.column('role', 'Role')
|
|
300
|
+
.pagination(20)
|
|
301
|
+
.pageSizes([10, 20, 50])
|
|
302
|
+
.searchable()
|
|
303
|
+
.selectable()
|
|
304
|
+
.multiSelect()
|
|
305
|
+
.bulkActions([
|
|
306
|
+
{ label: 'Delete', action: (rows) => deleteUsers(rows) }
|
|
307
|
+
])
|
|
308
|
+
.export('CSV', 'csv', 'users.csv')
|
|
309
|
+
.defaultSort('name', 'asc')
|
|
310
|
+
.persist('users-table')
|
|
311
|
+
.onPage((page, size) => fetchUsers({ page, pageSize: size }))
|
|
312
|
+
.onSort((col, dir) => fetchUsers({ sort: `${col}:${dir}` }))
|
|
313
|
+
.onSearch((query) => fetchUsers({ search: query }))
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Theming
|
|
321
|
+
|
|
322
|
+
```css
|
|
323
|
+
:root {
|
|
324
|
+
--data-table-font-size: var(--description-size);
|
|
325
|
+
--data-table-cell-padding-v: var(--space-cozy);
|
|
326
|
+
--data-table-cell-padding-h: var(--space-comfortable);
|
|
327
|
+
--data-table-border-color: var(--color-border-tertiary);
|
|
328
|
+
--data-table-header-color: var(--color-text-secondary);
|
|
329
|
+
--data-table-header-bg: var(--color-background-secondary);
|
|
330
|
+
--data-table-header-weight: 500;
|
|
331
|
+
--data-table-hover-bg: var(--color-background-secondary);
|
|
332
|
+
--data-table-selected-bg: var(--color-background-info);
|
|
333
|
+
--data-table-toolbar-bg: var(--color-background-secondary);
|
|
334
|
+
--data-table-bulk-bg: var(--color-background-info);
|
|
335
|
+
--data-table-pagination-bg: var(--color-background-secondary);
|
|
336
|
+
--data-table-spinner-size: 24px;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Next Steps
|
|
343
|
+
|
|
344
|
+
- **[SimpleTable](./simple-table.md)** - Lightweight alternative without built-in sort/filter
|
|
345
|
+
- **[Pagination](./pagination.md)** - Standalone pagination component
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dropdown
|
|
3
|
+
description: Dropdown component with keyboard navigation, search, grouping, and flexible trigger binding
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Dropdown
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Dropdown, DropdownItem, DropdownGroup, DropdownDivider, DropdownTrigger } from 'native-document/components';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
When `Dropdown.use()` is called, it automatically adds a `.nd.dropdown()` method to all NDElements and BaseComponents.
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import {
|
|
18
|
+
DropdownRender, DropdownItemRender,
|
|
19
|
+
DropdownGroupRender, DropdownDividerRender
|
|
20
|
+
} from 'native-document/ui';
|
|
21
|
+
|
|
22
|
+
Dropdown.use(DropdownRender);
|
|
23
|
+
DropdownItem.use(DropdownItemRender);
|
|
24
|
+
DropdownGroup.use(DropdownGroupRender);
|
|
25
|
+
DropdownDivider.use(DropdownDividerRender);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `$description`
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
{
|
|
32
|
+
trigger: null,
|
|
33
|
+
items: Observable.array([]),
|
|
34
|
+
position: 'bottom-start',
|
|
35
|
+
interaction: 'click', // 'click' | 'hover' | 'focus'
|
|
36
|
+
offset: [0, 4],
|
|
37
|
+
disabled: Observable(false),
|
|
38
|
+
closeOnSelect: true,
|
|
39
|
+
closeOnClickOutside: true,
|
|
40
|
+
closeOnEscape: true,
|
|
41
|
+
isOpen: Observable(false),
|
|
42
|
+
maxHeight: null,
|
|
43
|
+
searchable: false,
|
|
44
|
+
searchValue: null,
|
|
45
|
+
searchPlaceholder: 'Search...',
|
|
46
|
+
loopOnKeyboard: true,
|
|
47
|
+
props: {} // HTML attributes for the root element
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Adding Items
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
Dropdown()
|
|
55
|
+
.item(DropdownItem().label('Edit').icon(EditIcon).action(() => edit()))
|
|
56
|
+
.item(DropdownItem().label('Delete').danger().action(() => remove()))
|
|
57
|
+
.divider()
|
|
58
|
+
.item(DropdownItem().label('Export').action(() => exportData()))
|
|
59
|
+
.group((group) => {
|
|
60
|
+
group.item(DropdownItem().label('Item A'))
|
|
61
|
+
group.item(DropdownItem().label('Item B'))
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Binding from Data
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const roles = Observable.array(['Admin', 'Editor', 'Viewer']);
|
|
69
|
+
|
|
70
|
+
Dropdown()
|
|
71
|
+
.from(roles) // string array
|
|
72
|
+
|
|
73
|
+
Dropdown()
|
|
74
|
+
.from(users, (user) => DropdownItem().label(user.name).value(user.id).action(() => selectUser(user.id)))
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Trigger
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// Via .nd.dropdown() - attached to the element
|
|
81
|
+
Button('Actions').nd.dropdown(
|
|
82
|
+
Dropdown()
|
|
83
|
+
.item(DropdownItem().label('Edit'))
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Custom trigger component
|
|
87
|
+
Dropdown()
|
|
88
|
+
.trigger(
|
|
89
|
+
DropdownTrigger()
|
|
90
|
+
.content('Options')
|
|
91
|
+
.icon(ChevronIcon)
|
|
92
|
+
.stateOpenIcon(ChevronUpIcon)
|
|
93
|
+
.stateClosedIcon(ChevronDownIcon)
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Behavior
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
.onClicked() // open on click (default)
|
|
101
|
+
.onHovered() // open on hover
|
|
102
|
+
.onFocused() // open on focus
|
|
103
|
+
|
|
104
|
+
.closeOnSelect(false)
|
|
105
|
+
.closeOnClickOutside(false)
|
|
106
|
+
.closeOnEscape(false)
|
|
107
|
+
.maxHeight(300)
|
|
108
|
+
.loopOnKeyboard()
|
|
109
|
+
.matchTriggerWidth()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Search
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
.searchable()
|
|
116
|
+
.searchPlaceholder('Filter...')
|
|
117
|
+
.renderSearch(($description) =>
|
|
118
|
+
Input({ value: $description.searchValue, placeholder: $description.searchPlaceholder })
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
// Custom filter
|
|
122
|
+
.filter((item, search) => item.label.toLowerCase().includes(search), [searchObs])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Events
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
.onChange((item) => console.log('Selected:', item))
|
|
129
|
+
.onOpen(() => console.log('Opened'))
|
|
130
|
+
.onClose(() => console.log('Closed'))
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Custom Renderers
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
.renderItem(($item) => Div({ class: 'item' }, $item.label))
|
|
137
|
+
.renderHeader(() => Div({ class: 'header' }, 'Choose an option'))
|
|
138
|
+
.renderFooter(() => Div({ class: 'footer' }, Link({ href: '/all' }, 'See all')))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## `DropdownItem`
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
DropdownItem()
|
|
147
|
+
.label('Save')
|
|
148
|
+
.icon(SaveIcon)
|
|
149
|
+
.value('save')
|
|
150
|
+
.shortcut('+S')
|
|
151
|
+
.action(() => save())
|
|
152
|
+
.disabled(false)
|
|
153
|
+
.danger()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
See **[ShortcutManager](./shortcut-manager.md)** for the shortcut convention.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## `DropdownGroup`
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
DropdownGroup('File')
|
|
164
|
+
.item(DropdownItem().label('New').shortcut('+N').action(() => newFile()))
|
|
165
|
+
.item(DropdownItem().label('Open').shortcut('+O').action(() => openFile()))
|
|
166
|
+
.divider()
|
|
167
|
+
.item(DropdownItem().label('Save').shortcut('+S').action(() => save()))
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## `DropdownTrigger`
|
|
173
|
+
|
|
174
|
+
A custom trigger component that shows open/close state:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
DropdownTrigger()
|
|
178
|
+
.content('Options')
|
|
179
|
+
.icon(ChevronIcon)
|
|
180
|
+
.stateOpenIcon(ChevronUpIcon)
|
|
181
|
+
.stateClosedIcon(ChevronDownIcon)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Theming
|
|
187
|
+
|
|
188
|
+
```css
|
|
189
|
+
:root {
|
|
190
|
+
--dropdown-bg: var(--background);
|
|
191
|
+
--dropdown-border: var(--gray-lite-3);
|
|
192
|
+
--dropdown-radius: var(--radius-card);
|
|
193
|
+
--dropdown-shadow: var(--shadow-lg);
|
|
194
|
+
--dropdown-min-width: 180px;
|
|
195
|
+
--dropdown-max-width: 320px;
|
|
196
|
+
--dropdown-z-index: 100001;
|
|
197
|
+
--dropdown-font-size: var(--description-size);
|
|
198
|
+
--dropdown-animation-duration: 0.15s;
|
|
199
|
+
--dropdown-arrow-size: 8px;
|
|
200
|
+
--dropdown-item-padding: var(--space-cozy) var(--space-comfortable);
|
|
201
|
+
--dropdown-item-radius: var(--radius-button);
|
|
202
|
+
--dropdown-item-gap: var(--space-cozy);
|
|
203
|
+
--dropdown-item-color: var(--text-color);
|
|
204
|
+
--dropdown-item-color-hover: var(--text-color);
|
|
205
|
+
--dropdown-item-bg-hover: var(--gray-lite-5);
|
|
206
|
+
--dropdown-item-bg-active: var(--gray-lite-4);
|
|
207
|
+
--dropdown-item-color-disabled: var(--gray-lite-2);
|
|
208
|
+
--dropdown-group-label-size: var(--note-size);
|
|
209
|
+
--dropdown-group-label-color: var(--gray);
|
|
210
|
+
--dropdown-group-label-padding: var(--space-cozy) var(--space-comfortable);
|
|
211
|
+
--dropdown-search-padding: var(--space-cozy) var(--space-comfortable);
|
|
212
|
+
--dropdown-search-border: var(--gray-lite-3);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AutocompleteField
|
|
3
|
+
description: Text input with suggestions from a data source, with debounce and async support
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AutocompleteField
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { AutocompleteField } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
AutocompleteField(name, props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { AutocompleteFieldRender } from 'native-document/ui';
|
|
18
|
+
|
|
19
|
+
AutocompleteField.use(AutocompleteFieldRender);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Methods
|
|
23
|
+
|
|
24
|
+
| Method | Parameters | Description |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `.source(dataSource)` | `dataSource: *[] \| ObservableArray \| (query: string) => Promise<*[]>` | Static array, reactive array, or async function |
|
|
27
|
+
| `.labelKey(key)` | `key: string` | Property to display in the suggestion list. Default `'label'` |
|
|
28
|
+
| `.valueKey(key)` | `key: string` | Property to use as the bound value. Default `'id'` |
|
|
29
|
+
| `.minChars(n)` | `n: number` | Minimum characters before triggering source. Default `2` |
|
|
30
|
+
| `.debounce(ms)` | `ms: number` | Wait after typing stops before calling source. Default `300` |
|
|
31
|
+
| `.maxResults(n)` | `n: number` | Maximum number of suggestions shown. Default `10` |
|
|
32
|
+
| `.clearable()` | - | Show a clear button |
|
|
33
|
+
| `.required(message?)` | `message?: string` | Validation - field required |
|
|
34
|
+
| `.oneOf(values, message?)` | `values: *[]`, `message?: string` | Restrict value to a predefined list |
|
|
35
|
+
| `.onSelect(handler)` | `handler: (item) => void` | Fired when a suggestion is selected |
|
|
36
|
+
| `.renderItem(fn)` | `fn: (item) => NdChild` | Custom suggestion item renderer |
|
|
37
|
+
|
|
38
|
+
## Static source example
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const country = Observable(null);
|
|
42
|
+
|
|
43
|
+
AutocompleteField('country')
|
|
44
|
+
.label('Country')
|
|
45
|
+
.model(country)
|
|
46
|
+
.source(Observable.array(countries))
|
|
47
|
+
.labelKey('name')
|
|
48
|
+
.valueKey('code')
|
|
49
|
+
.placeholder('Search country...')
|
|
50
|
+
.minChars(1)
|
|
51
|
+
.required()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Async source example
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const userId = Observable(null);
|
|
58
|
+
|
|
59
|
+
AutocompleteField('user')
|
|
60
|
+
.label('Assign to')
|
|
61
|
+
.model(userId)
|
|
62
|
+
.source(async (query) => {
|
|
63
|
+
const res = await fetch(`/api/users?q=${query}`);
|
|
64
|
+
return res.json();
|
|
65
|
+
})
|
|
66
|
+
.labelKey('name')
|
|
67
|
+
.valueKey('id')
|
|
68
|
+
.minChars(2)
|
|
69
|
+
.debounce(300)
|
|
70
|
+
.maxResults(8)
|
|
71
|
+
.renderItem(($user) =>
|
|
72
|
+
HStack([
|
|
73
|
+
Avatar($user.avatar).small(),
|
|
74
|
+
VStack([
|
|
75
|
+
Span($user.name),
|
|
76
|
+
Span({ class: 'text-sm text-muted' }, $user.email)
|
|
77
|
+
]).spacing(2)
|
|
78
|
+
]).spacing(8)
|
|
79
|
+
)
|
|
80
|
+
.onSelect((item) => console.log('Selected:', item.name))
|
|
81
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CheckboxField
|
|
3
|
+
description: Single checkbox with two-way boolean binding
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CheckboxField
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { CheckboxField } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
CheckboxField(name, props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { CheckboxFieldRender } from 'native-document/ui';
|
|
18
|
+
|
|
19
|
+
CheckboxField.use(CheckboxFieldRender);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Methods
|
|
23
|
+
|
|
24
|
+
| Method | Parameters | Description |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `.model(observable)` | `observable: Observable<boolean>` | Two-way boolean binding |
|
|
27
|
+
| `.label(text)` | `text: NdChild` | Label displayed next to the checkbox |
|
|
28
|
+
| `.disabled(val)` | `val: boolean \| Observable<boolean>` | Disable the field |
|
|
29
|
+
| `.required(message?)` | `message?: string` | Validation - field required |
|
|
30
|
+
| `.checked()` | - | Returns the current checked value (getter) |
|
|
31
|
+
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const agreed = Observable(false);
|
|
36
|
+
|
|
37
|
+
CheckboxField('terms')
|
|
38
|
+
.label('I agree to the terms and conditions')
|
|
39
|
+
.model(agreed)
|
|
40
|
+
.required('You must accept the terms')
|
|
41
|
+
```
|