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,279 @@
1
+ ---
2
+ title: Layout Components
3
+ description: Stack, HStack, VStack, AbsoluteStack, FixedStack, RelativeStack - flexible layout building blocks
4
+ ---
5
+
6
+ # Layout Components
7
+
8
+ Layout components provide flexible, semantic containers for building UI structure.
9
+
10
+ ```javascript
11
+ import { HStack, VStack, Row, Col, AbsoluteStack, FixedStack, RelativeStack, Divider } from 'native-document/components';
12
+ import { Stack } from 'native-document/components'; // only needed to register the renderer
13
+ ```
14
+
15
+ ---
16
+
17
+ ## `Stack` - Abstract Base
18
+
19
+ `Stack` is the abstract base class for all layout components. Do not use it directly - use `HStack`, `VStack`, `Row`, or `Col` instead.
20
+
21
+ All stack variants share the same `$description` structure and methods:
22
+
23
+ ### `$description`
24
+
25
+ ```javascript
26
+ {
27
+ orientation: 'horizontal', // 'horizontal' | 'vertical'
28
+ content: [],
29
+ spacing: null, // string | number
30
+ alignment: 'center', // 'leading' | 'center' | 'trailing' | 'stretch'
31
+ justifyContent: 'between', // 'start' | 'center' | 'end' | 'between' | 'around'
32
+ wrap: false,
33
+ grow: false,
34
+ shrink: false,
35
+ reverse: false,
36
+ props: {}
37
+ }
38
+ ```
39
+
40
+ ### Methods
41
+
42
+ ```javascript
43
+ // Spacing
44
+ .spacing(8)
45
+
46
+ // Alignment
47
+ .alignLeading() // alignment: 'leading'
48
+ .alignCenter() // alignment: 'center'
49
+ .alignTrailing() // alignment: 'trailing'
50
+ .alignStretch() // alignment: 'stretch'
51
+
52
+ // Justify
53
+ .justifyStart()
54
+ .justifyCenter()
55
+ .justifyEnd()
56
+ .justifyBetween()
57
+ .justifyAround()
58
+
59
+ // Combined
60
+ .center() // alignCenter() + justifyCenter()
61
+
62
+ // Other
63
+ .wrap()
64
+ .grow()
65
+ .shrink()
66
+ .reverse()
67
+ ```
68
+
69
+ ### Renderer
70
+
71
+ Each variant needs its own renderer registered separately:
72
+
73
+ ```javascript
74
+ import { HStack, VStack } from 'native-document/components';
75
+ import { HStackRender, VStackRender } from 'native-document/ui';
76
+
77
+ HStack.use(HStackRender);
78
+ VStack.use(VStackRender);
79
+ // Row and Col share HStack and VStack renderers automatically
80
+ ```
81
+
82
+ Or write your own:
83
+
84
+ ```javascript
85
+ HStack.use(($description) => {
86
+ return Div({
87
+ class: buildStackClasses($description),
88
+ style: $description.spacing ? { gap: $description.spacing + 'px' } : {},
89
+ ...$description.props
90
+ }, $description.content);
91
+ });
92
+ ```
93
+
94
+ ---
95
+
96
+ ## `HStack` - Horizontal Stack
97
+
98
+ Alias for `Stack` with `orientation: 'horizontal'`. Also available as `Row`.
99
+
100
+ ```javascript
101
+ HStack([
102
+ Avatar(user.avatar),
103
+ Div(user.name)
104
+ ]).alignCenter().spacing(12)
105
+
106
+ // Row is an alias for HStack
107
+ Row([
108
+ Button('Cancel').ghost(),
109
+ Button('Save').primary()
110
+ ]).justifyEnd().spacing(8)
111
+ ```
112
+
113
+ ---
114
+
115
+ ## `VStack` - Vertical Stack
116
+
117
+ `Stack` with `orientation: 'vertical'` and `alignment: 'leading'` by default. Also available as `Col`.
118
+
119
+ ```javascript
120
+ VStack([
121
+ H2('Title'),
122
+ P('Description'),
123
+ Button('Action').primary()
124
+ ]).spacing(16).alignStretch()
125
+
126
+ // Col is an alias for VStack
127
+ Col([Label('Name'), Input({ value: name })]).spacing(4)
128
+ ```
129
+
130
+ ---
131
+
132
+ ## `AbsoluteStack` / `FixedStack` / `RelativeStack` - Positioned Containers
133
+
134
+ All three extend `PositionStack` and share the same API. They differ only in CSS `position`:
135
+
136
+ | Component | CSS position |
137
+ |---|---|
138
+ | `AbsoluteStack` | `absolute` |
139
+ | `FixedStack` | `fixed` |
140
+ | `RelativeStack` | `relative` |
141
+
142
+ ### `$description`
143
+
144
+ ```javascript
145
+ {
146
+ position: 'absolute', // 'absolute' | 'fixed' | 'relative'
147
+ content: [],
148
+ top: null, // string | number
149
+ right: null,
150
+ bottom: null,
151
+ left: null,
152
+ width: null,
153
+ height: null,
154
+ zIndex: null,
155
+ anchor: null, // preset anchor shorthand
156
+ props: {}
157
+ }
158
+ ```
159
+
160
+ ### Methods
161
+
162
+ ```javascript
163
+ AbsoluteStack(Div('Tooltip'))
164
+ .top(0)
165
+ .right(0)
166
+ .width(200)
167
+ .height(100)
168
+ .zIndex(50)
169
+
170
+ // Size helpers
171
+ .fullWidth() // width: '100%'
172
+ .fullHeight() // height: '100%'
173
+ .fullSize() // width + height: '100%'
174
+ .size(200, 100) // width: 200, height: 100
175
+ .size(200) // width: 200, height: 200
176
+
177
+ // Z-index helpers
178
+ .above(100) // zIndex: 100
179
+ .below() // zIndex: -1
180
+
181
+ // Anchor presets (position combinations)
182
+ .fill() // anchor: 'fill'
183
+ .topLeading() // anchor: 'top-leading'
184
+ .atTopCenter() // anchor: 'top-center'
185
+ .atTopTrailing() // anchor: 'top-trailing'
186
+ .atCenterLeading() // anchor: 'center-leading'
187
+ .atCenter() // anchor: 'center'
188
+ .atCenterTrailing() // anchor: 'center-trailing'
189
+ .atBottomLeading() // anchor: 'bottom-leading'
190
+ .atBottomCenter() // anchor: 'bottom-center'
191
+ .atBottomTrailing() // anchor: 'bottom-trailing'
192
+ ```
193
+
194
+ ### Example
195
+
196
+ ```javascript
197
+ // Floating action button
198
+ const fab = FixedStack(
199
+ Button(PlusIcon).circle().primary()
200
+ )
201
+ .atBottomTrailing()
202
+ .right(24)
203
+ .bottom(24)
204
+ .above(100)
205
+
206
+ // Overlay badge
207
+ const badge = AbsoluteStack(Span('3'))
208
+ .atTopTrailing()
209
+ .top(-8)
210
+ .right(-8)
211
+ ```
212
+
213
+ ---
214
+
215
+ ## `Divider`
216
+
217
+ A horizontal or vertical separator, optionally with a label.
218
+
219
+ ```javascript
220
+ Divider(label?, props?)
221
+ ```
222
+
223
+ ### `$description`
224
+
225
+ ```javascript
226
+ {
227
+ label: null, // string | null
228
+ orientation: 'horizontal', // 'horizontal' | 'vertical'
229
+ props: {}
230
+ }
231
+ ```
232
+
233
+ ### Methods
234
+
235
+ ```javascript
236
+ Divider() // horizontal, no label
237
+ Divider('OR') // with label
238
+ Divider().vertical() // vertical
239
+
240
+ .orientation('vertical')
241
+ .vertical()
242
+ .horizontal()
243
+ ```
244
+
245
+ ### Example
246
+
247
+ ```javascript
248
+ VStack([
249
+ Input({ placeholder: 'Email', value: email }),
250
+ Input({ placeholder: 'Password', type: 'password', value: password }),
251
+ Button('Sign in').primary().block(),
252
+ Divider('OR'),
253
+ Button('Continue with Google').ghost().block()
254
+ ]).spacing(16)
255
+ ```
256
+
257
+
258
+ ---
259
+
260
+ ## Theming
261
+
262
+ ```css
263
+ :root {
264
+ --divider-color: var(--gray-lite-3);
265
+ --divider-label-color: var(--gray);
266
+ --divider-label-size: var(--note-size);
267
+ --divider-label-gap: var(--space-comfortable);
268
+ --divider-thickness: 1px;
269
+ --divider-spacing: 16px;
270
+ }
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Next Steps
276
+
277
+ - **[Getting Started](./getting-started.md)** - Register renderers
278
+ - **[Button](./button.md)** - Button component
279
+ - **[Modal](./modal.md)** - Modal with draggable and resizable
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: List
3
+ description: Flexible list component with single/multi selection, checkbox or click-to-select modes, dividers, and keyboard navigation
4
+ ---
5
+
6
+ # List
7
+
8
+ > **Status: coming soon.** The `List` API is fully defined but the default renderer is not yet implemented. You can use `List` today by providing your own renderer via `List.use()`.
9
+
10
+ ```javascript
11
+ import { List } from 'native-document/components';
12
+
13
+ List(props?)
14
+ ```
15
+
16
+ ## Custom Renderer
17
+
18
+ Until the default renderer ships, register your own:
19
+
20
+ ```javascript
21
+ import { Ul, Li } from 'native-document/elements';
22
+
23
+ List.use({
24
+ list: ($d, instance) => {
25
+ return Ul({ class: `list ${$d.inset ? 'list-inset' : ''}` });
26
+ }
27
+ });
28
+ ```
29
+
30
+ > `List.use()` expects an object with a `list` key, not a function directly.
31
+
32
+ ## Methods
33
+
34
+ ### Data
35
+
36
+ | Method | Parameters | Description |
37
+ |---|---|---|
38
+ | `.items(items)` | `items: *[]` | Static item list |
39
+ | `.dynamic(obs?)` | `obs?: ObservableArray` | Bind to a reactive array |
40
+ | `.data(data)` | `data: *` | Data passed to the renderer |
41
+
42
+ ### Selection
43
+
44
+ | Method | Parameters | Description |
45
+ |---|---|---|
46
+ | `.selectable(enabled?)` | `enabled?: boolean` | Enable item selection |
47
+ | `.multiSelect(enabled?)` | `enabled?: boolean` | Allow multiple selections. Enables `.selectable()` automatically |
48
+ | `.selectByClick()` | - | Select items on click (mutually exclusive with `selectByCheckbox`) |
49
+ | `.selectByCheckbox()` | - | Select items via a checkbox (mutually exclusive with `selectByClick`) |
50
+ | `.selectedValuesModel(obs)` | `obs: Observable<*[]>` | Bind selected values to an external observable |
51
+
52
+ ### Layout
53
+
54
+ | Method | Parameters | Description |
55
+ |---|---|---|
56
+ | `.withDivider(enabled?)` | `enabled?: boolean` | Show a divider between items |
57
+ | `.inset(enabled?)` | `enabled?: boolean` | Add inset padding |
58
+ | `.loopOnKeyboard(enabled)` | `enabled: boolean` | Loop keyboard navigation at list boundaries. Default `true` |
59
+
60
+ ### Events
61
+
62
+ | Method | Parameters | Description |
63
+ |---|---|---|
64
+ | `.onItemClick(handler)` | `handler: (item, event) => void` | Fires when an item is clicked |
65
+ | `.onItemSelect(handler)` | `handler: (item) => void` | Fires when an item is selected |
66
+
67
+ ---
68
+
69
+ ## Next Steps
70
+
71
+ - **[Components Overview](./index.md)** - BaseComponent and renderer pattern
72
+ - **[Getting Started](./getting-started.md)** - Register default renderers
73
+ - **[Traits](./traits.md)** - `HasItems` trait used by this component
@@ -0,0 +1,215 @@
1
+ ---
2
+ title: Menu
3
+ description: Menu component with items, links, groups, dividers, keyboard navigation, and active state
4
+ ---
5
+
6
+ # Menu
7
+
8
+ ```javascript
9
+ import { Menu, MenuItem, MenuGroup, MenuLink, MenuDivider } from 'native-document/components';
10
+ ```
11
+
12
+ ## Default Renderer
13
+
14
+ ```javascript
15
+ import {
16
+ MenuRender, MenuItemRender,
17
+ MenuGroupRender, MenuLinkRender, MenuDividerRender
18
+ } from 'native-document/ui';
19
+
20
+ Menu.use(MenuRender);
21
+ MenuItem.use(MenuItemRender);
22
+ MenuGroup.use(MenuGroupRender);
23
+ MenuLink.use(MenuLinkRender);
24
+ MenuDivider.use(MenuDividerRender);
25
+ ```
26
+
27
+ ## Methods
28
+
29
+ ### Orientation
30
+
31
+ ```javascript
32
+ .horizontal()
33
+ .vertical()
34
+ .inline()
35
+ ```
36
+
37
+ ### Building items
38
+
39
+ ```javascript
40
+ .item(label, options?)
41
+ .item('Dashboard', { action: () => navigate('/'), icon: DashboardIcon, key: 'home' })
42
+
43
+ // Function form - receives MenuItem instance
44
+ .item('Settings', (item) => {
45
+ item.action(() => navigate('/settings'))
46
+ .icon(SettingsIcon)
47
+ .key('settings')
48
+ .shortcut('+,')
49
+ })
50
+
51
+ // Link item
52
+ .link(label, options?)
53
+ .link('Documentation', { href: 'https://docs.example.com', target: '_blank' })
54
+
55
+ // Router link
56
+ .linkTo(label, options?)
57
+ .linkTo('Home', { href: 'home' })
58
+ .linkTo('Profile', { href: { name: 'user', params: { id: 1 } } })
59
+
60
+ // Separator
61
+ .separator()
62
+ .divider() // alias
63
+
64
+ // Group
65
+ .group(label, (group) => {
66
+ group.item('Profile', { action: () => navigate('/profile') })
67
+ group.item('Security', { action: () => navigate('/security') })
68
+ group.separator()
69
+ group.item('Logout', { action: () => logout(), icon: LogoutIcon })
70
+ })
71
+ ```
72
+
73
+ ### Active state
74
+
75
+ ```javascript
76
+ .active(() => router.currentState().route?.name) // callback
77
+ .active(currentRoute.select(r => r.name)) // observable
78
+ ```
79
+
80
+ ### Behavior
81
+
82
+ ```javascript
83
+ .closeOnSelect(false)
84
+ .keyboardLoop(false)
85
+ .clickFirst()
86
+ .compactThreshold(80)
87
+ ```
88
+
89
+ ### Dynamic items
90
+
91
+ ```javascript
92
+ .bind(observableArray)
93
+ ```
94
+
95
+ ### Events
96
+
97
+ ```javascript
98
+ .onItemClick((item) => console.log('Clicked:', item.label))
99
+ .onItemSelect((item) => console.log('Selected:', item.key))
100
+ ```
101
+
102
+ ## Example - Sidebar Navigation
103
+
104
+ ```javascript
105
+ Menu()
106
+ .vertical()
107
+ .active(router.currentState().select(s => s.route?.name))
108
+ .item('Dashboard', { action: () => Router.push('/'), icon: HomeIcon, key: 'home' })
109
+ .item('Users', { action: () => Router.push('/users'), icon: UsersIcon, key: 'users' })
110
+ .separator()
111
+ .group('Settings', (group) => {
112
+ group.item('General', { action: () => Router.push('/settings'), key: 'settings' })
113
+ group.item('Security', { action: () => Router.push('/settings/security'), key: 'security' })
114
+ })
115
+ .separator()
116
+ .item('Logout', { action: () => logout(), icon: LogoutIcon, key: 'logout' })
117
+ ```
118
+
119
+ ## Example - Top Navigation
120
+
121
+ ```javascript
122
+ Menu()
123
+ .horizontal()
124
+ .linkTo('Home', { href: 'home' })
125
+ .linkTo('Products', { href: 'products' })
126
+ .linkTo('Pricing', { href: 'pricing' })
127
+ .link('Docs', { href: 'https://docs.example.com', target: '_blank' })
128
+ ```
129
+
130
+ ---
131
+
132
+ ## `MenuItem`
133
+
134
+ ```javascript
135
+ MenuItem()
136
+ .label('Dashboard')
137
+ .icon(DashboardIcon)
138
+ .key('dashboard')
139
+ .action(() => navigate('/dashboard'))
140
+ .shortcut('+D')
141
+ .disabled(Observable(false))
142
+ .selected(Observable(false))
143
+ .trailing(Badge('New').success())
144
+ ```
145
+
146
+ See **[ShortcutManager](./shortcut-manager.md)** for the shortcut convention.
147
+
148
+ ---
149
+
150
+ ## `MenuGroup`
151
+
152
+ ```javascript
153
+ MenuGroup('Settings')
154
+ .icon(SettingsIcon)
155
+ .collapsable()
156
+ .collapsed(true)
157
+ .item('Profile', { action: () => navigate('/profile') })
158
+ .item('Security', { action: () => navigate('/security') })
159
+ .divider()
160
+ .item('Logout', { action: logout })
161
+ ```
162
+
163
+ ---
164
+
165
+ ## `MenuLink`
166
+
167
+ Extends `MenuItem` with a `.target()` method:
168
+
169
+ ```javascript
170
+ MenuLink()
171
+ .label('Documentation')
172
+ .icon(BookIcon)
173
+ .action('https://docs.example.com')
174
+ .target('_blank')
175
+ ```
176
+
177
+ ---
178
+
179
+ ## `MenuDivider`
180
+
181
+ ```javascript
182
+ MenuDivider()
183
+ // or via shorthand:
184
+ menu.separator()
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Theming
190
+
191
+ ```css
192
+ :root {
193
+ --menu-bg: var(--background);
194
+ --menu-border: var(--gray-lite-3);
195
+ --menu-radius: var(--radius-card);
196
+ --menu-shadow: var(--shadow-md);
197
+ --menu-font-size: var(--description-size);
198
+ --menu-gap: var(--space-tiny);
199
+ --menu-padding: var(--space-cozy) 0;
200
+ --menu-item-padding: var(--space-cozy) var(--space-comfortable);
201
+ --menu-item-radius: var(--radius-button);
202
+ --menu-item-gap: var(--space-cozy);
203
+ --menu-item-color: var(--text-color);
204
+ --menu-item-color-hover: var(--text-color);
205
+ --menu-item-color-active: var(--color-primary);
206
+ --menu-item-color-disabled: var(--gray-lite-2);
207
+ --menu-item-bg-hover: var(--gray-lite-5);
208
+ --menu-group-label-size: var(--hint-size);
209
+ --menu-group-label-color: var(--gray);
210
+ --menu-shortcut-size: var(--note-size);
211
+ --menu-shortcut-color: var(--gray);
212
+ --menu-divider-color: var(--gray-lite-3);
213
+ --sub-menu-shadow: var(--shadow-lg);
214
+ }
215
+ ```
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Modal
3
+ description: Modal dialog with draggable and resizable support
4
+ ---
5
+
6
+ # Modal
7
+
8
+ ```javascript
9
+ import { Modal } from 'native-document/components';
10
+ ```
11
+
12
+ ## Default Renderers
13
+
14
+ ```javascript
15
+ import { ModalRender } from 'native-document/ui';
16
+
17
+ Modal.use(ModalRender);
18
+ ```
19
+
20
+ ---
21
+
22
+ ## `Modal`
23
+
24
+ ```javascript
25
+ Modal(content, props?)
26
+ ```
27
+
28
+ ### `$description`
29
+
30
+ ```javascript
31
+ {
32
+ content: null,
33
+ title: null,
34
+ footer: null,
35
+ size: null, // 'small' | 'medium' | 'large' | 'xl'
36
+ centered: true,
37
+ scrollable: false,
38
+ closeOnBackdrop: true,
39
+ closeOnEscape: true,
40
+ closable: true,
41
+ fullscreen: false,
42
+ draggable: false,
43
+ resizable: false,
44
+ focusTrap: true,
45
+ lockScroll: true,
46
+ isOpen: Observable(false),
47
+ props: {} // HTML attributes for the root element
48
+ }
49
+ ```
50
+
51
+ ### Methods
52
+
53
+ ```javascript
54
+ // Content
55
+ .title('Confirm deletion')
56
+ .content(Div('Are you sure?'))
57
+ .footer(
58
+ HStack([
59
+ Button('Cancel').ghost().nd.onClick(() => modal.close()),
60
+ Button('Delete').danger().nd.onClick(() => confirmDelete())
61
+ ]).justifyEnd().spacing(8)
62
+ )
63
+
64
+ // Size
65
+ .small()
66
+ .medium()
67
+ .large()
68
+ .extraLarge()
69
+ .fullscreen()
70
+
71
+ // Behavior
72
+ .centered()
73
+ .scrollable()
74
+ .closeOnBackdrop(false)
75
+ .closeOnEscape(false)
76
+ .closable(false)
77
+ .focusTrap(false)
78
+ .lockScroll(false)
79
+
80
+ // Draggable & Resizable
81
+ .draggable()
82
+ .resizable()
83
+ .resizable(true, { size: { minWidth: 300, minHeight: 200 } })
84
+
85
+ // Programmatic
86
+ .open()
87
+ .close()
88
+ .toggle()
89
+
90
+ // Events
91
+ .onOpen(() => console.log('Opened'))
92
+ .onClose(() => console.log('Closed'))
93
+ .onBeforeOpen(() => console.log('About to open'))
94
+ .onBeforeClose(() => console.log('About to close'))
95
+
96
+ // Custom renderers
97
+ .renderHeader(($description) => Div({ class: 'modal-header' }, $description.title))
98
+ .renderContent(($description) => Div({ class: 'modal-body' }, $description.content))
99
+ .renderFooter(($description) => Div({ class: 'modal-footer' }, $description.footer))
100
+ ```
101
+
102
+ ### Example
103
+
104
+ ```javascript
105
+ Modal(
106
+ Div([
107
+ P('This action cannot be undone.'),
108
+ P('All associated data will be permanently removed.')
109
+ ])
110
+ )
111
+ .title('Delete Account')
112
+ .footer(
113
+ HStack([
114
+ Button('Cancel').ghost().nd.onClick(() => deleteModal.close()),
115
+ Button('Yes, delete').danger().nd.onClick(async () => {
116
+ await deleteAccount();
117
+ deleteModal.close();
118
+ })
119
+ ]).justifyEnd().spacing(8)
120
+ )
121
+ .small()
122
+ .onOpen(() => trackEvent('delete_modal_open'))
123
+ .trigger(
124
+ Button('Delete account')
125
+ .danger()
126
+ )
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Theming
132
+
133
+ ```css
134
+ :root {
135
+ --modal-bg: var(--white);
136
+ --modal-border: var(--gray-lite-5);
137
+ --modal-radius: var(--radius-card);
138
+ --modal-shadow: var(--shadow-lg);
139
+ --modal-z-index: 10001;
140
+ --modal-font-size: var(--description-size);
141
+ --modal-header-size: var(--text-size);
142
+ --modal-header-weight: 600;
143
+ --modal-padding: var(--space-comfortable);
144
+ --modal-header-padding: var(--space-tiny);
145
+ --modal-animation-duration: 0.2s;
146
+ --modal-width-small: 400px;
147
+ --modal-width-medium: 560px;
148
+ --modal-width-large: 720px;
149
+ --modal-width-extra-large: 960px;
150
+ --modal-backdrop-bg: rgba(0, 0, 0, 0.8);
151
+ --modal-close-width: 25px;
152
+ --modal-close-height: 25px;
153
+ --modal-close-hover-bg-color: var(--contrasted-red);
154
+ --modal-close-hover-color: var(--white);
155
+ }
156
+ ```