native-document 1.0.168 → 1.0.172

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 (153) hide show
  1. package/CHANGELOG.md +99 -4
  2. package/components.js +3 -1
  3. package/dist/native-document.components.min.js +197 -171
  4. package/dist/native-document.dev.js +92 -75
  5. package/dist/native-document.dev.js.map +1 -1
  6. package/dist/native-document.min.js +1 -1
  7. package/docs/components/icons.md +321 -0
  8. package/docs/vitepress-conventions.md +2 -2
  9. package/package.json +11 -4
  10. package/src/components/BaseComponent.js +43 -1
  11. package/src/components/accordion/Accordion.js +1 -0
  12. package/src/components/accordion/AccordionItem.js +1 -0
  13. package/src/components/alert/Alert.js +4 -0
  14. package/src/components/avatar/Avatar.js +1 -0
  15. package/src/components/avatar/AvatarGroup.js +1 -0
  16. package/src/components/badge/Badge.js +1 -0
  17. package/src/components/breadcrumb/BreadCrumb.js +4 -0
  18. package/src/components/button/Button.js +1 -0
  19. package/src/components/card/Card.js +1 -0
  20. package/src/components/context-menu/ContextMenu.js +2 -0
  21. package/src/components/context-menu/ContextMenuGroup.js +2 -0
  22. package/src/components/context-menu/ContextMenuItem.js +2 -0
  23. package/src/components/divider/Divider.js +2 -0
  24. package/src/components/dropdown/Dropdown.js +1 -0
  25. package/src/components/dropdown/DropdownDivider.js +1 -0
  26. package/src/components/dropdown/DropdownGroup.js +1 -0
  27. package/src/components/dropdown/DropdownItem.js +1 -0
  28. package/src/components/dropdown/DropdownTrigger.js +1 -0
  29. package/src/components/form/FormControl.js +1 -0
  30. package/src/components/form/field/Field.js +1 -0
  31. package/src/components/form/field/types/FileField.js +3 -3
  32. package/src/components/form/field/types/StringField.js +1 -1
  33. package/src/components/icon/Icon.js +108 -0
  34. package/src/components/icon/icon-getters.js +142 -0
  35. package/src/components/icon/icons.js +171 -0
  36. package/src/components/icon/index.js +17 -0
  37. package/src/components/icon/types/Icon.d.ts +191 -0
  38. package/src/components/index.d.ts +6 -1
  39. package/src/components/list/ListItem.js +1 -0
  40. package/src/components/list/types/List.d.ts +45 -32
  41. package/src/components/list/types/ListDivider.ts +16 -0
  42. package/src/components/list/types/ListGroup.d.ts +51 -29
  43. package/src/components/list/types/ListItem.d.ts +20 -21
  44. package/src/components/menu/Menu.js +3 -0
  45. package/src/components/menu/MenuDivider.js +1 -0
  46. package/src/components/menu/MenuGroup.js +1 -0
  47. package/src/components/menu/MenuItem.js +1 -0
  48. package/src/components/menu/MenuLink.js +1 -0
  49. package/src/components/modal/Modal.js +4 -0
  50. package/src/components/pagination/Pagination.js +1 -0
  51. package/src/components/popover/Popover.js +1 -0
  52. package/src/components/popover/PopoverFooter.js +1 -0
  53. package/src/components/popover/PopoverHeader.js +1 -0
  54. package/src/components/progress/Progress.js +5 -0
  55. package/src/components/skeleton/Skeleton.js +4 -0
  56. package/src/components/slider/Slider.js +1 -0
  57. package/src/components/spacer/Spacer.js +1 -0
  58. package/src/components/spinner/Spinner.js +1 -0
  59. package/src/components/splitter/Splitter.js +1 -0
  60. package/src/components/splitter/SplitterGutter.js +6 -0
  61. package/src/components/splitter/SplitterPanel.js +1 -0
  62. package/src/components/stacks/AbsoluteStack.js +1 -0
  63. package/src/components/stacks/FixedStack.js +1 -0
  64. package/src/components/stacks/HStack.js +1 -0
  65. package/src/components/stacks/PositionStack.js +1 -0
  66. package/src/components/stacks/RelativeStack.js +1 -0
  67. package/src/components/stacks/Stack.js +1 -0
  68. package/src/components/stacks/VStack.js +1 -0
  69. package/src/components/stepper/Stepper.js +1 -0
  70. package/src/components/stepper/StepperStep.js +1 -0
  71. package/src/components/switch/Switch.js +1 -0
  72. package/src/components/table/DataTable.js +1 -0
  73. package/src/components/table/SimpleTable.js +1 -0
  74. package/src/components/tabs/Tabs.js +1 -0
  75. package/src/components/toast/Toast.js +1 -0
  76. package/src/components/tooltip/Tooltip.js +1 -0
  77. package/src/core/data/MemoryManager.js +6 -1
  78. package/src/core/data/ObservableItem.js +28 -10
  79. package/src/core/elements/anchor/anchor.js +4 -3
  80. package/src/core/elements/anchor/one-child-anchor-overwriting.js +4 -4
  81. package/src/core/elements/content-formatter.js +35 -2
  82. package/src/core/elements/control/for-each-array-cache.js +59 -0
  83. package/src/core/elements/control/for-each-array.js +15 -23
  84. package/src/core/elements/form.js +1 -1
  85. package/src/core/elements/img.js +1 -1
  86. package/src/core/elements/medias.js +2 -2
  87. package/src/core/elements/meta-data.js +1 -1
  88. package/src/core/wrappers/AttributesWrapper.js +44 -29
  89. package/src/core/wrappers/ElementCreator.js +38 -22
  90. package/src/core/wrappers/HtmlElementWrapper.js +33 -9
  91. package/src/core/wrappers/NDElement.js +35 -9
  92. package/src/core/wrappers/NdPrototype.js +31 -39
  93. package/src/core/wrappers/constants.js +12 -12
  94. package/src/core/wrappers/prototypes/attributes-extensions.js +24 -24
  95. package/src/core/wrappers/prototypes/nd-element-extensions.js +128 -65
  96. package/src/ui/components/accordion/AccordionItemRender.js +29 -7
  97. package/src/ui/components/alert/AlertRender.js +6 -0
  98. package/src/ui/components/breadcrumb/BreadcrumbRender.js +9 -1
  99. package/src/ui/components/button/ButtonRender.js +3 -0
  100. package/src/ui/components/contextmenu/ContextmenuRender.js +2 -0
  101. package/src/ui/components/dropdown/DropdownRender.js +3 -0
  102. package/src/ui/components/dropdown/item/DropdownItemRender.js +6 -1
  103. package/src/ui/components/form/FieldCollectionRender.js +4 -0
  104. package/src/ui/components/form/fields/CheckboxFieldRender.js +4 -0
  105. package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +4 -0
  106. package/src/ui/components/form/fields/RadioFieldRender.js +4 -0
  107. package/src/ui/components/form/fields/RangeFieldRender.js +4 -0
  108. package/src/ui/components/form/fields/SelectFieldRender.js +5 -0
  109. package/src/ui/components/form/fields/SliderFieldRender.js +3 -0
  110. package/src/ui/components/icon/material/MaterialIconRender.js +71 -0
  111. package/src/ui/components/icon/material/material.css +15 -0
  112. package/src/ui/components/icon/material/material.map.js +170 -0
  113. package/src/ui/components/icon/phosphor/PhosphorIconRender.js +71 -0
  114. package/src/ui/components/icon/phosphor/phosphor.css +17 -0
  115. package/src/ui/components/icon/phosphor/phosphor.map.js +165 -0
  116. package/src/ui/components/icon/tabler/TablerIconRender.js +58 -0
  117. package/src/ui/components/icon/tabler/tabler.css +14 -0
  118. package/src/ui/components/icon/tabler/tabler.map.js +165 -0
  119. package/src/ui/components/list/item/ListItemRender.js +4 -0
  120. package/src/ui/components/menu/MenuItemRender.js +8 -0
  121. package/src/ui/components/menu/MenuLinkRender.js +11 -0
  122. package/src/ui/components/menu/MenuRender.js +5 -0
  123. package/src/ui/components/menu/helpers.js +2 -1
  124. package/src/ui/components/modal/ModalRender.js +8 -2
  125. package/src/ui/components/progress/ProgressRender.js +9 -0
  126. package/src/ui/components/skeleton/SkeletonRender.js +2 -0
  127. package/src/ui/components/spinner/SpinnerRender.js +3 -0
  128. package/src/ui/components/splitter/SplitterGutterRender.js +4 -0
  129. package/src/ui/components/splitter/SplitterPanelRender.js +4 -0
  130. package/src/ui/components/splitter/SplitterRender.js +2 -0
  131. package/src/ui/components/stepper/StepperRender.js +2 -1
  132. package/src/ui/components/stepper/StepperStepRender.js +8 -1
  133. package/src/ui/components/switch/SwitchRender.js +3 -0
  134. package/src/ui/components/table/data-table/DataTableRender.js +4 -0
  135. package/src/ui/components/table/simple-table/SimpleTableRender.js +2 -0
  136. package/src/ui/components/toast/ToastRender.js +4 -0
  137. package/src/ui/index.js +5 -0
  138. package/src/ui/utils/aria.js +19 -0
  139. package/tests/integration/ui/attributes-wrapper.test.js +177 -0
  140. package/tests/integration/ui/for-each-array.test.js +167 -0
  141. package/tests/integration/ui/lifecycle.test.js +146 -0
  142. package/tests/integration/ui/show-if.test.js +168 -0
  143. package/tests/unit/core/observable-array.test.js +404 -0
  144. package/tests/unit/core/observable-item.test.js +428 -0
  145. package/tests/unit/core/observable-object.test.js +239 -0
  146. package/tests/unit/core/observable-resource.test.js +323 -0
  147. package/tests/unit/core/store.test.js +347 -0
  148. package/tests/unit/router/route-matching.test.js +169 -0
  149. package/tests/unit/router/router-guards.test.js +189 -0
  150. package/tests/unit/utils/cache.test.js +120 -0
  151. package/tests/unit/utils/filters.test.js +394 -0
  152. package/types/elements.d.ts +5 -0
  153. package/vitest.config.js +15 -0
@@ -0,0 +1,321 @@
1
+ ---
2
+ title: Icons
3
+ description: Adapter-based icon system with chainable API and support for Tabler, Phosphor, and Google Material Icons
4
+ ---
5
+
6
+ # Icons
7
+
8
+ NativeDocument provides an adapter-based icon system. Icon names are semantic and stable - switching from one icon library to another requires changing only the adapter and the CSS import.
9
+
10
+ ```javascript
11
+ // Usage - always via the Icon namespace
12
+ Icon.search.fill()
13
+ Icon.star.fill().large()
14
+ Icon.arrowRight.bold().color('red')
15
+ Icon.user.fill().size(32)
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Setup
21
+
22
+ ### 1. Register the adapter
23
+
24
+ In your app entry point (`main.js`):
25
+
26
+ ```javascript
27
+ import { Icon } from 'native-document/icons';
28
+ import TablerIconRender from 'native-document/ui/icon/tabler';
29
+
30
+ Icon.use(TablerIconRender, {
31
+ variant: 'outline', // default variant
32
+ size: 'medium', // default size: small | medium | large | extraLarge
33
+ });
34
+ ```
35
+
36
+ ### 2. Import the CSS
37
+
38
+ In the same entry point, import the icon font alongside your theme:
39
+
40
+ ```javascript
41
+ // main.js
42
+ import 'native-document/src/ui/theme.scss';
43
+ import '@tabler/icons-webfont/dist/tabler-icons.css';
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Methods
49
+
50
+ ### Variants
51
+
52
+ | Method | Sets variant to |
53
+ |---|---|
54
+ | `.thin()` | `'thin'` |
55
+ | `.light()` | `'light'` |
56
+ | `.regular()` | `'regular'` |
57
+ | `.bold()` | `'bold'` |
58
+ | `.fill()` | `'fill'` |
59
+ | `.duotone()` | `'duotone'` |
60
+ | `.variant(name)` | any custom string |
61
+
62
+ ### Sizes
63
+
64
+ | Method | Sets size to |
65
+ |---|---|
66
+ | `.small()` | `'small'` - 16px |
67
+ | `.medium()` | `'medium'` - 24px |
68
+ | `.large()` | `'large'` - 32px |
69
+ | `.extraLarge()` | `'extraLarge'` - 48px |
70
+ | `.size(value)` | number in px or named string |
71
+
72
+ ### Other
73
+
74
+ | Method | Parameters | Description |
75
+ |---|---|---|
76
+ | `.color(value)` | `value: string` | CSS color value |
77
+ | `.weight(value)` | `value: string` | Stroke weight (adapter-dependent) |
78
+
79
+ All methods accept `Observable` values - the icon updates automatically when the observable changes:
80
+
81
+ ```javascript
82
+ const isDark = Observable(false);
83
+
84
+ Icon.star
85
+ .variant(isDark.transform((d) => d ? 'fill' : 'outline'))
86
+ .color(isDark.transform((d) => d ? 'gold' : 'currentColor'))
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Available icons
92
+
93
+ > The standard set covers the most common use cases. Each adapter maps these names to its own icon names - not every icon exists in every library.
94
+
95
+ ### Navigation
96
+ `Icon.arrowLeft`, `Icon.arrowRight`, `Icon.arrowUp`, `Icon.arrowDown`, `Icon.chevronLeft`, `Icon.chevronRight`, `Icon.chevronUp`, `Icon.chevronDown`, `Icon.home`, `Icon.menu`, `Icon.close`, `Icon.back`, `Icon.forward`, `Icon.expand`, `Icon.collapse`, `Icon.fullscreen`, `Icon.fullscreenExit`
97
+
98
+ ### Actions
99
+ `Icon.search`, `Icon.filter`, `Icon.sort`, `Icon.edit`, `Icon.delete`, `Icon.add`, `Icon.remove`, `Icon.save`, `Icon.copy`, `Icon.download`, `Icon.upload`, `Icon.share`, `Icon.refresh`, `Icon.more`, `Icon.settings`, `Icon.drag`, `Icon.resize`, `Icon.undo`, `Icon.redo`, `Icon.scan`, `Icon.print`, `Icon.import`, `Icon.export`
100
+
101
+ ### Feedback
102
+ `Icon.check`, `Icon.checkCircle`, `Icon.error`, `Icon.warning`, `Icon.info`, `Icon.help`, `Icon.loading`, `Icon.success`
103
+
104
+ ### User
105
+ `Icon.user`, `Icon.userCircle`, `Icon.users`, `Icon.lock`, `Icon.unlock`, `Icon.eye`, `Icon.eyeOff`, `Icon.bell`, `Icon.notification`
106
+
107
+ ### Files & Media
108
+ `Icon.file`, `Icon.folder`, `Icon.image`, `Icon.document`, `Icon.attachment`, `Icon.link`, `Icon.externalLink`, `Icon.play`, `Icon.pause`, `Icon.stop`, `Icon.mute`, `Icon.sound`, `Icon.video`, `Icon.camera`
109
+
110
+ ### Interface
111
+ `Icon.grid`, `Icon.list`, `Icon.calendar`, `Icon.clock`, `Icon.tag`, `Icon.tags`, `Icon.qrCode`, `Icon.barcode`
112
+
113
+ ### Text editing
114
+ `Icon.bold`, `Icon.italic`, `Icon.underline`, `Icon.strikethrough`, `Icon.alignLeft`, `Icon.alignCenter`, `Icon.alignRight`, `Icon.alignJustify`, `Icon.orderedList`, `Icon.unorderedList`, `Icon.indent`, `Icon.outdent`, `Icon.scissors`, `Icon.fontColor`, `Icon.highlight`
115
+
116
+ ### Data & charts
117
+ `Icon.barChart`, `Icon.lineChart`, `Icon.pieChart`, `Icon.areaChart`, `Icon.table`, `Icon.database`
118
+
119
+ ### Commerce & finance
120
+ `Icon.shoppingCart`, `Icon.creditCard`, `Icon.wallet`, `Icon.bank`, `Icon.gift`, `Icon.percentage`, `Icon.dollar`, `Icon.euro`
121
+
122
+ ### Communication
123
+ `Icon.mail`, `Icon.message`, `Icon.comment`, `Icon.phone`
124
+
125
+ ### System & dev
126
+ `Icon.cloud`, `Icon.cloudUpload`, `Icon.cloudDownload`, `Icon.api`, `Icon.code`, `Icon.bug`, `Icon.tool`, `Icon.robot`
127
+
128
+ ### Location
129
+ `Icon.map`, `Icon.compass`, `Icon.global`, `Icon.location`
130
+
131
+ ### Shapes & objects
132
+ `Icon.star`, `Icon.heart`, `Icon.flag`, `Icon.trophy`, `Icon.medal`, `Icon.rocket`, `Icon.fire`, `Icon.thunder`, `Icon.smile`, `Icon.frown`, `Icon.like`, `Icon.dislike`, `Icon.safety`, `Icon.key`
133
+
134
+ ---
135
+
136
+ ## Custom icons
137
+
138
+ Create your own renderer that wraps the base adapter. Use NativeDocument elements for reactive support:
139
+
140
+ ```javascript
141
+ import { Img, Span, ShowIf } from 'native-document/elements';
142
+ import TablerIconRender from 'native-document/ui/icon/tabler/TablerIconRender';
143
+
144
+ function MyIconRender($desc) {
145
+ // Handle your custom icons
146
+ if ($desc.name === 'logo' || $desc.name === 'dashboard') {
147
+ const size = $desc.size?.__$Observable
148
+ ? $desc.size.transform((s) => typeof s === 'number' ? `${s}px` : `${SIZES[s] ?? 24}px`)
149
+ : `${typeof $desc.size === 'number' ? $desc.size : 24}px`;
150
+
151
+ return Img({
152
+ src: `/icons/${$desc.name}.svg`,
153
+ 'aria-hidden': 'true',
154
+ style: {
155
+ width: size,
156
+ height: size,
157
+ color: $desc.color ?? 'currentColor',
158
+ },
159
+ });
160
+ }
161
+
162
+ // Fall back to Tabler for everything else
163
+ return TablerIconRender($desc);
164
+ }
165
+
166
+ Icon.use(MyIconRender, { size: 'medium' });
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Adapters
172
+
173
+ ### Tabler Icons
174
+
175
+ > Outline and filled variants - the standard set covers the most common icons from Tabler's library.
176
+
177
+ **Install**
178
+
179
+ ```bash
180
+ npm install @tabler/icons-webfont
181
+ ```
182
+
183
+ **main.js**
184
+
185
+ ```javascript
186
+ import { Icon } from 'native-document/icons';
187
+ import TablerIconRender from 'native-document/ui/icon/tabler';
188
+ import 'native-document/src/ui/theme.scss';
189
+ import '@tabler/icons-webfont/dist/tabler-icons.css';
190
+
191
+ // Uncomment to enable .fill() variant
192
+ // import '@tabler/icons-webfont/dist/tabler-icons-filled.css';
193
+
194
+ Icon.use(TablerIconRender, { variant: 'outline', size: 'medium' });
195
+ ```
196
+
197
+ **Supported variants**
198
+
199
+ | Variant | Description | Required CSS |
200
+ |---|---|---|
201
+ | `outline` (default) | Stroked icons | `tabler-icons.css` |
202
+ | `fill` | Filled icons | `tabler-icons-filled.css` |
203
+
204
+ **Example**
205
+
206
+ ```javascript
207
+ Icon.search // outline, medium
208
+ Icon.search.fill() // filled
209
+ Icon.star.fill().large() // filled, 32px
210
+ ```
211
+
212
+ ---
213
+
214
+ ### Phosphor Icons
215
+
216
+ > 6 weights - the standard set covers the most common icons from Phosphor's library.
217
+
218
+ **Install**
219
+
220
+ ```bash
221
+ npm install @phosphor-icons/web
222
+ ```
223
+
224
+ **main.js**
225
+
226
+ ```javascript
227
+ import { Icon } from 'native-document/icons';
228
+ import PhosphorIconRender from 'native-document/ui/icon/phosphor';
229
+ import 'native-document/src/ui/theme.scss';
230
+
231
+ // Check the exact path in your node_modules/@phosphor-icons/web/ directory
232
+ import '@phosphor-icons/web/src/regular/style.css';
233
+
234
+ // Import only the weights you need
235
+ // import '@phosphor-icons/web/src/thin/style.css';
236
+ // import '@phosphor-icons/web/src/light/style.css';
237
+ // import '@phosphor-icons/web/src/bold/style.css';
238
+ // import '@phosphor-icons/web/src/fill/style.css';
239
+ // import '@phosphor-icons/web/src/duotone/style.css';
240
+
241
+ Icon.use(PhosphorIconRender, { variant: 'regular', size: 'medium' });
242
+ ```
243
+
244
+ > The CSS path may vary depending on the installed version. If you get a resolve error, run `ls node_modules/@phosphor-icons/web/` to find the correct path.
245
+
246
+ **Supported variants**
247
+
248
+ | Variant | Description | Required CSS |
249
+ |---|---|---|
250
+ | `thin` | Thinnest stroke | `thin/style.css` |
251
+ | `light` | Light stroke | `light/style.css` |
252
+ | `regular` (default) | Standard stroke | `regular/style.css` |
253
+ | `bold` | Bold stroke | `bold/style.css` |
254
+ | `fill` | Filled | `fill/style.css` |
255
+ | `duotone` | Two-tone | `duotone/style.css` |
256
+
257
+ **Example**
258
+
259
+ ```javascript
260
+ Icon.search // regular, medium
261
+ Icon.search.bold() // bold
262
+ Icon.star.fill().large() // filled, 32px
263
+ Icon.heart.duotone() // duotone
264
+ ```
265
+
266
+ ---
267
+
268
+ ### Google Material Icons
269
+
270
+ > Filled, outlined, round, sharp, two-tone - the standard set covers the most common icons from Material's library.
271
+
272
+ **Install**
273
+
274
+ ```bash
275
+ npm install material-icons
276
+ ```
277
+
278
+ **main.js**
279
+
280
+ ```javascript
281
+ import { Icon } from 'native-document/icons';
282
+ import MaterialIconRender from 'native-document/ui/icon/material';
283
+ import 'native-document/src/ui/theme.scss';
284
+ import 'material-icons/iconfont/filled.css';
285
+
286
+ // Import only the styles you need
287
+ // import 'material-icons/iconfont/outlined.css';
288
+ // import 'material-icons/iconfont/round.css';
289
+ // import 'material-icons/iconfont/sharp.css';
290
+ // import 'material-icons/iconfont/two-tone.css';
291
+
292
+ Icon.use(MaterialIconRender, { variant: 'fill', size: 'medium' });
293
+ ```
294
+
295
+ **Supported variants**
296
+
297
+ | Variant | Description | Required CSS |
298
+ |---|---|---|
299
+ | `fill` / `regular` (default) | Filled icons | `filled.css` |
300
+ | `outline` | Outlined icons | `outlined.css` |
301
+ | `round` | Rounded icons | `round.css` |
302
+ | `sharp` | Sharp corners | `sharp.css` |
303
+ | `twoTone` | Two-tone icons | `two-tone.css` |
304
+
305
+ > Material Icons uses **ligatures** - the icon name is the text content of the element, converted to an icon by the font. This is handled automatically by the renderer.
306
+
307
+ **Example**
308
+
309
+ ```javascript
310
+ Icon.search // filled, medium
311
+ Icon.search.variant('outline') // outlined
312
+ Icon.star.large() // filled, 32px
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Next Steps
318
+
319
+ - **[Components Overview](./index.md)** - BaseComponent and renderer pattern
320
+ - **[Getting Started](./getting-started.md)** - Register default renderers
321
+ - **[Theming](../theming.md)** - CSS tokens reference
@@ -75,7 +75,7 @@ docs/
75
75
  ## Internal Links
76
76
 
77
77
  VitePress resolves links relative to the current file.
78
- Always use `.md` extension VitePress strips it at build time.
78
+ Always use `.md` extension - VitePress strips it at build time.
79
79
 
80
80
  ```markdown
81
81
  <!-- From docs/readme.md or docs/getting-started.md -->
@@ -93,7 +93,7 @@ Never use absolute paths (`/docs/...`) or paths without `.md`.
93
93
 
94
94
  ---
95
95
 
96
- ## `.vitepress/config.js` Sidebar & Nav
96
+ ## `.vitepress/config.js` - Sidebar & Nav
97
97
 
98
98
  ```javascript
99
99
  // .vitepress/config.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "native-document",
3
- "version": "1.0.168",
3
+ "version": "1.0.172",
4
4
  "description": "A reactive JavaScript framework that preserves native DOM simplicity without sacrificing modern features",
5
5
  "author": "AfroCodeur <https://github.com/afrocodeur>",
6
6
  "license": "MIT",
@@ -41,7 +41,11 @@
41
41
  "build": "rollup --config rollup.config.js",
42
42
  "build:watch": "rollup --config rollup.config.js --watch",
43
43
  "lint": "eslint ./src",
44
- "lint:fix": "eslint ./src --fix"
44
+ "lint:fix": "eslint ./src --fix",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:ui": "vitest --ui",
48
+ "test:coverage": "vitest run --coverage"
45
49
  },
46
50
  "dependencies": {
47
51
  "@floating-ui/dom": "^1.7.6",
@@ -53,14 +57,17 @@
53
57
  "@rollup/plugin-alias": "^5.1.1",
54
58
  "@rollup/plugin-replace": "^6.0.2",
55
59
  "@rollup/plugin-terser": "^0.4.4",
60
+ "@vitest/coverage-v8": "^4.1.8",
56
61
  "eslint": "^9.33.0",
57
62
  "eslint-plugin-jsdoc": "^62.5.4",
63
+ "happy-dom": "^20.10.2",
58
64
  "magic-string": "^0.30.21",
59
65
  "postcss": "^8.5.12",
60
66
  "rollup": "^4.53.3",
61
- "rollup-plugin-postcss": "^4.0.2"
67
+ "rollup-plugin-postcss": "^4.0.2",
68
+ "vitest": "^4.1.8"
62
69
  },
63
70
  "engines": {
64
71
  "node": ">=18.0.0"
65
72
  }
66
- }
73
+ }
@@ -181,6 +181,12 @@ BaseComponent.prototype.getEditableProps = function() {
181
181
  class: classPropertyAccumulator(rawProps.class || {}),
182
182
  style: cssPropertyAccumulator(rawProps.style || {}),
183
183
  };
184
+
185
+ if (this.aria) {
186
+ for (const key in this.aria) {
187
+ this.$editableProps[resolveAriaKey(key)] = this.aria[key];
188
+ }
189
+ }
184
190
  }
185
191
 
186
192
  return this.$editableProps;
@@ -244,4 +250,40 @@ BaseComponent.prototype.context = function(context) {
244
250
  return this;
245
251
  };
246
252
 
247
- BaseComponent.prototype.visibility = BaseComponent.prototype.showIf;
253
+ BaseComponent.prototype.visibility = BaseComponent.prototype.showIf;
254
+
255
+ //------------------------------------------------------------------------------
256
+ //--Component Aria -------------------------------------------------------------
257
+ //------------------------------------------------------------------------------
258
+ const ARIA_NO_PREFIX = new Set(['role', 'id', 'tabindex']);
259
+
260
+ /**
261
+ * @param {string} key
262
+ * @returns {string}
263
+ */
264
+ const resolveAriaKey = (key) =>
265
+ ARIA_NO_PREFIX.has(key) || key.startsWith('aria-') ? key : `aria-${key}`;
266
+
267
+ /**
268
+ * Override or extend aria attributes on the root element.
269
+ * Automatically prefixes keys with 'aria-' unless they are in ARIA_NO_PREFIX.
270
+ *
271
+ * @example
272
+ * Modal()
273
+ * .setAria({ label: 'Confirmation dialog' })
274
+ * .setAria({ describedby: 'modal-desc' })
275
+ *
276
+ * @param {Record<string, string|boolean|Observable>} attrs
277
+ * @returns {this}
278
+ */
279
+ BaseComponent.prototype.setAria = function(attrs) {
280
+ if (!this.aria) {
281
+ this.aria = {};
282
+ }
283
+ for (const key in attrs) {
284
+ this.aria[resolveAriaKey(key)] = attrs[key];
285
+ }
286
+ return this;
287
+ };
288
+
289
+
@@ -36,6 +36,7 @@ export default function Accordion(props = {}) {
36
36
  renderIndicator: null,
37
37
  props,
38
38
  };
39
+ this.aria = {};
39
40
  }
40
41
 
41
42
  BaseComponent.extends(Accordion);
@@ -41,6 +41,7 @@ export default function AccordionItem(props = {}) {
41
41
  disabled: $(false),
42
42
  props,
43
43
  };
44
+ this.aria = { 'tabindex': '0' };
44
45
  }
45
46
 
46
47
  BaseComponent.extends(AccordionItem);
@@ -44,6 +44,10 @@ export default function Alert(content, props = {}) {
44
44
  actions: [],
45
45
  props,
46
46
  };
47
+ this.aria = {
48
+ 'role': 'alert',
49
+ 'aria-live': 'assertive'
50
+ };
47
51
  }
48
52
 
49
53
  Alert.defaultTemplate = null;
@@ -42,6 +42,7 @@ export default function Avatar(source, props = {}) {
42
42
  render: null,
43
43
  props,
44
44
  };
45
+ this.aria = { 'role': 'img' };
45
46
  }
46
47
 
47
48
  BaseComponent.extends(Avatar);
@@ -31,6 +31,7 @@ export default function AvatarGroup(props = {}) {
31
31
  props,
32
32
  };
33
33
 
34
+ this.aria = { 'role': 'group' };
34
35
  };
35
36
 
36
37
  BaseComponent.extends(AvatarGroup);
@@ -36,6 +36,7 @@ export default function Badge(content, props = {}) {
36
36
  content,
37
37
  props,
38
38
  };
39
+ this.aria = {};
39
40
  }
40
41
 
41
42
  BaseComponent.extends(Badge);
@@ -21,6 +21,10 @@ export default function Breadcrumb(props = {}) {
21
21
  renderItem: null,
22
22
  props,
23
23
  };
24
+ this.aria = {
25
+ 'role': 'navigation',
26
+ 'aria-label': 'Breadcrumb'
27
+ };
24
28
  }
25
29
 
26
30
  BaseComponent.extends(Breadcrumb);
@@ -55,6 +55,7 @@ export default function Button(label, props = {}) {
55
55
  outline: null,
56
56
  props,
57
57
  };
58
+ this.aria = { 'role': 'button' };
58
59
 
59
60
  this.$element = null;
60
61
  }
@@ -45,6 +45,7 @@ export default function Card(props = {}) {
45
45
  layout: null,
46
46
  props,
47
47
  };
48
+ this.aria = {};
48
49
  }
49
50
 
50
51
  BaseComponent.extends(Card);
@@ -35,6 +35,8 @@ export default function ContextMenu(props = {}) {
35
35
  trigger: null,
36
36
  data: null,
37
37
  });
38
+
39
+ this.aria = { 'role': 'menu' };
38
40
  }
39
41
 
40
42
  BaseComponent.extends(ContextMenu);
@@ -13,6 +13,8 @@ export default function ContextMenuGroup(label, config) {
13
13
  }
14
14
 
15
15
  MenuGroup.call(this, label, config);
16
+
17
+ this.aria = { 'role': 'group' };
16
18
  }
17
19
 
18
20
 
@@ -12,6 +12,8 @@ export default function ContextMenuItem(config) {
12
12
  }
13
13
 
14
14
  MenuItem.call(this, config);
15
+
16
+ this.aria = { 'role': 'menuitem' };
15
17
  }
16
18
 
17
19
 
@@ -41,6 +41,8 @@ export default function Divider(label, props = {}) {
41
41
  trailing: null,
42
42
  props,
43
43
  };
44
+
45
+ this.aria = { 'role': 'separator' };
44
46
  }
45
47
 
46
48
  BaseComponent.extends(Divider);
@@ -66,6 +66,7 @@ export default function Dropdown(props = {}) {
66
66
  includeTriggerIntoGhost: true,
67
67
  props,
68
68
  };
69
+ this.aria = { 'role': 'listbox' };
69
70
  }
70
71
 
71
72
  BaseComponent.extends(Dropdown);
@@ -18,6 +18,7 @@ export default function DropdownDivider(props = {}) {
18
18
  type: 'divider',
19
19
  props,
20
20
  };
21
+ this.aria = { 'role': 'separator' };
21
22
  }
22
23
 
23
24
  BaseComponent.extends(DropdownDivider);
@@ -27,6 +27,7 @@ export default function DropdownGroup(props) {
27
27
  Object.assign(this.$description, {
28
28
  items: $.array([]),
29
29
  });
30
+ this.aria = { 'role': 'group' };
30
31
  }
31
32
 
32
33
  BaseComponent.extends(DropdownGroup, DropdownItem);
@@ -38,6 +38,7 @@ export default function DropdownItem(props = {}) {
38
38
  renderContent: null,
39
39
  props,
40
40
  };
41
+ this.aria = { 'role': 'option', 'tabindex': '-1' };
41
42
  };
42
43
 
43
44
  BaseComponent.extends(DropdownItem);
@@ -26,6 +26,7 @@ export default function DropdownTrigger(config) {
26
26
  isOpen: null,
27
27
  ...config,
28
28
  };
29
+ this.aria = { 'aria-haspopup': 'listbox' };
29
30
  }
30
31
 
31
32
  BaseComponent.extends(DropdownTrigger);
@@ -47,6 +47,7 @@ export default function FormControl(props) {
47
47
  isValid: $(false),
48
48
  props,
49
49
  };
50
+ this.aria = { 'role': 'form' };
50
51
  }
51
52
 
52
53
  FormControl.defaultTemplate = null;
@@ -64,6 +64,7 @@ export default function Field(name, type, props) {
64
64
  slots: {},
65
65
  props,
66
66
  };
67
+ this.aria = { 'role': 'group' };
67
68
 
68
69
  this.$description.errors.intercept((nextValue) => nextValue === null ? [] : nextValue);
69
70
  }
@@ -13,7 +13,7 @@ import FileItemPreview from '../../field/types/file-field-mode/FileItemPreview';
13
13
  * .accept(['application/pdf', 'image/png'])
14
14
  * .maxSize(5 * 1024 * 1024, 'Max 5MB per file')
15
15
  * .maxFiles(3, 'Max 3 files')
16
- * .mode('dropzone')
16
+ * .mode(FileDropzoneMode())
17
17
  * .onFileAdd((file) => console.log('added', file.name))
18
18
  * .onFileRemove((file) => console.log('removed', file.name));
19
19
  *
@@ -54,7 +54,7 @@ FileField.defaultTemplate = null;
54
54
  * label: NdChild|null,
55
55
  * accept: string|null,
56
56
  * multiple: boolean,
57
- * mode: 'native'|'dropzone'|'button'|'wall'|'avatar'|null,
57
+ * mode: FileNativeMode|FileAvatarMode|FileDropzoneMode|FileUploadButtonMode|FileWallMode|null,
58
58
  * files: Observable<FileItemPreview[]>,
59
59
  * fileIcons: Array<(file: File) => NdChild>,
60
60
  * disabled: boolean|Observable<boolean>,
@@ -91,7 +91,7 @@ FileField.prototype.multiple = function(enabled = true) {
91
91
  };
92
92
 
93
93
  /**
94
- * @param {'native'|'dropzone'|'button'|'wall'|'avatar'} mode
94
+ * @param {FileNativeMode|FileAvatarMode|FileDropzoneMode|FileUploadButtonMode|FileWallMode|null} mode
95
95
  * @returns {this}
96
96
  */
97
97
  FileField.prototype.mode = function(mode) {
@@ -26,7 +26,7 @@ import {Validation} from '../../validation/Validation';
26
26
  */
27
27
  export default function StringField(name, type = 'text', props = {}) {
28
28
  if(!(this instanceof StringField)) {
29
- return new StringField(name, props);
29
+ return new StringField(name, type, props);
30
30
  }
31
31
 
32
32
  Field.call(this, name, type, props);