native-document 1.0.166 → 1.0.169
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 +187 -0
- package/components.js +4 -1
- package/dist/native-document.components.min.js +593 -300
- package/dist/native-document.dev.js +99 -75
- 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/icons.md +321 -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/form/field/types/FileField.js +3 -3
- package/src/components/icon/Icon.js +107 -0
- package/src/components/icon/icon-getters.js +142 -0
- package/src/components/icon/icons.js +171 -0
- package/src/components/icon/index.js +17 -0
- package/src/components/icon/types/Icon.d.ts +191 -0
- package/src/components/index.d.ts +6 -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/List.d.ts +45 -32
- package/src/components/list/types/ListDivider.ts +16 -0
- package/src/components/list/types/ListGroup.d.ts +51 -29
- package/src/components/list/types/ListItem.d.ts +40 -30
- 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/core/elements/content-formatter.js +16 -2
- package/src/core/elements/form.js +1 -1
- package/src/core/elements/img.js +1 -1
- package/src/core/elements/medias.js +2 -2
- package/src/core/elements/meta-data.js +1 -1
- package/src/core/wrappers/AttributesWrapper.js +37 -22
- package/src/core/wrappers/ElementCreator.js +9 -16
- package/src/core/wrappers/HtmlElementWrapper.js +26 -7
- package/src/core/wrappers/NDElement.js +12 -1
- package/src/core/wrappers/prototypes/attributes-extensions.js +24 -24
- package/src/core/wrappers/prototypes/nd-element-extensions.js +1 -8
- 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/icon/material/MaterialIconRender.js +71 -0
- package/src/ui/components/icon/material/material.css +15 -0
- package/src/ui/components/icon/material/material.map.js +170 -0
- package/src/ui/components/icon/phosphor/PhosphorIconRender.js +71 -0
- package/src/ui/components/icon/phosphor/phosphor.css +17 -0
- package/src/ui/components/icon/phosphor/phosphor.map.js +165 -0
- package/src/ui/components/icon/tabler/TablerIconRender.js +59 -0
- package/src/ui/components/icon/tabler/tabler.css +14 -0
- package/src/ui/components/icon/tabler/tabler.map.js +165 -0
- 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 +13 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pagination
|
|
3
|
+
description: Standalone pagination component with first/last, ellipsis, and custom renderers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pagination
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Pagination } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Pagination(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Standalone pagination. Also built into `DataTable`.
|
|
15
|
+
|
|
16
|
+
## Default Renderer
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { PaginationRender } from 'native-document/ui';
|
|
20
|
+
|
|
21
|
+
Pagination.use(PaginationRender);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Methods
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// State
|
|
28
|
+
.currentPage(Observable(1))
|
|
29
|
+
.totalPages(Observable(10))
|
|
30
|
+
.totalItems(Observable(200))
|
|
31
|
+
.pageSize(20)
|
|
32
|
+
.disabled(Observable(false))
|
|
33
|
+
|
|
34
|
+
// Display
|
|
35
|
+
.siblingCount(1) // pages shown around current page
|
|
36
|
+
.boundaryCount(1) // pages shown at start and end
|
|
37
|
+
.showFirstLast()
|
|
38
|
+
.showPreviousNext()
|
|
39
|
+
|
|
40
|
+
// Navigation (programmatic)
|
|
41
|
+
.goToPage(3)
|
|
42
|
+
.next()
|
|
43
|
+
.previous()
|
|
44
|
+
.first()
|
|
45
|
+
.last()
|
|
46
|
+
.hasNext()
|
|
47
|
+
.hasPrevious()
|
|
48
|
+
|
|
49
|
+
// Events
|
|
50
|
+
.onPageChange((page) => fetchData(page))
|
|
51
|
+
.onChange((page) => fetchData(page)) // alias
|
|
52
|
+
|
|
53
|
+
// Custom renderers
|
|
54
|
+
.renderPage(($page) => Button($page.label))
|
|
55
|
+
.renderEllipsis(() => Span('...'))
|
|
56
|
+
.renderPrevious(() => Span('Prev'))
|
|
57
|
+
.renderNext(() => Span('Next'))
|
|
58
|
+
.renderFirst(() => Span('First'))
|
|
59
|
+
.renderLast(() => Span('Last'))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Example
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
const page = Observable(1);
|
|
66
|
+
const total = Observable(50);
|
|
67
|
+
|
|
68
|
+
Pagination()
|
|
69
|
+
.currentPage(page)
|
|
70
|
+
.totalPages(total)
|
|
71
|
+
.siblingCount(2)
|
|
72
|
+
.showFirstLast()
|
|
73
|
+
.onPageChange((p) => {
|
|
74
|
+
page.set(p);
|
|
75
|
+
fetchUsers(p);
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Theming
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
:root {
|
|
85
|
+
--pagination-gap: var(--space-cozy);
|
|
86
|
+
--pagination-font-size: var(--description-size);
|
|
87
|
+
--page-size: 36px;
|
|
88
|
+
--page-radius: var(--radius-button);
|
|
89
|
+
--page-color: var(--text-color);
|
|
90
|
+
--page-bg-hover: var(--gray-lite-5);
|
|
91
|
+
--page-bg-active: var(--color-primary);
|
|
92
|
+
--page-color-active: var(--white);
|
|
93
|
+
--page-color-disabled: var(--gray-lite-2);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Popover
|
|
3
|
+
description: Floating panel anchored to a trigger element with flexible positioning and interaction modes
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Popover
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Popover } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Popover(content, props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A floating panel anchored to a trigger element. Lighter than Modal - no overlay, positions relative to trigger.
|
|
15
|
+
|
|
16
|
+
## Default Renderer
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { PopoverRender } from 'native-document/ui';
|
|
20
|
+
|
|
21
|
+
Popover.use(PopoverRender);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## `$description`
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
{
|
|
28
|
+
content: null,
|
|
29
|
+
header: null,
|
|
30
|
+
footer: null,
|
|
31
|
+
trigger: null,
|
|
32
|
+
interaction: 'click', // 'click' | 'hover' | 'focus'
|
|
33
|
+
position: 'bottom',
|
|
34
|
+
offset: [0, 8],
|
|
35
|
+
arrow: false,
|
|
36
|
+
shift: false,
|
|
37
|
+
closeOnEscape: true,
|
|
38
|
+
closeOnClickOutside: true,
|
|
39
|
+
focusTrap: false,
|
|
40
|
+
returnFocus: true,
|
|
41
|
+
matchTriggerWidth: null,
|
|
42
|
+
updatePositionOn: null,
|
|
43
|
+
isOpen: Observable(false),
|
|
44
|
+
props: {}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Methods
|
|
49
|
+
|
|
50
|
+
| Method | Parameters | Description |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `.content(element)` | `element: NdChild` | Popover body |
|
|
53
|
+
| `.header(element)` | `element: NdChild` | Popover header |
|
|
54
|
+
| `.footer(element)` | `element: NdChild` | Popover footer |
|
|
55
|
+
| `.trigger(element)` | `element: HTMLElement` | Element that opens the popover |
|
|
56
|
+
| `.onClicked()` | - | Open on click (default) |
|
|
57
|
+
| `.onHovered()` | - | Open on hover |
|
|
58
|
+
| `.onFocused()` | - | Open on focus |
|
|
59
|
+
| `.position(pos)` | `pos: string` | Position relative to trigger: `top`, `bottom`, `left`, `right`, `top-start`, `bottom-end`, etc. |
|
|
60
|
+
| `.offset([x, y])` | `[x: number, y: number]` | Offset from trigger in px |
|
|
61
|
+
| `.arrow()` | - | Show arrow pointing to trigger |
|
|
62
|
+
| `.shift()` | - | Keep popover in viewport when near edges |
|
|
63
|
+
| `.closeOnEscape(enabled)` | `enabled: boolean` | Default `true` |
|
|
64
|
+
| `.closeOnClickOutside(enabled)` | `enabled: boolean` | Default `true` |
|
|
65
|
+
| `.focusTrap()` | - | Trap focus inside the popover |
|
|
66
|
+
| `.returnFocus(enabled)` | `enabled: boolean` | Return focus to trigger on close. Default `true` |
|
|
67
|
+
| `.matchTriggerWidth()` | - | Set popover width to match the trigger width |
|
|
68
|
+
| `.updatePositionOn(observable)` | `observable: Observable` | Recalculate position when the observable changes |
|
|
69
|
+
| `.open()` | - | Open programmatically |
|
|
70
|
+
| `.close()` | - | Close programmatically |
|
|
71
|
+
| `.toggle()` | - | Toggle open/close |
|
|
72
|
+
|
|
73
|
+
## Example
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
Popover(
|
|
77
|
+
VStack([
|
|
78
|
+
Avatar(user.avatar).medium(),
|
|
79
|
+
Div(user.name),
|
|
80
|
+
Div(user.email),
|
|
81
|
+
Divider(),
|
|
82
|
+
Button('View profile')
|
|
83
|
+
.ghost()
|
|
84
|
+
.block()
|
|
85
|
+
.nd.onClick(() => {
|
|
86
|
+
userPopover.close();
|
|
87
|
+
Router.push({ name: 'profile' });
|
|
88
|
+
})
|
|
89
|
+
]).spacing(8)
|
|
90
|
+
)
|
|
91
|
+
.position('bottom-end')
|
|
92
|
+
.arrow()
|
|
93
|
+
.trigger(Button('Show Profile').warning())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Tooltip vs Popover
|
|
99
|
+
|
|
100
|
+
| | Tooltip | Popover |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| **Purpose** | Short contextual hint | Rich floating panel |
|
|
103
|
+
| **Content** | Text or simple element | Header, body, footer |
|
|
104
|
+
| **Triggered by** | Hover (default) | Click (default) |
|
|
105
|
+
| **Focus trap** | No | Optional |
|
|
106
|
+
| **Use when** | Labeling an icon, short help text | User profile card, settings panel |
|
|
107
|
+
|
|
108
|
+
See **[Tooltip](./tooltip.md)** for the lightweight alternative.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Theming
|
|
114
|
+
|
|
115
|
+
```css
|
|
116
|
+
:root {
|
|
117
|
+
--popover-bg: var(--background);
|
|
118
|
+
--popover-border: var(--gray-lite-3);
|
|
119
|
+
--popover-radius: var(--radius-card);
|
|
120
|
+
--popover-shadow: var(--shadow-lg);
|
|
121
|
+
--popover-padding: 0;
|
|
122
|
+
--popover-min-width: 200px;
|
|
123
|
+
--popover-max-width: 320px;
|
|
124
|
+
--popover-z-index: 100001;
|
|
125
|
+
--popover-font-size: var(--description-size);
|
|
126
|
+
--popover-header-size: var(--text-size);
|
|
127
|
+
--popover-header-weight: 600;
|
|
128
|
+
--popover-arrow-size: 8px;
|
|
129
|
+
--popover-animation-duration: 0.15s;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Progress
|
|
3
|
+
description: Progress bar and circular progress indicator
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Progress
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Progress } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Progress(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { ProgressRender } from 'native-document/ui';
|
|
18
|
+
|
|
19
|
+
Progress.use(ProgressRender);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## `$description`
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
{
|
|
26
|
+
value: null, // number | Observable<number>
|
|
27
|
+
type: null, // 'linear' | 'circular'
|
|
28
|
+
variant: null,
|
|
29
|
+
max: 100,
|
|
30
|
+
size: null,
|
|
31
|
+
stroke: null, // circular stroke width
|
|
32
|
+
height: null, // linear height
|
|
33
|
+
showValue: null,
|
|
34
|
+
showPercentage: null,
|
|
35
|
+
label: null,
|
|
36
|
+
format: null, // (value, max) => string
|
|
37
|
+
indeterminate: null,
|
|
38
|
+
striped: null,
|
|
39
|
+
animated: null,
|
|
40
|
+
borderRadiusType: null,
|
|
41
|
+
props: {}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Methods
|
|
46
|
+
|
|
47
|
+
| Method | Parameters | Description |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `.value(val)` | `val: number \| Observable<number>` | Current value |
|
|
50
|
+
| `.max(val)` | `val: number` | Maximum value. Default `100` |
|
|
51
|
+
| `.type(type)` | `'linear' \| 'circular'` | Progress type |
|
|
52
|
+
| `.primary()` | - | Primary color variant |
|
|
53
|
+
| `.success()` | - | Success color variant |
|
|
54
|
+
| `.danger()` | - | Danger color variant |
|
|
55
|
+
| `.warning()` | - | Warning color variant |
|
|
56
|
+
| `.size(size)` | `'small' \| 'large'` | Size preset |
|
|
57
|
+
| `.height(px)` | `px: number` | Height in px (linear) |
|
|
58
|
+
| `.stroke(px)` | `px: number` | Stroke width in px (circular) |
|
|
59
|
+
| `.showValue()` | - | Display the numeric value |
|
|
60
|
+
| `.showPercentage()` | - | Display the percentage |
|
|
61
|
+
| `.label(text)` | `text: string` | Label beside the bar |
|
|
62
|
+
| `.format(fn)` | `fn: (value, max) => string` | Custom value formatter |
|
|
63
|
+
| `.indeterminate()` | - | Indeterminate animation (no known value) |
|
|
64
|
+
| `.striped()` | - | Striped style |
|
|
65
|
+
| `.animated()` | - | Animate the stripes |
|
|
66
|
+
| `.rounded()` | - | Rounded border radius |
|
|
67
|
+
| `.pill()` | - | Pill border radius |
|
|
68
|
+
| `.onComplete(handler)` | `handler: () => void` | Fires once when value reaches max |
|
|
69
|
+
|
|
70
|
+
## Example
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const progress = Observable(0);
|
|
74
|
+
|
|
75
|
+
Progress()
|
|
76
|
+
.value(progress)
|
|
77
|
+
.primary()
|
|
78
|
+
.showPercentage()
|
|
79
|
+
.striped()
|
|
80
|
+
.animated()
|
|
81
|
+
.onComplete(() => {
|
|
82
|
+
Toast('Upload complete!').success().show();
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Theming
|
|
90
|
+
|
|
91
|
+
```css
|
|
92
|
+
:root {
|
|
93
|
+
--progress-height-small: 4px;
|
|
94
|
+
--progress-height-medium: 8px;
|
|
95
|
+
--progress-height-large: 14px;
|
|
96
|
+
--progress-color: var(--gray-lite-4);
|
|
97
|
+
--progress-color-primary: var(--color-primary);
|
|
98
|
+
--progress-color-secondary: var(--color-secondary);
|
|
99
|
+
--progress-color-success: var(--color-success);
|
|
100
|
+
--progress-color-danger: var(--color-danger);
|
|
101
|
+
--progress-color-warning: var(--color-warning);
|
|
102
|
+
--progress-color-info: var(--color-info);
|
|
103
|
+
--progress-circle-size: 80px;
|
|
104
|
+
--progress-circle-thickness: 6px;
|
|
105
|
+
--progress-label-size: var(--note-size);
|
|
106
|
+
--progress-label-color: var(--gray);
|
|
107
|
+
--progress-stripe-size: 20px;
|
|
108
|
+
--progress-animation-duration: 1s;
|
|
109
|
+
--progress-indeterminate-duration: 1.5s;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: ShortcutManager
|
|
3
|
+
description: Register, display, and manage keyboard shortcuts with OS-aware rendering and context scoping
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ShortcutManager
|
|
7
|
+
|
|
8
|
+
`ShortcutManager` handles global and context-scoped keyboard shortcuts. It parses shortcut strings, renders them correctly per OS (Mac symbols or Windows labels), and dispatches handlers on keydown.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
import { ShortcutManager } from 'native-document';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Initialization
|
|
17
|
+
|
|
18
|
+
Call `init()` once at app startup to activate the global keyboard listener:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
ShortcutManager.init();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Shortcut Conventions
|
|
27
|
+
|
|
28
|
+
Two formats are supported:
|
|
29
|
+
|
|
30
|
+
### Short convention
|
|
31
|
+
|
|
32
|
+
Starts with `+`. The first `+` means Ctrl/Cmd (meta):
|
|
33
|
+
|
|
34
|
+
| Shortcut | Meaning |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `'+S'` | Ctrl/Cmd + S |
|
|
37
|
+
| `'++S'` | Ctrl/Cmd + Alt + S |
|
|
38
|
+
| `'+Shift+S'` | Ctrl/Cmd + Shift + S |
|
|
39
|
+
| `'+Alt+S'` | Ctrl/Cmd + Alt + S |
|
|
40
|
+
|
|
41
|
+
### Standard convention
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
'Ctrl+S'
|
|
45
|
+
'Cmd+Shift+S'
|
|
46
|
+
'Alt+F4'
|
|
47
|
+
'Ctrl+Alt+Delete'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Both formats are equivalent and can be mixed freely.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## OS-Aware Display
|
|
55
|
+
|
|
56
|
+
`ShortcutManager.display(shortcut)` formats a shortcut string for the current OS:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
ShortcutManager.display('+S')
|
|
60
|
+
// Mac: "⌘ S"
|
|
61
|
+
// Windows: "Ctrl+S"
|
|
62
|
+
|
|
63
|
+
ShortcutManager.display('+Shift+S')
|
|
64
|
+
// Mac: "⌘ ⇧ S"
|
|
65
|
+
// Windows: "Ctrl+Shift+S"
|
|
66
|
+
|
|
67
|
+
ShortcutManager.display('++S')
|
|
68
|
+
// Mac: "⌘ ⌥ S"
|
|
69
|
+
// Windows: "Ctrl+Alt+S"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Mac symbols:
|
|
73
|
+
|
|
74
|
+
| Key | Symbol |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Meta / Cmd | `⌘` |
|
|
77
|
+
| Shift | `⇧` |
|
|
78
|
+
| Alt / Option | `⌥` |
|
|
79
|
+
| Ctrl | `⌃` |
|
|
80
|
+
|
|
81
|
+
Use `display()` in your renderer to show the shortcut label next to menu items:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
MenuItem.use(($description) => {
|
|
85
|
+
return Div({ class: 'menu-item' }, [
|
|
86
|
+
$description.label,
|
|
87
|
+
$description.shortcut
|
|
88
|
+
? Span({ class: 'shortcut' }, ShortcutManager.display($description.shortcut))
|
|
89
|
+
: null
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Registering Shortcuts
|
|
97
|
+
|
|
98
|
+
### `ShortcutManager.register(shortcut, handler, options?)`
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
ShortcutManager.register('+S', () => save(), { source: 'editor' });
|
|
102
|
+
ShortcutManager.register('+Z', () => undo(), { source: 'editor' });
|
|
103
|
+
ShortcutManager.register('+Shift+Z', () => redo(), { source: 'editor' });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Options:
|
|
107
|
+
|
|
108
|
+
| Option | Type | Default | Description |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| `context` | `string` | `'global'` | Scope the shortcut to a named context |
|
|
111
|
+
| `source` | `string` | `'unknown'` | Label used in conflict warnings |
|
|
112
|
+
| `force` | `boolean` | `false` | Override an existing registration without warning |
|
|
113
|
+
|
|
114
|
+
### `ShortcutManager.unregister(shortcut, context?)`
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
ShortcutManager.unregister('+S');
|
|
118
|
+
ShortcutManager.unregister('+S', 'editor');
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `ShortcutManager.has(shortcut, context?)`
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
if (!ShortcutManager.has('+S')) {
|
|
125
|
+
ShortcutManager.register('+S', () => save());
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Contexts
|
|
132
|
+
|
|
133
|
+
Contexts let you scope shortcuts so the same key combination can do different things in different parts of the app. A `'global'` shortcut always fires. A context shortcut only fires when that context is active (you control activation logic):
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
// Global - always active
|
|
137
|
+
ShortcutManager.register('+S', () => save(), {
|
|
138
|
+
context: 'global',
|
|
139
|
+
source: 'app'
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Scoped - only active in the modal context
|
|
143
|
+
ShortcutManager.register('Escape', () => closeModal(), {
|
|
144
|
+
context: 'modal',
|
|
145
|
+
source: 'modal'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Unregister when leaving context
|
|
149
|
+
ShortcutManager.unregister('Escape', 'modal');
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Conflict Handling
|
|
155
|
+
|
|
156
|
+
Registering the same shortcut in the same context twice logs a warning:
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
ShortcutManager.register('+S', () => save(), { source: 'editor' });
|
|
160
|
+
ShortcutManager.register('+S', () => download(), { source: 'toolbar' });
|
|
161
|
+
// warn: "+S" is already registered by "editor" in context "global". Use { force: true } to override.
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Use `force: true` to override without warning:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
ShortcutManager.register('+S', () => download(), { force: true, source: 'toolbar' });
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Usage with Menu Items
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
import { MenuItem } from 'native-document/components';
|
|
176
|
+
|
|
177
|
+
MenuItem()
|
|
178
|
+
.label('Save')
|
|
179
|
+
.icon(SaveIcon)
|
|
180
|
+
.shortcut('+S')
|
|
181
|
+
.action(() => save())
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The shortcut string is passed as-is to `ShortcutManager.display()` by the renderer to show the OS-formatted label.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Integration with Dropdown and Context Menu
|
|
189
|
+
|
|
190
|
+
`DropdownItem` and `ContextMenuItem` both accept a `.shortcut()` method that passes the string directly to `ShortcutManager.display()` in the renderer:
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
import { DropdownItem } from 'native-document/components';
|
|
194
|
+
|
|
195
|
+
DropdownItem()
|
|
196
|
+
.label('Save')
|
|
197
|
+
.icon(SaveIcon)
|
|
198
|
+
.shortcut('+S')
|
|
199
|
+
.action(() => save())
|
|
200
|
+
|
|
201
|
+
// ContextMenuItem works the same way
|
|
202
|
+
ContextMenuItem()
|
|
203
|
+
.label('Delete')
|
|
204
|
+
.shortcut('Delete')
|
|
205
|
+
.danger()
|
|
206
|
+
.action((data) => deleteRow(data))
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The shortcut label is display-only in Dropdown and ContextMenu items - they do not automatically register the shortcut with `ShortcutManager`. Register shortcuts separately if you want them to fire on keydown:
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
ShortcutManager.register('+S', () => save(), { source: 'toolbar' });
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Next Steps
|
|
218
|
+
|
|
219
|
+
- **[Menu](./menu.md)** - Menu with shortcut labels
|
|
220
|
+
- **[Dropdown](./dropdown.md)** - Dropdown items with shortcuts
|
|
221
|
+
- **[Context Menu](./context-menu.md)** - Context menu items with shortcuts
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SimpleTable
|
|
3
|
+
description: Lightweight table component for static or observable data with custom cell renderers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SimpleTable
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { SimpleTable } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
SimpleTable(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A lightweight table for static or observable data.
|
|
15
|
+
|
|
16
|
+
## Default Renderer
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { SimpleTableRender } from 'native-document/ui';
|
|
20
|
+
|
|
21
|
+
SimpleTable.use(SimpleTableRender);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Methods
|
|
25
|
+
|
|
26
|
+
| Method | Parameters | Description |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `.column(key, title, props?, callback?)` | `key: string`, `title: string`, `props?: object`, `callback?: (value, row) => element` | Add a column. `callback` renders a custom cell. |
|
|
29
|
+
| `.group(title, fn)` | `title: string`, `fn: (group) => void` | Group columns under a shared header. |
|
|
30
|
+
| `.data(data)` | `data: array \| ObservableArray` | Static array or reactive observable array |
|
|
31
|
+
| `.empty(element)` | `element: NdChild` | Content shown when data is empty |
|
|
32
|
+
| `.noHeader()` | - | Hide the table header row |
|
|
33
|
+
| `.onRowClick(handler)` | `handler: (row) => void` | Fired when a row is clicked |
|
|
34
|
+
| `.rowProps(fn)` | `fn: (row) => object` | Dynamic HTML attributes per row |
|
|
35
|
+
|
|
36
|
+
## Example
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
SimpleTable()
|
|
40
|
+
.column('name', 'Name')
|
|
41
|
+
.column('email', 'Email')
|
|
42
|
+
.column('role', 'Role', {}, (value) => Badge(value).primary())
|
|
43
|
+
.column('actions', '', {}, (value, row) =>
|
|
44
|
+
HStack([
|
|
45
|
+
Button('Edit')
|
|
46
|
+
.small()
|
|
47
|
+
.nd.onClick(() => edit(row)),
|
|
48
|
+
Button('Delete')
|
|
49
|
+
.small()
|
|
50
|
+
.danger()
|
|
51
|
+
.nd.onClick(() => remove(row))
|
|
52
|
+
]).spacing(4)
|
|
53
|
+
)
|
|
54
|
+
.data(users)
|
|
55
|
+
.empty(Div('No users found'))
|
|
56
|
+
.onRowClick((row) => openUserDetail(row))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Column Groups
|
|
60
|
+
|
|
61
|
+
Group related columns under a shared header:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
SimpleTable()
|
|
65
|
+
.column('name', 'Name')
|
|
66
|
+
.group('Address', (group) => {
|
|
67
|
+
group.column('city', 'City')
|
|
68
|
+
group.column('country', 'Country')
|
|
69
|
+
group.column('zip', 'ZIP')
|
|
70
|
+
})
|
|
71
|
+
.column('actions', '', {}, (value, row) => Button('Edit').small())
|
|
72
|
+
.data(users)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## SimpleTable vs DataTable
|
|
78
|
+
|
|
79
|
+
| | SimpleTable | DataTable |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| **Sorting** | Manual | Built-in |
|
|
82
|
+
| **Filtering / Search** | Manual | Built-in |
|
|
83
|
+
| **Pagination** | Manual | Built-in |
|
|
84
|
+
| **Selection** | No | Yes |
|
|
85
|
+
| **Server-side** | Manual | Built-in |
|
|
86
|
+
| **Use when** | Small static or observable list | Large dataset with sort/filter/pagination |
|
|
87
|
+
|
|
88
|
+
See **[DataTable](./data-table.md)** for the full-featured alternative.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Theming
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
:root {
|
|
97
|
+
--simple-table-font-size: var(--description-size);
|
|
98
|
+
--simple-table-cell-padding-v: var(--space-cozy);
|
|
99
|
+
--simple-table-cell-padding-h: var(--space-comfortable);
|
|
100
|
+
--simple-table-border-color: var(--color-border-tertiary);
|
|
101
|
+
--simple-table-header-color: var(--color-text-secondary);
|
|
102
|
+
--simple-table-header-bg: var(--color-background-secondary);
|
|
103
|
+
--simple-table-header-weight: 600;
|
|
104
|
+
--simple-table-hover-bg: var(--color-background-secondary);
|
|
105
|
+
--simple-table-empty-color: var(--color-text-secondary);
|
|
106
|
+
}
|
|
107
|
+
```
|