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.
Files changed (108) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.js +2 -1
  4. package/dist/native-document.components.min.js +495 -228
  5. package/dist/native-document.dev.js +7 -0
  6. package/dist/native-document.dev.js.map +1 -1
  7. package/dist/native-document.min.js +1 -1
  8. package/docs/advanced-components.md +213 -608
  9. package/docs/anchor.md +173 -312
  10. package/docs/cache.md +95 -803
  11. package/docs/cli.md +179 -0
  12. package/docs/components/accordion.md +172 -0
  13. package/docs/components/alert.md +99 -0
  14. package/docs/components/avatar.md +160 -0
  15. package/docs/components/badge.md +102 -0
  16. package/docs/components/breadcrumb.md +89 -0
  17. package/docs/components/button.md +183 -0
  18. package/docs/components/card.md +69 -0
  19. package/docs/components/context-menu.md +118 -0
  20. package/docs/components/data-table.md +345 -0
  21. package/docs/components/dropdown.md +214 -0
  22. package/docs/components/form/autocomplete-field.md +81 -0
  23. package/docs/components/form/checkbox-field.md +41 -0
  24. package/docs/components/form/checkbox-group-field.md +54 -0
  25. package/docs/components/form/color-field.md +64 -0
  26. package/docs/components/form/date-field.md +92 -0
  27. package/docs/components/form/field-collection.md +63 -0
  28. package/docs/components/form/file-field.md +203 -0
  29. package/docs/components/form/form-control.md +87 -0
  30. package/docs/components/form/image-field.md +90 -0
  31. package/docs/components/form/index.md +115 -0
  32. package/docs/components/form/number-field.md +65 -0
  33. package/docs/components/form/radio-field.md +51 -0
  34. package/docs/components/form/select-field.md +123 -0
  35. package/docs/components/form/slider.md +136 -0
  36. package/docs/components/form/string-field.md +134 -0
  37. package/docs/components/form/textarea-field.md +65 -0
  38. package/docs/components/form-fields.md +372 -0
  39. package/docs/components/getting-started.md +264 -0
  40. package/docs/components/index.md +337 -0
  41. package/docs/components/layout.md +279 -0
  42. package/docs/components/list.md +73 -0
  43. package/docs/components/menu.md +215 -0
  44. package/docs/components/modal.md +156 -0
  45. package/docs/components/pagination.md +95 -0
  46. package/docs/components/popover.md +131 -0
  47. package/docs/components/progress.md +111 -0
  48. package/docs/components/shortcut-manager.md +221 -0
  49. package/docs/components/simple-table.md +107 -0
  50. package/docs/components/skeleton.md +155 -0
  51. package/docs/components/spinner.md +100 -0
  52. package/docs/components/splitter.md +133 -0
  53. package/docs/components/stepper.md +163 -0
  54. package/docs/components/switch.md +113 -0
  55. package/docs/components/tabs.md +153 -0
  56. package/docs/components/toast.md +119 -0
  57. package/docs/components/tooltip.md +151 -0
  58. package/docs/components/traits.md +261 -0
  59. package/docs/conditional-rendering.md +170 -588
  60. package/docs/contributing.md +300 -25
  61. package/docs/core-concepts.md +205 -374
  62. package/docs/elements.md +251 -367
  63. package/docs/extending-native-document-element.md +192 -207
  64. package/docs/filters.md +153 -1122
  65. package/docs/getting-started.md +193 -267
  66. package/docs/i18n.md +241 -0
  67. package/docs/index.md +76 -0
  68. package/docs/lifecycle-events.md +143 -75
  69. package/docs/list-rendering.md +227 -852
  70. package/docs/memory-management.md +134 -47
  71. package/docs/native-document-element.md +337 -186
  72. package/docs/native-fetch.md +99 -630
  73. package/docs/observable-resource.md +364 -0
  74. package/docs/observables.md +592 -526
  75. package/docs/routing.md +244 -653
  76. package/docs/state-management.md +134 -241
  77. package/docs/svg-elements.md +231 -0
  78. package/docs/theming.md +409 -0
  79. package/docs/tutorials/.gitkeep +0 -0
  80. package/docs/validation.md +95 -97
  81. package/docs/vitepress-conventions.md +219 -0
  82. package/package.json +34 -13
  83. package/readme.md +269 -89
  84. package/src/components/card/Card.js +93 -39
  85. package/src/components/card/index.js +1 -1
  86. package/src/components/list/HasListItem.js +171 -0
  87. package/src/components/list/List.js +41 -107
  88. package/src/components/list/ListDivider.js +39 -0
  89. package/src/components/list/ListGroup.js +76 -59
  90. package/src/components/list/ListItem.js +117 -69
  91. package/src/components/list/index.js +3 -1
  92. package/src/components/list/types/ListItem.d.ts +45 -34
  93. package/src/components/spacer/Spacer.js +1 -1
  94. package/src/core/data/ObservableResource.js +5 -0
  95. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  96. package/src/ui/components/card/CardRender.js +133 -0
  97. package/src/ui/components/card/card.css +169 -0
  98. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  99. package/src/ui/components/list/ListRender.js +18 -0
  100. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  101. package/src/ui/components/list/divider/list-divider.css +12 -0
  102. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  103. package/src/ui/components/list/group/list-group.css +62 -0
  104. package/src/ui/components/list/item/ListItemRender.js +238 -0
  105. package/src/ui/components/list/item/list-item.css +191 -0
  106. package/src/ui/components/list/list.css +24 -0
  107. package/src/ui/components/spacer/SpacerRender.js +10 -0
  108. 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
+ ```