native-document 1.0.165 → 1.0.168

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.d.ts +2 -0
  4. package/components.js +2 -1
  5. package/devtools/widget.js +1 -1
  6. package/dist/native-document.components.min.js +11589 -2983
  7. package/dist/native-document.dev.js +2280 -396
  8. package/dist/native-document.dev.js.map +1 -1
  9. package/dist/native-document.min.js +1 -1
  10. package/docs/advanced-components.md +213 -608
  11. package/docs/anchor.md +173 -312
  12. package/docs/cache.md +95 -803
  13. package/docs/cli.md +179 -0
  14. package/docs/components/accordion.md +172 -0
  15. package/docs/components/alert.md +99 -0
  16. package/docs/components/avatar.md +160 -0
  17. package/docs/components/badge.md +102 -0
  18. package/docs/components/breadcrumb.md +89 -0
  19. package/docs/components/button.md +183 -0
  20. package/docs/components/card.md +69 -0
  21. package/docs/components/context-menu.md +118 -0
  22. package/docs/components/data-table.md +345 -0
  23. package/docs/components/dropdown.md +214 -0
  24. package/docs/components/form/autocomplete-field.md +81 -0
  25. package/docs/components/form/checkbox-field.md +41 -0
  26. package/docs/components/form/checkbox-group-field.md +54 -0
  27. package/docs/components/form/color-field.md +64 -0
  28. package/docs/components/form/date-field.md +92 -0
  29. package/docs/components/form/field-collection.md +63 -0
  30. package/docs/components/form/file-field.md +203 -0
  31. package/docs/components/form/form-control.md +87 -0
  32. package/docs/components/form/image-field.md +90 -0
  33. package/docs/components/form/index.md +115 -0
  34. package/docs/components/form/number-field.md +65 -0
  35. package/docs/components/form/radio-field.md +51 -0
  36. package/docs/components/form/select-field.md +123 -0
  37. package/docs/components/form/slider.md +136 -0
  38. package/docs/components/form/string-field.md +134 -0
  39. package/docs/components/form/textarea-field.md +65 -0
  40. package/docs/components/form-fields.md +372 -0
  41. package/docs/components/getting-started.md +264 -0
  42. package/docs/components/index.md +337 -0
  43. package/docs/components/layout.md +279 -0
  44. package/docs/components/list.md +73 -0
  45. package/docs/components/menu.md +215 -0
  46. package/docs/components/modal.md +156 -0
  47. package/docs/components/pagination.md +95 -0
  48. package/docs/components/popover.md +131 -0
  49. package/docs/components/progress.md +111 -0
  50. package/docs/components/shortcut-manager.md +221 -0
  51. package/docs/components/simple-table.md +107 -0
  52. package/docs/components/skeleton.md +155 -0
  53. package/docs/components/spinner.md +100 -0
  54. package/docs/components/splitter.md +133 -0
  55. package/docs/components/stepper.md +163 -0
  56. package/docs/components/switch.md +113 -0
  57. package/docs/components/tabs.md +153 -0
  58. package/docs/components/toast.md +119 -0
  59. package/docs/components/tooltip.md +151 -0
  60. package/docs/components/traits.md +261 -0
  61. package/docs/conditional-rendering.md +170 -588
  62. package/docs/contributing.md +300 -25
  63. package/docs/core-concepts.md +205 -374
  64. package/docs/elements.md +251 -367
  65. package/docs/extending-native-document-element.md +192 -207
  66. package/docs/filters.md +153 -1122
  67. package/docs/getting-started.md +193 -267
  68. package/docs/i18n.md +241 -0
  69. package/docs/index.md +76 -0
  70. package/docs/lifecycle-events.md +143 -75
  71. package/docs/list-rendering.md +227 -852
  72. package/docs/memory-management.md +134 -47
  73. package/docs/native-document-element.md +337 -186
  74. package/docs/native-fetch.md +99 -630
  75. package/docs/observable-resource.md +364 -0
  76. package/docs/observables.md +592 -526
  77. package/docs/routing.md +244 -653
  78. package/docs/state-management.md +134 -241
  79. package/docs/svg-elements.md +231 -0
  80. package/docs/theming.md +409 -0
  81. package/docs/validation.md +95 -97
  82. package/docs/vitepress-conventions.md +219 -0
  83. package/eslint.config.js +28 -33
  84. package/i18n.js +1 -1
  85. package/i18n.ts +2 -0
  86. package/index.js +3 -0
  87. package/package.json +36 -14
  88. package/readme.md +269 -89
  89. package/src/components/$traits/has-draggable/HasDraggable.d.ts +4 -0
  90. package/src/components/$traits/has-draggable/HasDraggable.js +13 -0
  91. package/src/components/$traits/has-items/HasItems.d.ts +9 -0
  92. package/src/components/$traits/has-items/HasItems.js +6 -6
  93. package/src/components/$traits/has-position/HasFullPosition.d.ts +14 -0
  94. package/src/components/$traits/has-position/HasFullPosition.js +44 -0
  95. package/src/components/$traits/has-position/HasPosition.d.ts +7 -0
  96. package/src/components/$traits/has-position/HasPosition.js +23 -1
  97. package/src/components/$traits/has-resizable/HasResizable.d.ts +13 -0
  98. package/src/components/$traits/has-resizable/HasResizable.js +9 -0
  99. package/src/components/$traits/has-validation/HasValidation.d.ts +17 -0
  100. package/src/components/$traits/has-validation/HasValidation.js +54 -7
  101. package/src/components/BaseComponent.d.ts +32 -0
  102. package/src/components/BaseComponent.js +65 -9
  103. package/src/components/accordion/Accordion.js +39 -14
  104. package/src/components/accordion/AccordionItem.js +45 -14
  105. package/src/components/accordion/index.js +2 -2
  106. package/src/components/accordion/types/Accordion.d.ts +47 -0
  107. package/src/components/accordion/types/AccordionItem.d.ts +48 -0
  108. package/src/components/alert/Alert.js +70 -38
  109. package/src/components/alert/index.js +2 -2
  110. package/src/components/alert/types/Alert.d.ts +62 -0
  111. package/src/components/avatar/Avatar.js +49 -12
  112. package/src/components/avatar/AvatarGroup.js +50 -2
  113. package/src/components/avatar/index.js +2 -2
  114. package/src/components/avatar/types/Avatar.d.ts +74 -0
  115. package/src/components/avatar/types/AvatarGroup.d.ts +32 -0
  116. package/src/components/badge/Badge.js +125 -5
  117. package/src/components/badge/index.js +2 -2
  118. package/src/components/badge/types/Badge.d.ts +51 -0
  119. package/src/components/breadcrumb/BreadCrumb.js +61 -5
  120. package/src/components/breadcrumb/index.js +2 -2
  121. package/src/components/breadcrumb/types/BreadCrumb.d.ts +42 -0
  122. package/src/components/button/Button.js +164 -9
  123. package/src/components/button/index.js +1 -1
  124. package/src/components/button/types/Button.d.ts +62 -0
  125. package/src/components/card/Card.js +204 -32
  126. package/src/components/card/index.js +4 -4
  127. package/src/components/card/types/Card.d.ts +42 -0
  128. package/src/components/context-menu/ContextMenu.js +49 -5
  129. package/src/components/context-menu/ContextMenuGroup.js +15 -2
  130. package/src/components/context-menu/ContextMenuItem.js +14 -2
  131. package/src/components/context-menu/index.js +5 -5
  132. package/src/components/context-menu/types/ContextMenu.d.ts +30 -0
  133. package/src/components/context-menu/types/ContextMenuGroup.d.ts +18 -0
  134. package/src/components/context-menu/types/ContextMenuItem.d.ts +18 -0
  135. package/src/components/divider/Divider.js +120 -4
  136. package/src/components/divider/index.js +3 -3
  137. package/src/components/divider/types/Divider.d.ts +55 -0
  138. package/src/components/dropdown/Dropdown.js +239 -16
  139. package/src/components/dropdown/DropdownDivider.js +22 -2
  140. package/src/components/dropdown/DropdownGroup.js +44 -5
  141. package/src/components/dropdown/DropdownItem.js +76 -3
  142. package/src/components/dropdown/DropdownTrigger.js +49 -20
  143. package/src/components/dropdown/helpers.js +1 -1
  144. package/src/components/dropdown/index.js +6 -6
  145. package/src/components/dropdown/types/Dropdown.d.ts +88 -0
  146. package/src/components/dropdown/types/DropdownDivider.d.ts +20 -0
  147. package/src/components/dropdown/types/DropdownGroup.d.ts +25 -0
  148. package/src/components/dropdown/types/DropdownItem.d.ts +41 -0
  149. package/src/components/dropdown/types/DropdownTrigger.d.ts +32 -0
  150. package/src/components/form/FormControl.js +156 -13
  151. package/src/components/form/field/Field.js +172 -9
  152. package/src/components/form/field/FieldCollection.js +116 -12
  153. package/src/components/form/field/types/AutocompleteField.js +92 -2
  154. package/src/components/form/field/types/CheckboxField.js +43 -2
  155. package/src/components/form/field/types/CheckboxGroupField.js +83 -6
  156. package/src/components/form/field/types/ColorField.js +56 -3
  157. package/src/components/form/field/types/DateField.js +155 -4
  158. package/src/components/form/field/types/EmailField.js +54 -4
  159. package/src/components/form/field/types/FileField.js +140 -6
  160. package/src/components/form/field/types/HiddenField.js +27 -1
  161. package/src/components/form/field/types/ImageField.js +82 -3
  162. package/src/components/form/field/types/NumberField.js +97 -4
  163. package/src/components/form/field/types/PasswordField.js +103 -7
  164. package/src/components/form/field/types/RadioField.js +75 -4
  165. package/src/components/form/field/types/RangeField.js +67 -1
  166. package/src/components/form/field/types/SearchField.js +41 -2
  167. package/src/components/form/field/types/SelectField.js +133 -4
  168. package/src/components/form/field/types/StringField.js +91 -2
  169. package/src/components/form/field/types/TelField.js +55 -4
  170. package/src/components/form/field/types/TextAreaField.js +76 -2
  171. package/src/components/form/field/types/TimeField.js +120 -5
  172. package/src/components/form/field/types/UrlField.js +59 -4
  173. package/src/components/form/field/types/file-field-mode/FileAvatarMode.js +83 -4
  174. package/src/components/form/field/types/file-field-mode/FileDropzoneMode.js +61 -3
  175. package/src/components/form/field/types/file-field-mode/FileItemPreview.js +79 -3
  176. package/src/components/form/field/types/file-field-mode/FileNativeMode.js +24 -2
  177. package/src/components/form/field/types/file-field-mode/FileUploadButtonMode.js +64 -3
  178. package/src/components/form/field/types/file-field-mode/FileWallMode.js +56 -3
  179. package/src/components/form/index.js +28 -28
  180. package/src/components/form/types/Field.d.ts +73 -0
  181. package/src/components/form/types/FieldCollection.d.ts +53 -0
  182. package/src/components/form/types/FormControl.d.ts +64 -0
  183. package/src/components/form/types/fields/AutocompleteField.d.ts +48 -0
  184. package/src/components/form/types/fields/CheckboxField.d.ts +33 -0
  185. package/src/components/form/types/fields/CheckboxGroupField.d.ts +49 -0
  186. package/src/components/form/types/fields/ColorField.d.ts +37 -0
  187. package/src/components/form/types/fields/DateField.d.ts +70 -0
  188. package/src/components/form/types/fields/EmailField.d.ts +35 -0
  189. package/src/components/form/types/fields/FileAvatarMode.d.ts +46 -0
  190. package/src/components/form/types/fields/FileDropzoneMode.d.ts +28 -0
  191. package/src/components/form/types/fields/FileField.d.ts +56 -0
  192. package/src/components/form/types/fields/FileItemPreview.d.ts +35 -0
  193. package/src/components/form/types/fields/FileNativeMode.d.ts +21 -0
  194. package/src/components/form/types/fields/FileUploadButtonMode.d.ts +34 -0
  195. package/src/components/form/types/fields/FileWallMode.d.ts +32 -0
  196. package/src/components/form/types/fields/HiddenField.d.ts +26 -0
  197. package/src/components/form/types/fields/ImageField.d.ts +45 -0
  198. package/src/components/form/types/fields/NumberField.d.ts +48 -0
  199. package/src/components/form/types/fields/PasswordField.d.ts +46 -0
  200. package/src/components/form/types/fields/RadioField.d.ts +48 -0
  201. package/src/components/form/types/fields/RangeField.d.ts +44 -0
  202. package/src/components/form/types/fields/SearchField.d.ts +34 -0
  203. package/src/components/form/types/fields/SelectField.d.ts +71 -0
  204. package/src/components/form/types/fields/StringField.d.ts +48 -0
  205. package/src/components/form/types/fields/TelField.d.ts +37 -0
  206. package/src/components/form/types/fields/TextAreaField.d.ts +44 -0
  207. package/src/components/form/types/fields/TimeField.d.ts +51 -0
  208. package/src/components/form/types/fields/UrlField.d.ts +35 -0
  209. package/src/components/form/validation/Validation.js +54 -54
  210. package/src/components/index.d.ts +160 -0
  211. package/src/components/list/HasListItem.js +171 -0
  212. package/src/components/list/List.js +85 -67
  213. package/src/components/list/ListDivider.js +39 -0
  214. package/src/components/list/ListGroup.js +105 -38
  215. package/src/components/list/ListItem.js +158 -49
  216. package/src/components/list/index.js +8 -6
  217. package/src/components/list/types/List.d.ts +43 -0
  218. package/src/components/list/types/ListGroup.d.ts +37 -0
  219. package/src/components/list/types/ListItem.d.ts +53 -0
  220. package/src/components/menu/HasMenuItem.js +55 -6
  221. package/src/components/menu/Menu.js +113 -22
  222. package/src/components/menu/MenuDivider.js +18 -2
  223. package/src/components/menu/MenuGroup.js +61 -6
  224. package/src/components/menu/MenuItem.js +95 -11
  225. package/src/components/menu/MenuLink.js +27 -2
  226. package/src/components/menu/index.js +6 -6
  227. package/src/components/menu/types/Menu.d.ts +60 -0
  228. package/src/components/menu/types/MenuDivider.d.ts +19 -0
  229. package/src/components/menu/types/MenuGroup.d.ts +44 -0
  230. package/src/components/menu/types/MenuItem.d.ts +46 -0
  231. package/src/components/menu/types/MenuLink.d.ts +16 -0
  232. package/src/components/modal/Modal.js +258 -17
  233. package/src/components/modal/index.js +3 -3
  234. package/src/components/modal/types/Modal.d.ts +94 -0
  235. package/src/components/pagination/Pagination.js +155 -7
  236. package/src/components/pagination/index.js +3 -3
  237. package/src/components/pagination/types/Pagination.d.ts +68 -0
  238. package/src/components/popover/Popover.js +198 -11
  239. package/src/components/popover/PopoverFooter.js +33 -9
  240. package/src/components/popover/PopoverHeader.js +33 -8
  241. package/src/components/popover/index.js +4 -4
  242. package/src/components/popover/types/Popover.d.ts +83 -0
  243. package/src/components/popover/types/PopoverFooter.d.ts +24 -0
  244. package/src/components/popover/types/PopoverHeader.d.ts +26 -0
  245. package/src/components/progress/Progress.js +182 -13
  246. package/src/components/progress/index.js +3 -3
  247. package/src/components/progress/types/Progress.d.ts +77 -0
  248. package/src/components/skeleton/Skeleton.js +117 -49
  249. package/src/components/skeleton/index.js +3 -3
  250. package/src/components/skeleton/types/Skeleton.d.ts +55 -0
  251. package/src/components/slider/Slider.js +207 -10
  252. package/src/components/slider/index.js +2 -2
  253. package/src/components/slider/types/Slider.d.ts +82 -0
  254. package/src/components/spacer/Spacer.js +12 -3
  255. package/src/components/spacer/index.js +2 -2
  256. package/src/components/spacer/types/Spacer.d.ts +19 -0
  257. package/src/components/spinner/Spinner.js +180 -9
  258. package/src/components/spinner/index.js +3 -3
  259. package/src/components/spinner/types/Spinner.d.ts +71 -0
  260. package/src/components/splitter/Splitter.js +76 -13
  261. package/src/components/splitter/SplitterGutter.js +67 -5
  262. package/src/components/splitter/SplitterPanel.js +69 -2
  263. package/src/components/splitter/index.js +5 -5
  264. package/src/components/splitter/types/Splitter.d.ts +38 -0
  265. package/src/components/splitter/types/SplitterGutter.d.ts +38 -0
  266. package/src/components/splitter/types/SplitterPanel.d.ts +41 -0
  267. package/src/components/stacks/AbsoluteStack.js +23 -3
  268. package/src/components/stacks/FixedStack.js +23 -3
  269. package/src/components/stacks/HStack.js +24 -3
  270. package/src/components/stacks/PositionStack.js +111 -3
  271. package/src/components/stacks/RelativeStack.js +23 -3
  272. package/src/components/stacks/Stack.js +73 -2
  273. package/src/components/stacks/VStack.js +24 -4
  274. package/src/components/stacks/index.js +7 -7
  275. package/src/components/stacks/types/AbsoluteStack.d.ts +16 -0
  276. package/src/components/stacks/types/FixedStack.d.ts +16 -0
  277. package/src/components/stacks/types/HStack.d.ts +16 -0
  278. package/src/components/stacks/types/PositionStack.d.ts +54 -0
  279. package/src/components/stacks/types/RelativeStack.d.ts +17 -0
  280. package/src/components/stacks/types/Stack.d.ts +39 -0
  281. package/src/components/stacks/types/VStack.d.ts +16 -0
  282. package/src/components/stepper/Stepper.js +152 -12
  283. package/src/components/stepper/StepperStep.js +104 -3
  284. package/src/components/stepper/index.js +4 -4
  285. package/src/components/stepper/types/Stepper.d.ts +68 -0
  286. package/src/components/stepper/types/StepperStep.d.ts +54 -0
  287. package/src/components/switch/Switch.js +143 -6
  288. package/src/components/switch/index.js +1 -1
  289. package/src/components/switch/types/Switch.d.ts +55 -0
  290. package/src/components/table/Column.js +105 -6
  291. package/src/components/table/ColumnGroup.js +48 -3
  292. package/src/components/table/DataTable.js +256 -19
  293. package/src/components/table/SimpleTable.js +58 -4
  294. package/src/components/table/index.js +2 -2
  295. package/src/components/table/types/Column.d.ts +49 -0
  296. package/src/components/table/types/ColumnGroup.d.ts +28 -0
  297. package/src/components/table/types/DataTable.d.ts +97 -0
  298. package/src/components/table/types/SimpleTable.d.ts +40 -0
  299. package/src/components/tabs/Tabs.js +192 -5
  300. package/src/components/tabs/index.js +3 -3
  301. package/src/components/tabs/types/Tabs.d.ts +78 -0
  302. package/src/components/toast/Toast.js +133 -5
  303. package/src/components/toast/index.js +3 -3
  304. package/src/components/toast/types/Toast.d.ts +57 -0
  305. package/src/components/toast/types/ToastError.d.ts +7 -0
  306. package/src/components/toast/types/ToastInfo.d.ts +7 -0
  307. package/src/components/toast/types/ToastSuccess.d.ts +7 -0
  308. package/src/components/toast/types/ToastWarning.d.ts +7 -0
  309. package/src/components/tooltip/Tooltip.js +157 -13
  310. package/src/components/tooltip/index.js +2 -2
  311. package/src/components/tooltip/prototypes.js +1 -1
  312. package/src/components/tooltip/types/Tooltip.d.ts +65 -0
  313. package/src/core/data/MemoryManager.js +2 -2
  314. package/src/core/data/Observable.js +15 -18
  315. package/src/core/data/ObservableArray.js +118 -46
  316. package/src/core/data/ObservableChecker.js +2 -2
  317. package/src/core/data/ObservableItem.js +135 -21
  318. package/src/core/data/ObservableObject.js +126 -35
  319. package/src/core/data/ObservableResource.js +118 -3
  320. package/src/core/data/Store.js +142 -26
  321. package/src/core/data/observable-helpers/observable.is-to.js +196 -1
  322. package/src/core/data/observable-helpers/observable.prototypes.js +35 -8
  323. package/src/core/elements/anchor/anchor-with-sentinel.js +23 -2
  324. package/src/core/elements/anchor/anchor.js +16 -7
  325. package/src/core/elements/anchor/one-child-anchor-overwriting.js +2 -2
  326. package/src/core/elements/content-formatter.js +1 -1
  327. package/src/core/elements/control/for-each-array.js +9 -9
  328. package/src/core/elements/control/for-each.js +14 -14
  329. package/src/core/elements/control/show-if.js +11 -11
  330. package/src/core/elements/control/show-when.js +5 -5
  331. package/src/core/elements/control/switch.js +14 -14
  332. package/src/core/elements/description-list.js +1 -1
  333. package/src/core/elements/form.js +2 -2
  334. package/src/core/elements/fragment.js +1 -1
  335. package/src/core/elements/html5-semantics.js +1 -1
  336. package/src/core/elements/img.js +3 -3
  337. package/src/core/elements/interactive.js +1 -1
  338. package/src/core/elements/list.js +1 -1
  339. package/src/core/elements/medias.js +1 -1
  340. package/src/core/elements/meta-data.js +1 -1
  341. package/src/core/elements/svg.js +1 -1
  342. package/src/core/elements/table.js +1 -1
  343. package/src/core/errors/ArgTypesError.js +1 -1
  344. package/src/core/utils/HasEventEmitter.js +36 -2
  345. package/src/core/utils/args-types.js +9 -9
  346. package/src/core/utils/cache.js +1 -1
  347. package/src/core/utils/callback-handler.js +29 -0
  348. package/src/core/utils/debug-manager.js +6 -6
  349. package/src/core/utils/events.js +139 -139
  350. package/src/core/utils/filters/date.js +84 -3
  351. package/src/core/utils/filters/standard.js +136 -11
  352. package/src/core/utils/filters/strings.js +34 -2
  353. package/src/core/utils/filters/utils.js +40 -4
  354. package/src/core/utils/formatters.js +4 -4
  355. package/src/core/utils/helpers.js +39 -7
  356. package/src/core/utils/localstorage.js +11 -11
  357. package/src/core/utils/memoize.js +56 -3
  358. package/src/core/utils/plugins-manager.js +3 -3
  359. package/src/core/utils/property-accumulator.js +6 -6
  360. package/src/core/utils/prototypes.js +26 -1
  361. package/src/core/utils/shortcut-manager.js +2 -2
  362. package/src/core/utils/validator.js +8 -8
  363. package/src/core/wrappers/AttributesWrapper.js +32 -22
  364. package/src/core/wrappers/DocumentObserver.js +3 -3
  365. package/src/core/wrappers/ElementCreator.js +5 -5
  366. package/src/core/wrappers/HtmlElementWrapper.js +38 -12
  367. package/src/core/wrappers/NDElement.js +328 -22
  368. package/src/core/wrappers/NdPrototype.js +60 -16
  369. package/src/core/wrappers/SingletonView.js +50 -2
  370. package/src/core/wrappers/SvgElementWrapper.js +1 -1
  371. package/src/core/wrappers/constants.js +35 -2
  372. package/src/core/wrappers/prototypes/attributes-extensions.js +7 -7
  373. package/src/core/wrappers/prototypes/nd-element-extensions.js +72 -6
  374. package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +42 -2
  375. package/src/core/wrappers/template-cloner/NodeCloner.js +53 -8
  376. package/src/core/wrappers/template-cloner/TemplateCloner.js +75 -6
  377. package/src/core/wrappers/template-cloner/attributes-hydrator.js +58 -2
  378. package/src/core/wrappers/template-cloner/utils.js +42 -6
  379. package/src/fetch/NativeFetch.js +3 -3
  380. package/src/i18n/bin/scan.js +6 -6
  381. package/src/i18n/index.d.ts +2 -0
  382. package/src/i18n/service/I18nService.d.ts +27 -0
  383. package/src/i18n/service/I18nService.js +5 -5
  384. package/src/i18n/service/functions.d.ts +22 -0
  385. package/src/i18n/service/functions.js +2 -2
  386. package/src/router/Route.js +3 -3
  387. package/src/router/RouteGroupHelper.js +2 -2
  388. package/src/router/Router.js +15 -15
  389. package/src/router/RouterComponent.js +33 -7
  390. package/src/router/link.js +4 -4
  391. package/src/router/modes/HashRouter.js +2 -2
  392. package/src/router/modes/HistoryRouter.js +2 -2
  393. package/src/router/modes/MemoryRouter.js +1 -1
  394. package/src/ui/components/accordion/AccordionItemRender.js +3 -3
  395. package/src/ui/components/accordion/AccordionRender.js +1 -1
  396. package/src/ui/components/alert/AlertRender.js +10 -10
  397. package/src/ui/components/avatar/avata-group/AvatarGroupRender.js +1 -1
  398. package/src/ui/components/avatar/avatar/AvatarRender.js +1 -1
  399. package/src/ui/components/breadcrumb/BreadcrumbRender.js +2 -2
  400. package/src/ui/components/button/ButtonRender.js +1 -1
  401. package/src/ui/components/card/CardRender.js +133 -0
  402. package/src/ui/components/card/card.css +169 -0
  403. package/src/ui/components/contextmenu/ContextmenuRender.js +6 -6
  404. package/src/ui/components/dropdown/DropdownRender.js +8 -8
  405. package/src/ui/components/dropdown/group/DropdownGroupRender.js +2 -2
  406. package/src/ui/components/dropdown/item/DropdownItemRender.js +1 -1
  407. package/src/ui/components/form/FieldCollectionRender.js +2 -2
  408. package/src/ui/components/form/FormControlRender.js +5 -5
  409. package/src/ui/components/form/fields/AutocompleteFieldRender.js +3 -3
  410. package/src/ui/components/form/fields/CheckboxFieldRender.js +1 -1
  411. package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +1 -1
  412. package/src/ui/components/form/fields/DateFieldRender.js +7 -7
  413. package/src/ui/components/form/fields/EmailFieldRender.js +1 -1
  414. package/src/ui/components/form/fields/FieldRender.js +4 -4
  415. package/src/ui/components/form/fields/FileFieldRender.js +1 -1
  416. package/src/ui/components/form/fields/PasswordFieldRender.js +2 -2
  417. package/src/ui/components/form/fields/RadioFieldRender.js +1 -1
  418. package/src/ui/components/form/fields/RangeFieldRender.js +1 -1
  419. package/src/ui/components/form/fields/SelectFieldRender.js +2 -2
  420. package/src/ui/components/form/fields/SliderFieldRender.js +6 -6
  421. package/src/ui/components/form/fields/StringFieldRender.js +1 -1
  422. package/src/ui/components/form/fields/TelFieldRender.js +1 -1
  423. package/src/ui/components/form/fields/TextAreaFieldRender.js +1 -1
  424. package/src/ui/components/form/fields/TimeFieldRender.js +3 -3
  425. package/src/ui/components/form/fields/UrlFieldRender.js +1 -1
  426. package/src/ui/components/form/file-upload-mode/FileAvatarModeRender.js +1 -1
  427. package/src/ui/components/form/file-upload-mode/FileDropzoneModeRender.js +2 -2
  428. package/src/ui/components/form/file-upload-mode/FileUploadButtonModeRender.js +2 -2
  429. package/src/ui/components/form/file-upload-mode/FileWallModeRender.js +1 -1
  430. package/src/ui/components/form/helpers.js +8 -8
  431. package/src/ui/components/form/index.js +27 -27
  432. package/src/ui/components/list/ListRender.js +18 -0
  433. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  434. package/src/ui/components/list/divider/list-divider.css +12 -0
  435. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  436. package/src/ui/components/list/group/list-group.css +62 -0
  437. package/src/ui/components/list/item/ListItemRender.js +238 -0
  438. package/src/ui/components/list/item/list-item.css +191 -0
  439. package/src/ui/components/list/list.css +24 -0
  440. package/src/ui/components/menu/MenuDividerRender.js +1 -1
  441. package/src/ui/components/menu/MenuGroupRender.js +3 -3
  442. package/src/ui/components/menu/MenuItemRender.js +2 -2
  443. package/src/ui/components/menu/MenuLinkRender.js +3 -3
  444. package/src/ui/components/menu/helpers.js +4 -4
  445. package/src/ui/components/modal/ModalRender.js +4 -4
  446. package/src/ui/components/pagination/PaginationRender.js +9 -9
  447. package/src/ui/components/popover/PopoverRender.js +7 -7
  448. package/src/ui/components/progress/ProgressRender.js +12 -12
  449. package/src/ui/components/skeleton/SkeletonRender.js +56 -0
  450. package/src/ui/components/spacer/SpacerRender.js +10 -0
  451. package/src/ui/components/splitter/SplitterGutterRender.js +1 -1
  452. package/src/ui/components/splitter/SplitterPanelRender.js +2 -2
  453. package/src/ui/components/stacks/PositionStackRender.js +1 -1
  454. package/src/ui/components/stacks/StackRender.js +1 -1
  455. package/src/ui/components/stacks/absolute-stack/AbsoluteStackRender.js +1 -1
  456. package/src/ui/components/stacks/fixed-stack/FixedStackRender.js +1 -1
  457. package/src/ui/components/stacks/h-stack/HStackRender.js +1 -1
  458. package/src/ui/components/stacks/index.js +5 -5
  459. package/src/ui/components/stacks/relative-stack/RelativeStackRender.js +1 -1
  460. package/src/ui/components/stacks/v-stack/VStackRender.js +1 -1
  461. package/src/ui/components/stepper/StepperRender.js +2 -2
  462. package/src/ui/components/stepper/StepperStepRender.js +4 -4
  463. package/src/ui/components/switch/SwitchRender.js +4 -4
  464. package/src/ui/components/table/data-table/DataTableRender.js +5 -5
  465. package/src/ui/components/table/data-table/bulk-actions.js +7 -7
  466. package/src/ui/components/table/data-table/pagination.js +6 -6
  467. package/src/ui/components/table/data-table/tables.js +25 -25
  468. package/src/ui/components/table/data-table/toolbar.js +3 -3
  469. package/src/ui/components/table/simple-table/SimpleTableRender.js +8 -8
  470. package/src/ui/components/tabs/TabsRender.js +11 -11
  471. package/src/ui/components/toast/ToastRender.js +3 -3
  472. package/src/ui/components/tooltip/TooltipRender.js +1 -1
  473. package/src/ui/index.js +44 -36
  474. package/types/elements.d.ts +163 -1037
  475. package/types/forms.d.ts +16 -20
  476. package/types/globals.d.ts +543 -0
  477. package/types/images.d.ts +2 -2
  478. package/types/observable-resource.d.ts +3 -0
  479. package/types/property-accumulator.d.ts +4 -4
  480. package/types/store.d.ts +26 -2
  481. package/types/validator.ts +3 -3
  482. package/ui.js +1 -0
  483. package/src/components/form/field/DefaultRender.js +0 -77
  484. package/src/components/form/field/FieldFactory.js +0 -107
  485. package/src/components/skeleton/SkeletonList.js +0 -0
  486. package/src/components/skeleton/SkeletonParagraph.js +0 -0
  487. package/src/components/skeleton/SkeletonTable.js +0 -0
  488. /package/{src/components/skeleton/SkeletonCard.js → docs/tutorials/.gitkeep} +0 -0
package/docs/routing.md CHANGED
@@ -1,826 +1,417 @@
1
- # Routing
1
+ ---
2
+ title: Routing
3
+ description: Build single-page applications with NativeDocument's full-featured router - hash, history, and memory modes with layouts, middlewares, and named routes
4
+ ---
2
5
 
3
- NativeDocument's routing system enables building single-page applications with client-side navigation. The router automatically manages URL changes, renders appropriate components, and maintains application state without full page reloads.
6
+ # Routing
4
7
 
5
- ## Understanding Routing
8
+ NativeDocument's routing system enables single-page applications with client-side navigation. The router manages URL changes, renders the right components, and maintains application state without full page reloads.
6
9
 
7
- The routing system works by matching URL patterns against registered routes and rendering corresponding components. When users navigate, the router updates the view while preserving application state and providing smooth transitions.
10
+ ## Setup
8
11
 
9
12
  ```javascript
10
- import { Router } from 'native-document/router';
13
+ import { Router, Link } from 'native-document/router';
11
14
 
12
- // Create router and define routes
13
- const router = Router.create({ mode: 'history' }, (router) => {
15
+ Router.create({ name: 'default', mode: 'history' }, (router) => {
14
16
  router.add('/', HomePage);
15
17
  router.add('/about', AboutPage);
16
18
  router.add('/users/{id}', UserProfile);
17
19
  });
18
-
19
- // Mount to DOM
20
- router.mount('#app');
21
20
  ```
22
21
 
23
22
  ## Router Modes
24
23
 
25
- NativeDocument supports three routing modes for different deployment scenarios:
24
+ ### History mode (recommended)
26
25
 
27
- ### History Mode (Recommended)
28
-
29
- Uses HTML5 History API for clean URLs without hash symbols:
26
+ Uses the HTML5 History API for clean URLs:
30
27
 
31
28
  ```javascript
32
- const router = Router.create({ mode: 'history' }, (router) => {
33
- router.add('/', () => Div('Home Page'));
34
- router.add('/products', () => Div('Product List'));
35
- router.add('/contact', () => Div('Contact Us'));
29
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
30
+ router.add('/', HomePage);
31
+ router.add('/products', ProductList);
36
32
  });
37
-
38
- // URLs: /products, /contact, /users/123
33
+ // URLs: /products, /users/123
39
34
  ```
40
35
 
41
- ### Hash Mode
36
+ ### Hash mode
42
37
 
43
- Uses URL fragments for compatibility with static hosting:
38
+ Uses URL fragments - compatible with static hosting:
44
39
 
45
40
  ```javascript
46
- const router = Router.create({ mode: 'hash' }, (router) => {
47
- router.add('/', () => Div('Home Page'));
48
- router.add('/dashboard', () => Div('Dashboard'));
41
+ Router.create({ name: 'app', mode: 'hash' }, (router) => {
42
+ router.add('/', HomePage);
43
+ router.add('/dashboard', Dashboard);
49
44
  });
50
-
51
45
  // URLs: #/dashboard, #/users/456
52
46
  ```
53
47
 
54
- ### Memory Mode
48
+ ### Memory mode
55
49
 
56
- Keeps routing state in memory without URL changes (useful for testing):
50
+ Keeps routing state in memory without changing the URL - useful for testing or embedded flows:
57
51
 
58
52
  ```javascript
59
- const router = Router.create({ mode: 'memory' }, (router) => {
60
- router.add('/', () => Div('Test Home'));
61
- router.add('/test', () => Div('Test Page'));
53
+ Router.create({ name: 'app', mode: 'memory' }, (router) => {
54
+ router.add('/', HomePage);
55
+ router.add('/step2', Step2);
62
56
  });
63
-
64
- // URLs don't change, navigation happens programmatically
65
57
  ```
66
58
 
67
- ## Basic Route Definition
59
+ ---
68
60
 
69
- Routes map URL patterns to component functions that receive navigation data:
61
+ ## Defining Routes
70
62
 
71
63
  ```javascript
72
- const router = Router.create({ mode: 'history' }, (router) => {
64
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
73
65
  // Static routes
74
- router.add('/', () => Div('Welcome to our site'));
75
- router.add('/about', () => Div('About our company'));
76
-
77
- // Routes with parameters
78
- router.add('/users/{id}', ({ params }) =>
79
- Div(['User Profile for ID: ', params.id])
66
+ router.add('/', HomePage);
67
+ router.add('/about', AboutPage);
68
+
69
+ // Route with parameter
70
+ router.add('/users/{id}', ({ params }) =>
71
+ Div(['User: ', params.id])
80
72
  );
81
-
73
+
82
74
  // Multiple parameters
83
- router.add('/posts/{category}/{slug}', ({ params }) =>
84
- Div([
85
- 'Category: ', params.category,
86
- ', Post: ', params.slug
87
- ])
75
+ router.add('/posts/{category}/{slug}', ({ params }) =>
76
+ BlogPost({ category: params.category, slug: params.slug })
88
77
  );
78
+
79
+ // Catch-all 404 (must be last)
80
+ router.add('{*}', NotFoundPage);
89
81
  });
90
82
  ```
91
83
 
92
- ## Route Parameters
84
+ ---
93
85
 
94
- Extract dynamic values from URLs using parameter syntax:
86
+ ## Route Parameters
95
87
 
96
- ### Basic Parameters
88
+ ### Basic parameters
97
89
 
98
90
  ```javascript
99
- router.add('/users/{userId}', ({ params }) => {
100
- const user = getUserById(params.userId);
101
- return UserProfileComponent(user);
102
- });
103
-
104
- router.add('/blog/{year}/{month}', ({ params }) => {
105
- return BlogArchive({
106
- year: parseInt(params.year),
107
- month: parseInt(params.month)
108
- });
109
- });
110
- ```
111
-
112
- ### Parameter Validation
113
-
114
- Define custom patterns for parameter validation:
91
+ router.add('/users/{id}', ({ params }) => UserProfile(params.id));
115
92
 
116
- ```javascript
117
- // Define global patterns
118
- RouteParamPatterns.number = '[0-9]+';
119
-
120
- const router = Router.create({ mode: 'history' }, (router) => {
121
- // Only numeric IDs - inline pattern
122
- router.add('/users/{id}', ({ params }) =>
123
- UserProfile(params.id),
124
- { with: { id: '[0-9]+' }}
125
- );
126
-
127
- // OR using global pattern
128
- router.add('/users/{id:number}', ({ params }) =>
129
- UserProfile(params.id)
130
- );
131
-
132
- // Custom patterns - inline
133
- router.add('/posts/{slug}', ({ params }) =>
134
- BlogPost(params.slug),
135
- { with: { slug: '[a-z0-9-]+' }}
136
- );
137
-
138
- // OR define and use pattern
139
- RouteParamPatterns.slug = '[a-z0-9-]+';
140
- router.add('/posts/{slug:slug}', ({ params }) =>
141
- BlogPost(params.slug)
142
- );
143
- });
93
+ router.add('/blog/{year}/{month}', ({ params }) =>
94
+ BlogArchive({ year: parseInt(params.year), month: parseInt(params.month) })
95
+ );
144
96
  ```
145
97
 
146
- ## Query Parameters
98
+ ### Parameter validation
147
99
 
148
- Access URL query strings through the query object:
100
+ Use `{ with: { paramName: pattern } }` to restrict a parameter to a regex pattern:
149
101
 
150
102
  ```javascript
151
- router.add('/search', ({ query }) => {
152
- const { term, category, page = 1 } = query;
153
-
154
- return SearchResults({
155
- searchTerm: term,
156
- category: category,
157
- currentPage: parseInt(page)
158
- });
159
- });
103
+ // Inline pattern - only numeric ids
104
+ router.add('/users/{id}', UserProfile, { with: { id: '[0-9]+' } });
160
105
 
161
- // URL: /search?term=javascript&category=tutorials&page=2
162
- // query = { term: 'javascript', category: 'tutorials', page: '2' }
106
+ // Slug pattern
107
+ router.add('/posts/{slug}', BlogPost, { with: { slug: '[a-z0-9-]+' } });
163
108
  ```
164
109
 
165
- ## Navigation
166
-
167
- Navigate programmatically using router methods:
168
-
169
- ### Push Navigation
170
-
171
- Add new entries to browser history. **Specify router name** when using multiple routers:
110
+ You can also register patterns globally via `RouteParamPatterns` and reference them as `{param:patternName}`:
172
111
 
173
112
  ```javascript
174
- const NavigationExample = Div([
175
- Button('Go to About').nd.onClick(() => {
176
- Router.push('/about'); // Uses default router
177
- }),
178
-
179
- Button('Go to About (Main Router)').nd.onClick(() => {
180
- Router.push('/about', 'main'); // Uses named router
181
- }),
182
-
183
- Button('View User 123').nd.onClick(() => {
184
- // Navigate in specific router
185
- Router.push('/users/123', 'app');
186
- }),
187
-
188
- Button('Search Products').nd.onClick(() => {
189
- Router.push('/search?term=laptop&category=electronics', 'main');
190
- })
191
- ]);
113
+ import { RouteParamPatterns } from 'native-document/router';
192
114
  ```
193
115
 
194
- ### Replace Navigation
195
-
196
- Replace current history entry without adding to stack:
116
+ Built-in patterns:
117
+
118
+ | Name | Pattern | Matches |
119
+ |---|---|---|
120
+ | `id` | `[0-9]+` | Numeric IDs |
121
+ | `uuid` | `[0-9a-f]{8}-...` | UUIDs |
122
+ | `slug` | `[a-z0-9]+(?:-[a-z0-9]+)*` | URL slugs |
123
+ | `hash` | `[a-f0-9]{32,64}` | MD5/SHA hashes |
124
+ | `alpha` | `[a-zA-Z]+` | Letters only |
125
+ | `alphanum` | `[a-zA-Z0-9]+` | Letters and digits |
126
+ | `string` | `[^/]+` | Any non-slash string |
127
+ | `any` | `.*` | Anything including slashes |
128
+ | `int` | `[0-9]+` | Integers |
129
+ | `float` | `[0-9]+\.[0-9]+` | Floats |
130
+ | `number` | `[0-9]+(\.[0-9]+)?` | Integers or floats |
131
+ | `positive` | `[1-9][0-9]*` | Positive integers (no zero) |
132
+ | `locale` | `[a-z]{2}(-[A-Z]{2})?` | Locale codes (`fr`, `en-US`) |
133
+ | `lang` | `[a-z]{2}` | Language codes (`fr`, `en`) |
134
+ | `token` | `[A-Za-z0-9_\-]+` | Tokens and API keys |
197
135
 
198
136
  ```javascript
199
- const LoginRedirect = () => {
200
- // Redirect after login without allowing back navigation
201
- Router.replace('/dashboard'); // Default router
202
-
203
- // Or specify router
204
- Router.replace('/dashboard', 'main');
205
-
206
- return Div('Redirecting...');
207
- };
137
+ router.add('/users/{id:id}', UserProfile); // numeric only
138
+ router.add('/posts/{slug:slug}', BlogPost); // url-safe slug
139
+ router.add('/files/{hash:hash}', FileView); // hash string
140
+ router.add('/lang/{code:locale}', LocalePage); // fr, en-US
208
141
  ```
209
142
 
210
- ### History Navigation
211
-
212
- Navigate through browser history:
143
+ You can also extend `RouteParamPatterns` with your own:
213
144
 
214
145
  ```javascript
215
- const HistoryControls = Div([
216
- Button('Go Back').nd.onClick(() => Router.back()), // Default router
217
- Button('Go Forward').nd.onClick(() => Router.forward()), // Default router
218
-
219
- // Navigate specific router's history
220
- Button('Back in Main').nd.onClick(() => Router.back('main')),
221
- Button('Forward in Admin').nd.onClick(() => Router.forward('admin'))
222
- ]);
146
+ RouteParamPatterns.year = '[0-9]{4}';
147
+ RouteParamPatterns.color = '[0-9a-fA-F]{6}';
148
+
149
+ router.add('/archive/{year:year}', ArchivePage);
223
150
  ```
224
151
 
225
- ## Named Routes
152
+ ---
226
153
 
227
- Name routes for easier URL generation and maintenance:
154
+ ## Query Parameters
228
155
 
229
156
  ```javascript
230
- const router = Router.create({ mode: 'history' }, (router) => {
231
- router.add('/', HomePage, { name: 'home' });
232
- router.add('/users/{id}', UserProfile, { name: 'user.profile' });
233
- router.add('/posts/{category}/{slug}', BlogPost, { name: 'blog.post' });
157
+ router.add('/search', ({ query }) => {
158
+ const { term, category, page = '1' } = query;
159
+ return SearchResults({ term, category, page: parseInt(page) });
234
160
  });
235
-
236
- // Generate URLs by name
237
- const userUrl = router.generateUrl('user.profile', { id: 123 });
238
- // Result: '/users/123'
239
-
240
- const blogUrl = router.generateUrl('blog.post', { category: 'javascript', slug: 'getting-started' }, { ref: 'newsletter' });
241
- // Result: '/posts/javascript/getting-started?ref=newsletter'
161
+ // URL: /search?term=javascript&category=tutorials&page=2
242
162
  ```
243
163
 
244
- ### Navigation with Named Routes
245
-
246
- ```javascript
247
- const navigation = Div([
248
- Button('Home').nd.onClick(() =>
249
- Router.push({ name: 'home' }) // Uses router containing this route
250
- ),
251
-
252
- Button('My Profile').nd.onClick(() =>
253
- Router.push({
254
- name: 'user.profile',
255
- params: { id: currentUser.id }
256
- }, 'main') // Specify router if needed
257
- ),
258
-
259
- Button('Latest Post').nd.onClick(() =>
260
- Router.push({
261
- name: 'blog.post',
262
- params: { category: 'news', slug: 'latest-update' },
263
- query: { highlight: 'new-features' }
264
- }, 'blog') // Navigate in blog router
265
- )
266
- ]);
267
- ```
164
+ ---
268
165
 
269
166
  ## Route Groups
270
167
 
271
168
  Organize related routes with shared configuration:
272
169
 
273
170
  ```javascript
274
- const router = Router.create({ mode: 'history' }, (router) => {
275
- // Admin routes with auth middleware
276
- router.group('/admin', { middlewares: [requireAuth, requireAdmin] }, () => {
277
- router.add('/', AdminDashboard, { name: 'admin.dashboard' });
278
- router.add('/users', AdminUsers, { name: 'admin.users' });
279
- router.add('/settings', AdminSettings, { name: 'admin.settings' });
171
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
172
+
173
+ // Group with layout
174
+ router.group('', { layout: DefaultLayout }, () => {
175
+ router.add('/', HomePage, { name: 'home' });
176
+ router.add('/about', AboutPage, { name: 'about' });
280
177
  });
281
-
282
- // User dashboard with nested naming
283
- router.group('/dashboard', { name: 'dashboard' }, () => {
284
- router.add('/', UserDashboard, { name: 'home' }); // dashboard.home
285
- router.add('/profile', UserProfile, { name: 'profile' }); // dashboard.profile
286
- router.add('/settings', UserSettings, { name: 'settings' }); // dashboard.settings
178
+
179
+ // Group with middleware and name prefix
180
+ router.group('/admin', { middlewares: [requireAuth], name: 'admin' }, () => {
181
+ router.add('/', AdminDashboard, { name: 'dashboard' }); // admin.dashboard
182
+ router.add('/users', AdminUsers, { name: 'users' }); // admin.users
287
183
  });
184
+
288
185
  });
289
186
  ```
290
187
 
291
- ## Middleware
188
+ ---
292
189
 
293
- Add logic that runs before route components are rendered:
190
+ ## Named Routes
294
191
 
295
- ### Authentication Middleware
192
+ Naming routes lets you generate URLs and navigate without hardcoding paths:
296
193
 
297
194
  ```javascript
298
- const requireAuth = (context, next) => {
299
- const { route, params, query, path } = context;
300
-
301
- if (!isUserAuthenticated()) {
302
- // Redirect to login with return path
303
- Router.replace({
304
- name: 'auth.login',
305
- query: { redirect: path }
306
- });
307
- return;
308
- }
309
-
310
- // Continue to route component
311
- next();
312
- };
313
-
314
- const requireAdmin = (context, next) => {
315
- if (!isUserAdmin()) {
316
- Router.replace({ name: 'errors.forbidden' });
317
- return;
318
- }
319
- next();
320
- };
321
- ```
195
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
196
+ router.add('/', HomePage, { name: 'home' });
197
+ router.add('/users/{id}', UserProfile, { name: 'user.profile' });
198
+ router.add('/posts/{category}/{slug}', BlogPost, { name: 'blog.post' });
199
+ });
322
200
 
323
- ### Loading States
201
+ // Generate a URL by name
202
+ const router = Router.get('app');
203
+ router.generateUrl('user.profile', { id: 123 });
204
+ // -> '/users/123'
324
205
 
325
- ```javascript
326
- const loadingMiddleware = (context, next) => {
327
- // Show loading indicator
328
- showGlobalLoader();
329
-
330
- // Continue to route component
331
- next();
332
-
333
- // Hide loading indicator
334
- setTimeout(() => hideGlobalLoader(), 100);
335
- };
206
+ router.generateUrl('blog.post',
207
+ { category: 'javascript', slug: 'getting-started' },
208
+ { ref: 'newsletter' }
209
+ );
210
+ // -> '/posts/javascript/getting-started?ref=newsletter'
336
211
  ```
337
212
 
338
- ### Analytics Tracking
339
-
340
- ```javascript
341
- const analyticsMiddleware = (context, next) => {
342
- const { route, params, path } = context;
343
-
344
- // Track page view
345
- analytics.track('page_view', {
346
- path: path,
347
- route_name: route.name(),
348
- timestamp: Date.now()
349
- });
350
-
351
- next();
352
- };
353
- ```
213
+ ---
354
214
 
355
- ## Link Component
215
+ ## Navigation
356
216
 
357
- Create navigational links that integrate with the router:
217
+ ### `Router.push(target, routerName?)`
358
218
 
359
- ### Basic Links
219
+ Add a new entry to the browser history:
360
220
 
361
221
  ```javascript
362
- import { Link } from 'native-document/router';
222
+ Router.push('/about'); // path - uses default router
223
+ Router.push('/about', 'app'); // path - named router
363
224
 
364
- const Navigation = Nav([
365
- Link({ to: '/' }, 'Home'),
366
- Link({ to: '/about' }, 'About'),
367
- Link({ to: '/contact' }, 'Contact'),
368
-
369
- // External links open in new tab
370
- Link.blank({ href: 'https://example.com' }, 'External Site')
371
- ]);
225
+ Router.push({ name: 'home' }); // named route
226
+ Router.push({ name: 'user.profile', params: { id: 42 } }, 'app');
227
+ Router.push({ name: 'search', query: { term: 'laptop' } });
372
228
  ```
373
229
 
374
- ### Links with Router Specification
230
+ ### `Router.replace(target, routerName?)`
375
231
 
376
- ```javascript
377
- const UserMenu = Div([
378
- Link({
379
- to: {
380
- name: 'user.profile',
381
- params: { id: currentUser.id },
382
- router: 'main' // Specify which router to use
383
- }
384
- }, 'My Profile'),
385
-
386
- Link({
387
- to: {
388
- name: 'user.settings',
389
- params: { id: currentUser.id },
390
- query: { tab: 'preferences' },
391
- router: 'main'
392
- }
393
- }, 'Settings'),
394
-
395
- // Cross-router navigation
396
- Link({
397
- to: {
398
- name: 'admin.dashboard',
399
- router: 'admin'
400
- }
401
- }, 'Admin Panel')
402
- ]);
403
- ```
404
-
405
- ### Active Link Styling
232
+ Replace the current history entry without adding to the stack:
406
233
 
407
234
  ```javascript
408
- const NavItem = (path, text, routerName = null) => {
409
- const router = Router.get(routerName);
410
- const isActive = Observable(router.currentState().path === path);
411
- router.subscribe((state) => {
412
- isActive.set(state.path === path);
413
- });
414
-
415
- return Link({
416
- to: path,
417
- class: { 'nav-link': true, 'active': isActive }
418
- }, text);
419
- };
420
-
421
- const MainNav = Nav([
422
- NavItem('/', 'Home', 'main'),
423
- NavItem('/products', 'Products', 'main'),
424
- NavItem('/services', 'Services', 'main')
425
- ]);
426
-
427
- // Multi-router navigation
428
- const AppNav = Nav([
429
- NavItem('/', 'Main App', 'main'),
430
- NavItem('/admin', 'Admin', 'admin'),
431
- NavItem('/blog', 'Blog', 'blog')
432
- ]);
433
-
235
+ Router.replace('/dashboard');
236
+ Router.replace({ name: 'login', query: { redirect: '/dashboard' } });
434
237
  ```
435
238
 
436
- ## Multiple Routers
239
+ ### `Router.redirectTo(pathOrRouteName, params?, routerName?)`
437
240
 
438
- Create separate router instances for different application areas. **Always name your routers** to avoid conflicts and enable proper navigation.
241
+ Redirect to a path or named route. If a route name is found, it navigates by name; otherwise it treats the argument as a path:
439
242
 
440
243
  ```javascript
441
- // Main application router (named)
442
- const mainRouter = Router.create(
443
- { mode: 'history', name: 'main' },
444
- (router) => {
445
- router.add('/', HomePage);
446
- router.add('/products', ProductList);
447
- router.add('/admin', AdminApp);
448
- }
449
- );
450
-
451
- // Admin-specific router (named)
452
- const adminRouter = Router.create(
453
- { mode: 'history', name: 'admin', entry: '/admin' },
454
- (router) => {
455
- router.add('/', AdminDashboard);
456
- router.add('/users', AdminUsers);
457
- router.add('/reports', AdminReports);
458
- }
459
- );
460
-
461
- // Access routers by name
462
- const mainRouter = Router.get('main');
463
- const adminRouter = Router.get('admin');
464
-
465
- // Or access via routers object
466
- const mainRouter = Router.routers.main;
467
- const adminRouter = Router.routers.admin;
468
-
469
- // Cross-router navigation
470
- Button('Go to Admin').nd.onClick(() => {
471
- Router.push('/admin/users', 'admin'); // Specify router name
472
- });
473
-
474
- Button('Back to Main').nd.onClick(() => {
475
- Router.push('/', 'main'); // Navigate in main router
476
- });
244
+ Router.redirectTo('home');
245
+ Router.redirectTo('user.profile', { id: 42 });
246
+ Router.redirectTo('/legacy-path');
477
247
  ```
478
248
 
479
- ### Router Naming Best Practices
249
+ ### `Router.back(routerName?)` / `Router.forward(routerName?)`
480
250
 
481
- **Always name your routers** to prevent conflicts and enable reliable navigation:
251
+ Navigate through browser history:
482
252
 
483
253
  ```javascript
484
- // BAD: Unnamed router (becomes default)
485
- const router1 = Router.create({ mode: 'history' }, (router) => {
486
- // This becomes the default router
487
- });
488
-
489
- // ❌ BAD: Multiple unnamed routers cause conflicts
490
- const router2 = Router.create({ mode: 'history' }, (router) => {
491
- // This overwrites the default router!
492
- });
493
-
494
- // ✅ GOOD: Named routers
495
- const appRouter = Router.create({ mode: 'history', name: 'app' }, (router) => {
496
- router.add('/', HomePage);
497
- });
498
-
499
- const modalRouter = Router.create({ mode: 'memory', name: 'modal' }, (router) => {
500
- router.add('/confirm', ConfirmDialog);
501
- });
254
+ Button('Back').nd.onClick(() => Router.back());
255
+ Button('Forward').nd.onClick(() => Router.forward('app'));
502
256
  ```
503
257
 
504
- ### Default Router Access
258
+ ---
505
259
 
506
- When no name is specified, Router.get() returns the router named "default":
507
-
508
- ```javascript
509
- // Create default router
510
- Router.create({ mode: 'history' }, (router) => {
511
- router.add('/', HomePage);
512
- });
260
+ ## Link Component
513
261
 
514
- // Access default router (named "default" internally)
515
- const defaultRouter = Router.get(); // Returns router named "default"
516
- const defaultRouter = Router.get('default'); // Same as above
517
- const defaultRouter = Router.routers.default; // Direct access
262
+ `to` takes a route **name** (string) or an object with `name`, `params`, `query`, and `router`. Use `href` for direct path links:
518
263
 
519
- // Navigate using default router
520
- Router.push('/'); // Uses "default" router
521
- Router.push('/', 'default'); // Explicitly use default router
522
- ```
264
+ ```javascript
265
+ import { Link } from 'native-document/router';
523
266
 
524
- ### Router Naming Best Practices
267
+ // Named route
268
+ Link({ to: 'home' }, 'Home')
525
269
 
526
- **Always name your routers explicitly** to avoid relying on the default:
270
+ // Named route with params
271
+ Link({ to: { name: 'user.profile', params: { id: 42 } } }, 'Profile')
527
272
 
528
- ```javascript
529
- // BAD: Unnamed router (becomes "default")
530
- const router1 = Router.create({ mode: 'history' }, (router) => {
531
- // This is stored as Router.routers.default
532
- });
273
+ // Named route with query
274
+ Link({ to: { name: 'search', query: { term: 'js' } } }, 'Search')
533
275
 
534
- // BAD: Multiple unnamed routers cause conflicts
535
- const router2 = Router.create({ mode: 'history' }, (router) => {
536
- // This overwrites Router.routers.default!
537
- });
276
+ // Named route on a specific router
277
+ Link({ to: { name: 'admin.dashboard', router: 'admin' } }, 'Admin')
538
278
 
539
- // GOOD: Named routers
540
- const appRouter = Router.create({ mode: 'history', name: 'app' }, (router) => {
541
- router.add('/', HomePage);
542
- // Stored as Router.routers.app
543
- });
279
+ // Direct path link
280
+ Link({ href: '/about' }, 'About')
544
281
 
545
- const modalRouter = Router.create({ mode: 'memory', name: 'modal' }, (router) => {
546
- router.add('/confirm', ConfirmDialog);
547
- // Stored as Router.routers.modal
548
- });
282
+ // External link - opens in new tab
283
+ Link.blank({ href: 'https://example.com' }, 'External')
549
284
  ```
550
285
 
551
- ## Route Guards and Data Loading
286
+ ---
552
287
 
553
- Implement route guards and data preloading:
288
+ ## Middleware
554
289
 
555
- ### Route-Specific Data Loading
290
+ Middleware runs before the route component is rendered. It receives a `context` object and a `next` function:
556
291
 
557
292
  ```javascript
558
- const UserProfile = ({ params }) => {
559
- const userId = params.id;
560
- const user = Observable(null);
561
- const loading = Observable(true);
562
- const error = Observable(null);
563
-
564
- // Load user data
565
- fetchUser(userId)
566
- .then(userData => {
567
- user.set(userData);
568
- loading.set(false);
569
- })
570
- .catch(err => {
571
- error.set(err.message);
572
- loading.set(false);
573
- });
574
-
575
- return Match(loading, {
576
- true: LoadingSpinner,
577
- false: () => Switch(error, ErrorMessage(error), UserProfileView(user) )
578
- });
293
+ const requireAuth = (context, next) => {
294
+ const { path } = context;
295
+ if (!isAuthenticated()) {
296
+ Router.replace({ name: 'login', query: { redirect: path } });
297
+ return; // stop - don't call next()
298
+ }
299
+ next();
579
300
  };
580
301
 
581
- router.add('/users/{id}', UserProfile, { name: 'user.profile' });
302
+ const analytics = (context, next) => {
303
+ trackPageView(context.path);
304
+ next();
305
+ };
582
306
  ```
583
307
 
584
- ### Global Route Guards
308
+ Apply middleware to a group or individual route:
585
309
 
586
310
  ```javascript
587
- const authGuard = (context, next) => {
588
- const { route } = context;
589
- const protectedRoutes = ['dashboard', 'profile', 'settings'];
590
-
591
- if (protectedRoutes.includes(route.name()) && !isAuthenticated()) {
592
- Router.replace({
593
- name: 'login',
594
- query: { redirect: context.path }
595
- });
596
- return;
597
- }
598
-
599
- next();
600
- };
311
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
312
+
313
+ router.add('/', HomePage, { name: 'home' });
314
+
315
+ router.group('/dashboard', { middlewares: [requireAuth, analytics] }, () => {
316
+ router.add('/', Dashboard, { name: 'dashboard' });
317
+ router.add('/settings', Settings, { name: 'settings' });
318
+ });
319
+
320
+ // Route-level middleware
321
+ router.add('/admin', AdminPage, {
322
+ name: 'admin',
323
+ middlewares: [requireAuth, requireAdmin]
324
+ });
601
325
 
602
- // Add to all routes that need protection
603
- router.group('/', { middlewares: [authGuard] }, () => {
604
- router.add('/dashboard', Dashboard, { name: 'dashboard' });
605
- router.add('/profile', Profile, { name: 'profile' });
606
- router.add('/settings', Settings, { name: 'settings' });
607
326
  });
608
327
  ```
609
328
 
610
- ## Error Handling
329
+ ---
611
330
 
612
- Handle routing errors gracefully:
331
+ ## Multiple Routers
613
332
 
614
- ### 404 - Route Not Found
333
+ Always name your routers to avoid conflicts:
615
334
 
616
335
  ```javascript
617
- const router = Router.create({ mode: 'history' }, (router) => {
336
+ Router.create({ name: 'main', mode: 'history' }, (router) => {
618
337
  router.add('/', HomePage);
619
- router.add('/about', AboutPage);
620
-
621
- // Catch-all route for 404s (must be last)
622
- router.add('.*', ({ params }) =>
623
- NotFoundPage({ attemptedPath: params.path })
624
- );
338
+ router.add('/products', ProductList);
625
339
  });
626
340
 
627
- const NotFoundPage = ({ attemptedPath }) => Div([
628
- H1('Page Not Found'),
629
- P(['The page "', attemptedPath, '" could not be found.']),
630
- Link({ to: '/' }, 'Return Home')
631
- ]);
632
- ```
341
+ Router.create({ name: 'admin', mode: 'history', entry: '/admin' }, (router) => {
342
+ router.add('/', AdminDashboard);
343
+ router.add('/users', AdminUsers);
344
+ });
633
345
 
634
- ### Error Boundaries
346
+ // Access by name
347
+ const main = Router.get('main');
348
+ const admin = Router.get('admin');
635
349
 
636
- ```javascript
637
- const errorHandler = (context, next) => {
638
- try {
639
- next();
640
- } catch (error) {
641
- console.error('Route error:', error);
642
-
643
- // Navigate to error page
644
- Router.replace({
645
- name: 'error',
646
- query: { message: error.message }
647
- });
648
- }
649
- };
350
+ // Or via the routers object (plain object, not reactive)
351
+ Router.routers.main;
352
+ Router.routers.admin;
650
353
 
651
- router.add('/error', ({ query }) =>
652
- ErrorPage({ message: query.message })
653
- );
354
+ // Navigate in a specific router
355
+ Button('Go to admin').nd.onClick(() => Router.push('/admin/users', 'admin'));
654
356
  ```
655
357
 
656
- ## Real-World Examples
358
+ > Two unnamed routers would both be stored as `Router.routers.default` - the second would overwrite the first.
657
359
 
658
- ### E-commerce Application
360
+ ---
361
+
362
+ ## Current State
659
363
 
660
364
  ```javascript
661
- const ecommerceRouter = Router.create({ mode: 'history' }, (router) => {
662
- // Public routes
663
- router.add('/', HomePage, { name: 'home' });
664
- router.add('/products', ProductCatalog, { name: 'products' });
665
- router.add('/products/{category}', CategoryPage, { name: 'category' });
666
- router.add('/products/{category}/{id}', ProductDetail, { name: 'product' });
667
-
668
- // User account routes
669
- router.group('/account', { middlewares: [requireAuth] }, () => {
670
- router.add('/', AccountDashboard, { name: 'account.dashboard' });
671
- router.add('/orders', OrderHistory, { name: 'account.orders' });
672
- router.add('/profile', UserProfile, { name: 'account.profile' });
673
- router.add('/addresses', AddressBook, { name: 'account.addresses' });
674
- });
675
-
676
- // Shopping cart
677
- router.add('/cart', ShoppingCart, { name: 'cart' });
678
- router.add('/checkout', CheckoutProcess, { name: 'checkout', middlewares: [requireAuth] });
679
-
680
- // Search and filters
681
- router.add('/search', SearchResults, { name: 'search' });
682
- // URL: /search?q=laptop&category=electronics&price_min=100&price_max=1000
683
- });
365
+ const router = Router.get('app');
684
366
 
685
- // Product component with category navigation
686
- const ProductDetail = ({ params, query }) => {
687
- const { category, id } = params;
688
- const product = Observable(null);
689
-
690
- loadProduct(id).then(data => product.set(data));
691
-
692
- return ShowIf(product, () => {
693
- const p = product.val();
694
- return Div([
695
- // Breadcrumb navigation
696
- Nav([
697
- Link({ to: { name: 'home' } }, 'Home'),
698
- ' > ',
699
- Link({ to: { name: 'category', params: { category } } }, category),
700
- ' > ',
701
- Span(p.name)
702
- ]),
703
-
704
- // Product details
705
- H1(p.name),
706
- Div({ class: 'price' }, p.price),
707
- P(p.description),
708
-
709
- // Add to cart with redirect to login if needed
710
- Button('Add to Cart').nd.onClick(() => {
711
- if (!isAuthenticated()) {
712
- Router.push({
713
- name: 'login',
714
- query: {
715
- redirect: Router.get().currentState().path
716
- }
717
- });
718
- } else {
719
- addToCart(p.id);
720
- Router.push({ name: 'cart' });
721
- }
722
- })
723
- ]);
724
- });
725
- };
367
+ // Read current state
368
+ const { route, params, query, path, hash } = router.currentState();
369
+
370
+ // Subscribe to navigation changes
371
+ router.subscribe(state => {
372
+ console.log('Navigated to:', state.path);
373
+ });
726
374
  ```
727
375
 
728
- ### Multi-Step Form with Navigation
376
+ ---
377
+
378
+ ## Error Handling
729
379
 
730
380
  ```javascript
731
- const formRouter = Router.create({ mode: 'hash' }, (router) => {
732
- const formData = Observable.object({
733
- step1: { name: '', email: '' },
734
- step2: { address: '', city: '' },
735
- step3: { payment: '', terms: false }
736
- });
737
-
738
- router.add('/form/personal', () =>
739
- PersonalInfoStep(formData),
740
- { name: 'form.personal' }
741
- );
742
-
743
- router.add('/form/address', () =>
744
- AddressStep(formData),
745
- { name: 'form.address', middlewares: [validateStep1] }
746
- );
747
-
748
- router.add('/form/payment', () =>
749
- PaymentStep(formData),
750
- { name: 'form.payment', middlewares: [validateStep1, validateStep2] }
751
- );
752
-
753
- router.add('/form/review', () =>
754
- ReviewStep(formData),
755
- { name: 'form.review', middlewares: [validateAllSteps] }
756
- );
757
- });
381
+ Router.create({ name: 'app', mode: 'history' }, (router) => {
382
+ router.add('/', HomePage);
758
383
 
759
- const FormNavigation = (currentStep, formData) => {
760
- const steps = ['personal', 'address', 'payment', 'review'];
761
- const currentIndex = steps.indexOf(currentStep);
762
-
763
- return Div({ class: 'form-navigation' }, [
764
- // Step indicator
765
- Div({ class: 'step-indicator' },
766
- steps.map((step, index) =>
767
- Span({
768
- class: index <= currentIndex ? 'step active' : 'step'
769
- }, step)
770
- )
771
- ),
772
-
773
- // Navigation buttons
774
- Div({ class: 'nav-buttons' }, [
775
- ShowIf(Observable(currentIndex > 0),
776
- Button('Previous').nd.onClick(() => {
777
- const prevStep = steps[currentIndex - 1];
778
- Router.push({ name: `form.${prevStep}` });
779
- })
780
- ),
781
-
782
- ShowIf(Observable(currentIndex < steps.length - 1),
783
- Button('Next').nd.onClick(() => {
784
- const nextStep = steps[currentIndex + 1];
785
- Router.push({ name: `form.${nextStep}` });
786
- })
787
- ),
788
-
789
- ShowIf(Observable(currentIndex === steps.length - 1),
790
- Button('Submit').nd.onClick(() => {
791
- submitForm(formData.$value);
792
- })
793
- )
384
+ // Catch-all - must be last
385
+ router.add('{*}', ({ params }) =>
386
+ Div([
387
+ H1('Page not found'),
388
+ Link({ href: '/' }, 'Go home')
794
389
  ])
795
- ]);
796
- };
390
+ );
391
+ });
797
392
  ```
798
393
 
394
+ ---
395
+
799
396
  ## Best Practices
800
397
 
801
- 1. **Use named routes** for maintainable URL generation
802
- 2. **Group related routes** with shared middleware
803
- 3. **Validate parameters** to prevent runtime errors
804
- 4. **Handle loading states** in data-dependent routes
805
- 5. **Implement proper error boundaries** for robust navigation
806
- 6. **Use middleware** for cross-cutting concerns like authentication
807
- 7. **Keep route components focused** - extract complex logic to separate modules
808
- 8. **Test navigation flows** with different router modes
398
+ 1. Always name your routers - unnamed routers default to `'default'` and will conflict
399
+ 2. Use named routes for all `Link` and `push` calls - avoids hardcoded paths
400
+ 3. Use `router.group()` for shared middleware and layout
401
+ 4. Use `Router.replace()` after login or form submission to avoid back-button issues
402
+ 5. Define the catch-all `{*}` route last
809
403
 
810
- ## Next Steps
404
+ ---
811
405
 
812
- Explore these related topics to build complete applications:
406
+ ## Next Steps
813
407
 
814
- - **[State Management](state-management.md)** - Global state patterns
815
- - **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
816
- - **[NDElement](native-document-element.md)** - Native Document Element
817
- - **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
818
- - **[Args Validation](validation.md)** - Function Argument Validation
819
- - **[Memory Management](memory-management.md)** - Memory management
820
- - **[Anchor](anchor.md)** - Anchor
408
+ - **[State Management](./state-management.md)** - Global state patterns
409
+ - **[Lifecycle Events](./lifecycle-events.md)** - Lifecycle events
410
+ - **[NDElement](./native-document-element.md)** - Native Document Element
411
+ - **[Anchor](./anchor.md)** - Anchor
821
412
 
822
413
  ## Utilities
823
414
 
824
- - **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
825
- - **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
826
- - **[Filters](docs/utils/filters.md)** - Data filtering helpers
415
+ - **[Cache](./cache.md)** - Lazy initialization and singleton patterns
416
+ - **[NativeFetch](./native-fetch.md)** - HTTP client with interceptors
417
+ - **[Filters](./filters.md)** - Data filtering helpers