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.
Files changed (139) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +187 -0
  3. package/components.js +4 -1
  4. package/dist/native-document.components.min.js +593 -300
  5. package/dist/native-document.dev.js +99 -75
  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/icons.md +321 -0
  41. package/docs/components/index.md +337 -0
  42. package/docs/components/layout.md +279 -0
  43. package/docs/components/list.md +73 -0
  44. package/docs/components/menu.md +215 -0
  45. package/docs/components/modal.md +156 -0
  46. package/docs/components/pagination.md +95 -0
  47. package/docs/components/popover.md +131 -0
  48. package/docs/components/progress.md +111 -0
  49. package/docs/components/shortcut-manager.md +221 -0
  50. package/docs/components/simple-table.md +107 -0
  51. package/docs/components/skeleton.md +155 -0
  52. package/docs/components/spinner.md +100 -0
  53. package/docs/components/splitter.md +133 -0
  54. package/docs/components/stepper.md +163 -0
  55. package/docs/components/switch.md +113 -0
  56. package/docs/components/tabs.md +153 -0
  57. package/docs/components/toast.md +119 -0
  58. package/docs/components/tooltip.md +151 -0
  59. package/docs/components/traits.md +261 -0
  60. package/docs/conditional-rendering.md +170 -588
  61. package/docs/contributing.md +300 -25
  62. package/docs/core-concepts.md +205 -374
  63. package/docs/elements.md +251 -367
  64. package/docs/extending-native-document-element.md +192 -207
  65. package/docs/filters.md +153 -1122
  66. package/docs/getting-started.md +193 -267
  67. package/docs/i18n.md +241 -0
  68. package/docs/index.md +76 -0
  69. package/docs/lifecycle-events.md +143 -75
  70. package/docs/list-rendering.md +227 -852
  71. package/docs/memory-management.md +134 -47
  72. package/docs/native-document-element.md +337 -186
  73. package/docs/native-fetch.md +99 -630
  74. package/docs/observable-resource.md +364 -0
  75. package/docs/observables.md +592 -526
  76. package/docs/routing.md +244 -653
  77. package/docs/state-management.md +134 -241
  78. package/docs/svg-elements.md +231 -0
  79. package/docs/theming.md +409 -0
  80. package/docs/tutorials/.gitkeep +0 -0
  81. package/docs/validation.md +95 -97
  82. package/docs/vitepress-conventions.md +219 -0
  83. package/package.json +34 -13
  84. package/readme.md +269 -89
  85. package/src/components/card/Card.js +93 -39
  86. package/src/components/card/index.js +1 -1
  87. package/src/components/form/field/types/FileField.js +3 -3
  88. package/src/components/icon/Icon.js +107 -0
  89. package/src/components/icon/icon-getters.js +142 -0
  90. package/src/components/icon/icons.js +171 -0
  91. package/src/components/icon/index.js +17 -0
  92. package/src/components/icon/types/Icon.d.ts +191 -0
  93. package/src/components/index.d.ts +6 -1
  94. package/src/components/list/HasListItem.js +171 -0
  95. package/src/components/list/List.js +41 -107
  96. package/src/components/list/ListDivider.js +39 -0
  97. package/src/components/list/ListGroup.js +76 -59
  98. package/src/components/list/ListItem.js +117 -69
  99. package/src/components/list/index.js +3 -1
  100. package/src/components/list/types/List.d.ts +45 -32
  101. package/src/components/list/types/ListDivider.ts +16 -0
  102. package/src/components/list/types/ListGroup.d.ts +51 -29
  103. package/src/components/list/types/ListItem.d.ts +40 -30
  104. package/src/components/spacer/Spacer.js +1 -1
  105. package/src/core/data/ObservableResource.js +5 -0
  106. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  107. package/src/core/elements/content-formatter.js +16 -2
  108. package/src/core/elements/form.js +1 -1
  109. package/src/core/elements/img.js +1 -1
  110. package/src/core/elements/medias.js +2 -2
  111. package/src/core/elements/meta-data.js +1 -1
  112. package/src/core/wrappers/AttributesWrapper.js +37 -22
  113. package/src/core/wrappers/ElementCreator.js +9 -16
  114. package/src/core/wrappers/HtmlElementWrapper.js +26 -7
  115. package/src/core/wrappers/NDElement.js +12 -1
  116. package/src/core/wrappers/prototypes/attributes-extensions.js +24 -24
  117. package/src/core/wrappers/prototypes/nd-element-extensions.js +1 -8
  118. package/src/ui/components/card/CardRender.js +133 -0
  119. package/src/ui/components/card/card.css +169 -0
  120. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  121. package/src/ui/components/icon/material/MaterialIconRender.js +71 -0
  122. package/src/ui/components/icon/material/material.css +15 -0
  123. package/src/ui/components/icon/material/material.map.js +170 -0
  124. package/src/ui/components/icon/phosphor/PhosphorIconRender.js +71 -0
  125. package/src/ui/components/icon/phosphor/phosphor.css +17 -0
  126. package/src/ui/components/icon/phosphor/phosphor.map.js +165 -0
  127. package/src/ui/components/icon/tabler/TablerIconRender.js +59 -0
  128. package/src/ui/components/icon/tabler/tabler.css +14 -0
  129. package/src/ui/components/icon/tabler/tabler.map.js +165 -0
  130. package/src/ui/components/list/ListRender.js +18 -0
  131. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  132. package/src/ui/components/list/divider/list-divider.css +12 -0
  133. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  134. package/src/ui/components/list/group/list-group.css +62 -0
  135. package/src/ui/components/list/item/ListItemRender.js +238 -0
  136. package/src/ui/components/list/item/list-item.css +191 -0
  137. package/src/ui/components/list/list.css +24 -0
  138. package/src/ui/components/spacer/SpacerRender.js +10 -0
  139. 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
+ ```