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
@@ -1,35 +1,41 @@
1
+ ---
2
+ title: Advanced Components
3
+ description: Optimize rendering performance with useCache and useSingleton - template cloning and data binding for high-performance lists and layouts
4
+ ---
5
+
1
6
  # Advanced Components
2
7
 
3
- NativeDocument provides advanced component patterns for optimizing rendering performance through template cloning and data binding. The `useCache()` utility creates reusable component templates with a sophisticated binding system that efficiently updates only the dynamic parts.
8
+ NativeDocument provides advanced component patterns for optimizing rendering performance through template cloning and data binding. The `useCache()` utility creates reusable component templates with a binding system that efficiently updates only the dynamic parts.
4
9
 
5
10
  ## Overview
6
11
 
7
- Advanced component utilities include:
8
12
  - **`useCache(fn)`** - Create cached components with data binding
9
- - **`useSingleton(fn)`** - Create singleton views with updateable sections
10
- - **Binder API** - Powerful data binding system for templates
13
+ - **`useSingleton(fn)`** - Create singleton views with updatable sections
14
+ - **Binder API** - Data binding system for templates
11
15
 
12
16
  ## Import
17
+
13
18
  ```javascript
14
19
  import { useCache, useSingleton } from 'native-document';
20
+ import { ForEachArray } from 'native-document/elements';
15
21
  ```
16
22
 
17
- ## useCache() - Cached Components with Binding
23
+ ---
24
+
25
+ ## `useCache()` - Cached Components with Binding
18
26
 
19
27
  `useCache()` creates a component template that is built once and then cloned efficiently. The component function receives a **binder** object (`$binder`) that creates bindings for dynamic data.
20
28
 
21
29
  ### Basic Concept
30
+
22
31
  ```javascript
23
32
  import { useCache } from 'native-document';
24
- import { Div, Span } from 'native-document/src/elements';
33
+ import { Div, Span } from 'native-document/elements';
25
34
 
26
- // Component function receives $binder
27
35
  const UserCard = useCache(($binder) => {
28
- // Create bindings for dynamic data
29
36
  const name = $binder.value('name');
30
- const age = $binder.value('age');
37
+ const age = $binder.value('age');
31
38
 
32
- // Build template with bindings
33
39
  return Div({ class: 'user-card' }, [
34
40
  Span('Name: '),
35
41
  Span(name),
@@ -38,777 +44,376 @@ const UserCard = useCache(($binder) => {
38
44
  ]);
39
45
  });
40
46
 
41
- // Usage - pass data object
42
47
  const card1 = UserCard({ name: 'Alice', age: 25 });
43
- const card2 = UserCard({ name: 'Bob', age: 30 });
44
- // card2 is cloned from template, bindings update automatically
48
+ const card2 = UserCard({ name: 'Bob', age: 30 });
49
+ // card2 is cloned from the template - bindings update automatically
45
50
  ```
46
51
 
47
- ### How Arguments Are Passed
52
+ ---
48
53
 
49
- Binder functions receive arguments exactly as the user passes them:
50
- ```javascript
51
- import { useCache } from 'native-document';
52
- import { Div } from 'native-document/src/elements';
54
+ ## Binder Methods
53
55
 
54
- const Component = useCache(($binder) => {
55
- // Function receives arguments exactly as passed
56
- const value = $binder.value((data) => {
57
- // data = { name: 'Alice', age: 25 }
58
- return data.name;
59
- });
60
-
61
- return Div(value);
62
- });
56
+ ### `$binder.name` - Property shorthand (Proxy)
63
57
 
64
- // Usage - object passed directly
65
- const instance = Component({ name: 'Alice', age: 25 });
58
+ `$binder` is a Proxy. Any property access that is not a known method is automatically treated as `$binder.value('propertyName')`:
59
+
60
+ ```javascript
61
+ const name = $binder.name; // same as $binder.value('name')
62
+ const price = $binder.price; // same as $binder.value('price')
63
+ const stock = $binder.stock; // same as $binder.value('stock')
66
64
  ```
67
65
 
68
- ## Binder Methods
66
+ > The shorthand only works for property names. For transform functions you still need `$binder.value(fn)`.
69
67
 
70
- ### $binder.value() - Property Binding
68
+ ### `$binder.value(key | fn)` / `$binder.property(key | fn)` / `$binder.text(key | fn)`
71
69
 
72
- Bind to a property name or use a transform function:
73
- ```javascript
74
- import { useCache } from 'native-document';
75
- import { Div, Span } from 'native-document/src/elements';
70
+ Bind to a property by name, or use a transform function. `property()` and `text()` are aliases for `value()`:
76
71
 
72
+ ```javascript
77
73
  const ProductCard = useCache(($binder) => {
78
- // Bind to property by key name
79
- const name = $binder.value('name');
80
- const price = $binder.value('price');
81
-
82
- // Bind with transform function
83
- // Function receives the data object directly
84
- const formattedPrice = $binder.value((product) => {
85
- return `$${product.price.toFixed(2)}`;
86
- });
87
-
88
- const status = $binder.value((product) => {
89
- return product.stock > 0 ? 'In Stock' : 'Out of Stock';
90
- });
91
-
74
+ // Shorthand via Proxy
75
+ const name = $binder.name;
76
+ const stock = $binder.stock;
77
+
78
+ // Transform function - receives the full data object
79
+ const formattedPrice = $binder.value(product => `$${product.price.toFixed(2)}`);
80
+ const status = $binder.value(product => product.stock > 0 ? 'In Stock' : 'Out of Stock');
81
+
92
82
  return Div({ class: 'product-card' }, [
93
- Span({ class: 'name' }, name),
94
- Span({ class: 'price' }, formattedPrice),
83
+ Span({ class: 'name' }, name),
84
+ Span({ class: 'price' }, formattedPrice),
95
85
  Span({ class: 'status' }, status)
96
86
  ]);
97
87
  });
98
88
 
99
- // Usage
100
- const card = ProductCard({ name: 'Phone', price: 599, stock: 10 });
101
- // Displays: Phone, $599.00, In Stock
89
+ ProductCard({ name: 'Phone', price: 599, stock: 10 });
90
+ // -> Phone, $599.00, In Stock
102
91
  ```
103
92
 
104
- ### $binder.property() - Alias for value()
105
- ```javascript
106
- import { useCache } from 'native-document';
107
-
108
- const Component = useCache(($binder) => {
109
- // property() is an alias for value()
110
- const name = $binder.property('name');
111
- const title = $binder.value('title');
112
-
113
- // Both work the same way
114
- return Div([Span(name), Span(title)]);
115
- });
116
- ```
93
+ ### `$binder.class(fn)`
117
94
 
118
- ### $binder.class() - Class Binding
95
+ Bind CSS classes dynamically. The function returns a boolean:
119
96
 
120
- Bind CSS classes dynamically based on data:
121
97
  ```javascript
122
- import { useCache } from 'native-document';
123
- import { Div, Span } from 'native-document/src/elements';
124
-
125
98
  const TaskItem = useCache(($binder) => {
126
- const text = $binder.value('text');
127
-
128
- // Function receives the data object directly
129
- const completedClass = $binder.class((task) => {
130
- return task.completed;
131
- });
132
-
133
- const priorityClass = $binder.class((task) => {
134
- return task.priority === 'high';
135
- });
136
-
137
- return Div({
138
- class: {
139
- 'task': true,
140
- 'completed': completedClass,
141
- 'high-priority': priorityClass
142
- }
143
- }, [
144
- Span(text)
145
- ]);
99
+ const text = $binder.value('text');
100
+ const completedClass = $binder.class(task => task.completed);
101
+ const priorityClass = $binder.class(task => task.priority === 'high');
102
+
103
+ return Div({
104
+ class: {
105
+ 'task': true,
106
+ 'completed': completedClass,
107
+ 'high-priority': priorityClass
108
+ }
109
+ }, [Span(text)]);
146
110
  });
147
111
 
148
- // Usage
149
- const task = TaskItem({ text: 'Fix bug', completed: true, priority: 'high' });
150
- // <div class="task completed high-priority">...</div>
112
+ TaskItem({ text: 'Fix bug', completed: true, priority: 'high' });
113
+ // -> <div class="task completed high-priority">...</div>
151
114
  ```
152
115
 
153
- ### $binder.class() with Observable.when()
116
+ Combine with `Observable.when()` for reactive class binding:
117
+
154
118
  ```javascript
155
- import { useCache } from 'native-document';
156
- import { Tr, Td } from 'native-document/src/elements';
157
119
  import { Observable } from 'native-document';
120
+ import { Tr, Td } from 'native-document/elements';
158
121
 
159
122
  const selectedId = Observable(null);
160
123
 
161
124
  const TableRow = useCache(($binder) => {
162
- const id = $binder.value('id');
125
+ const id = $binder.value('id');
163
126
  const name = $binder.value('name');
164
-
165
- // Bind class to observable condition
166
- const isSelected = $binder.class((item) => {
167
- return selectedId.when(item.id);
168
- });
169
-
170
- return Tr({
171
- class: { 'selected': isSelected }
172
- }, [
127
+
128
+ // Updates automatically when selectedId changes
129
+ const isSelected = $binder.class(item => selectedId.when(item.id));
130
+
131
+ return Tr({ class: { 'selected': isSelected } }, [
173
132
  Td(id),
174
133
  Td(name)
175
134
  ]);
176
135
  });
177
136
 
178
- // Usage
179
- const row1 = TableRow({ id: 1, name: 'Item 1' });
180
- const row2 = TableRow({ id: 2, name: 'Item 2' });
181
-
182
- // Change selection - classes update automatically
183
- selectedId.set(1); // row1 gets 'selected' class
184
- selectedId.set(2); // row2 gets 'selected' class, row1 loses it
137
+ selectedId.set(1); // row with id 1 gets 'selected' class
138
+ selectedId.set(2); // row 2 selected, row 1 loses the class
185
139
  ```
186
140
 
187
- ### $binder.style() - Style Binding
141
+ ### `$binder.style(fn)`
188
142
 
189
- Bind inline styles dynamically:
190
- ```javascript
191
- import { useCache } from 'native-document';
192
- import { Div } from 'native-document/src/elements';
143
+ Bind inline style values:
193
144
 
145
+ ```javascript
194
146
  const ProgressBar = useCache(($binder) => {
195
- const percentage = $binder.value('percentage');
196
-
197
- // Function receives the data object directly
198
- const widthStyle = $binder.style((progress) => {
199
- return progress.percentage + '%';
200
- });
201
-
202
- const colorStyle = $binder.style((progress) => {
203
- return progress.percentage >= 100 ? 'green' : 'blue';
204
- });
205
-
147
+ const widthStyle = $binder.style(p => p.percentage + '%');
148
+ const colorStyle = $binder.style(p => p.percentage >= 100 ? 'green' : 'blue');
149
+
206
150
  return Div({ class: 'progress-bar' }, [
207
- Div({
151
+ Div({
208
152
  class: 'progress-fill',
209
153
  style: {
210
- width: widthStyle,
154
+ width: widthStyle,
211
155
  backgroundColor: colorStyle
212
156
  }
213
157
  })
214
158
  ]);
215
159
  });
216
160
 
217
- // Usage
218
- const bar = ProgressBar({ percentage: 75 });
219
- // <div style="width: 75%; background-color: blue"></div>
161
+ ProgressBar({ percentage: 75 });
162
+ // -> <div style="width: 75%; background-color: blue"></div>
220
163
  ```
221
164
 
222
- ### $binder.attr() - Attribute Binding
165
+ ### `$binder.attr(fn)`
166
+
167
+ Bind element attributes. Takes only a function:
223
168
 
224
- Bind element attributes dynamically. Takes only a function that returns the attribute value:
225
169
  ```javascript
226
- import { useCache } from 'native-document';
227
- import { Div, Img, A } from 'native-document/src/elements';
170
+ import { Img, Link } from 'native-document/elements';
228
171
 
229
172
  const ProductCard = useCache(($binder) => {
230
- const name = $binder.value('name');
231
-
232
- // attr() takes ONLY a function
233
- // Function receives the data object directly
234
- const imageSrc = $binder.attr((product) => {
235
- return product.imageUrl;
236
- });
237
-
238
- const detailsHref = $binder.attr((product) => {
239
- return `/products/${product.id}`;
240
- });
241
-
242
- // Use as object property with attribute name
173
+ const name = $binder.value('name');
174
+ const imageSrc = $binder.attr(product => product.imageUrl);
175
+ const detailHref = $binder.attr(product => `/products/${product.id}`);
176
+
243
177
  return Div({ class: 'product' }, [
244
178
  Img({ src: imageSrc }),
245
- A({ href: detailsHref }, name)
179
+ Link({ href: detailHref }, name)
246
180
  ]);
247
181
  });
248
182
 
249
- // Usage
250
- const card = ProductCard({
251
- id: 123,
252
- name: 'Phone',
253
- imageUrl: '/images/phone.jpg'
254
- });
255
- // <img src="/images/phone.jpg">
256
- // <a href="/products/123">Phone</a>
183
+ ProductCard({ id: 123, name: 'Phone', imageUrl: '/images/phone.jpg' });
184
+ // -> <img src="/images/phone.jpg">
185
+ // -> <a href="/products/123">Phone</a>
257
186
  ```
258
187
 
259
- ### $binder.attach() - Event Handler Binding
188
+ ### `$binder.attach(fn)` + `.nd.attach()`
189
+
190
+ Bind event handlers. The handler receives `(...userArguments, event)` - user data comes first, the DOM event is last. Use `.nd.attach()` on the element to connect it:
260
191
 
261
- Attach event handlers that receive the event followed by user arguments:
262
192
  ```javascript
263
- import { useCache } from 'native-document';
264
- import { Div, Button, Span } from 'native-document/src/elements';
193
+ import { Div, Button, Span } from 'native-document/elements';
265
194
 
266
195
  const TodoItem = useCache(($binder) => {
267
196
  const text = $binder.value('text');
268
-
269
- // Handler receives (event, ...userArguments)
270
- const handleToggle = $binder.attach((event, todo) => {
271
- console.log('Toggle clicked:', event);
272
- console.log('Todo data:', todo);
197
+
198
+ const handleToggle = $binder.attach((todo, event) => {
273
199
  updateTodo(todo.id, { completed: !todo.completed });
274
200
  });
275
-
276
- const handleDelete = $binder.attach((event, todo) => {
277
- console.log('Delete clicked:', event);
201
+
202
+ const handleDelete = $binder.attach((todo, event) => {
278
203
  deleteTodo(todo.id);
279
204
  });
280
-
205
+
281
206
  return Div({ class: 'todo' }, [
282
- Span(text).nd.attach('onClick', handleToggle),
283
- Button('Delete').nd.attach('onClick', handleDelete)
207
+ Span(text)
208
+ .nd.attach('onClick', handleToggle),
209
+ Button('Delete')
210
+ .nd.attach('onClick', handleDelete)
284
211
  ]);
285
212
  });
286
213
 
287
- // Usage
288
- const todo = TodoItem({ id: 1, text: 'Buy milk', completed: false });
289
- // Clicking triggers handlers with the todo object
214
+ TodoItem({ id: 1, text: 'Buy milk', completed: false });
290
215
  ```
291
216
 
292
- ### Using .nd.attach()
217
+ ### Multiple Arguments
218
+
219
+ When calling a cached component with multiple arguments, binder functions receive all of them:
293
220
 
294
- The `.nd.attach(eventName, handler)` method connects binder handlers to elements:
295
221
  ```javascript
296
- import { useCache } from 'native-document';
297
- import { Button } from 'native-document/src/elements';
298
-
299
- const ActionButton = useCache(($binder) => {
300
- const label = $binder.value('label');
301
-
302
- // Handler receives event + user data
303
- const clickHandler = $binder.attach((event, data) => {
304
- console.log('Action:', data.action);
305
- console.log('Event:', event);
306
- performAction(data.action);
222
+ const ArticleCard = useCache(($binder) => {
223
+ const title = $binder.value((article, options) => {
224
+ return options.uppercase ? article.title.toUpperCase() : article.title;
225
+ });
226
+
227
+ const excerpt = $binder.value((article, options) => {
228
+ const length = options.excerptLength || 100;
229
+ return article.content.substring(0, length) + '...';
307
230
  });
308
-
309
- // Use .nd.attach(eventName, handler)
310
- return Button(label).nd.attach('onClick', clickHandler);
231
+
232
+ return Div({ class: 'article' }, [H3(title), P(excerpt)]);
311
233
  });
312
234
 
313
- // Usage
314
- const btn = ActionButton({ label: 'Save', action: 'save' });
315
- // Clicking logs: "Action: save"
235
+ ArticleCard(
236
+ { title: 'My Article', content: 'Long content...' },
237
+ { uppercase: true, excerptLength: 150 }
238
+ );
316
239
  ```
317
240
 
241
+ ---
242
+
243
+ ## Binder API Reference
244
+
245
+ | Method | Parameters | Description |
246
+ |---|---|---|
247
+ | `$binder.name` | property name | Proxy shorthand for `$binder.value('name')` |
248
+ | `$binder.value(key)` | `key: string` | Bind to property by name |
249
+ | `$binder.value(fn)` | `fn: (...args) => any` | Bind with transform function |
250
+ | `$binder.property(key \| fn)` | same as `value` | Alias for `value()` |
251
+ | `$binder.text(key \| fn)` | same as `value` | Alias for `value()` |
252
+ | `$binder.class(fn)` | `fn: (...args) => boolean` | Bind CSS class toggle |
253
+ | `$binder.style(fn)` | `fn: (...args) => string` | Bind inline style value |
254
+ | `$binder.attr(fn)` | `fn: (...args) => string` | Bind attribute value |
255
+ | `$binder.attach(fn)` | `fn: (event, ...args) => void` | Bind event handler |
256
+ | `$binder.callback(fn)` | same as `attach` | Alias for `attach()` |
257
+
258
+ > All binder methods receive arguments exactly as passed by the caller. `attach` pre-binds user arguments first - the handler signature is `(...userArguments, event)`, so data comes before the DOM event.
259
+
260
+ ---
261
+
318
262
  ## Complete Example
319
263
 
320
- Complete table row component from the codebase:
264
+ A cached table row using `ForEachArray`:
265
+
321
266
  ```javascript
322
267
  import { useCache } from 'native-document';
323
- import { Tr, Td, Link, Button } from 'native-document/src/elements';
324
- import { Observable, ForEachArray, TBody } from 'native-document';
268
+ import { ForEachArray, Tr, Td, Link, Button, TBody } from 'native-document/elements';
269
+ import { Observable } from 'native-document';
325
270
 
326
- // App state
327
271
  const AppService = {
328
- data: Observable.array([
272
+ data: Observable.array([
329
273
  { id: 1, label: 'Item 1' },
330
274
  { id: 2, label: 'Item 2' },
331
275
  { id: 3, label: 'Item 3' }
332
276
  ]),
333
277
  selected: Observable(null),
334
-
335
- select(id) {
336
- this.selected.set(id);
337
- },
338
-
339
- remove(id) {
278
+
279
+ select(id) { this.selected.set(id); },
280
+ remove(id) {
340
281
  const index = this.data.val().findIndex(item => item.id === id);
341
- if (index > -1) {
342
- this.data.remove(index);
343
- }
282
+ if (index > -1) this.data.remove(index);
344
283
  }
345
284
  };
346
285
 
347
- // Cached table row with bindings
348
286
  const TableRowBuilder = useCache(($binder) => {
349
- // Class binding with Observable.when()
350
- const isSelected = $binder.class((item) => {
351
- return AppService.selected.when(item.id);
352
- });
353
-
354
- // Value bindings
355
- const id = $binder.value('id');
356
- const label = $binder.value('label');
357
-
358
- // Event handler bindings - receive (event, item)
359
- const rowClick = $binder.attach((event, item) => {
360
- AppService.select(item.id);
361
- });
362
-
363
- const removeClick = $binder.attach((event, item) => {
364
- AppService.remove(item.id);
365
- });
366
-
287
+ const isSelected = $binder.class(item => AppService.selected.when(item.id));
288
+ const id = $binder.id;
289
+ const label = $binder.label;
290
+ const rowClick = $binder.attach((item, event) => AppService.select(item.id));
291
+ const removeClick = $binder.attach((item, event) => AppService.remove(item.id));
292
+
367
293
  return Tr({ class: { 'selected': isSelected } }, [
368
294
  Td({ class: 'col-md-1' }, id),
369
- Td({ class: 'col-md-4' },
295
+ Td({ class: 'col-md-4' },
370
296
  Link(label).nd.attach('onClick', rowClick)
371
297
  ),
372
298
  Td({ class: 'col-md-1' },
373
- Button('×').nd.attach('onClick', removeClick)
299
+ Button('x').nd.attach('onClick', removeClick)
374
300
  ),
375
301
  Td({ class: 'col-md-6' })
376
302
  ]);
377
303
  });
378
304
 
379
- // Usage with ForEachArray
380
- const TableBody = TBody(
381
- ForEachArray(AppService.data, TableRowBuilder)
382
- );
305
+ const TableBody = TBody(ForEachArray(AppService.data, TableRowBuilder));
383
306
  ```
384
307
 
385
- ## Complex Binding Examples
386
-
387
- ### Multiple Bindings
388
- ```javascript
389
- import { useCache } from 'native-document';
390
- import { Div, H3, Span, Img } from 'native-document/src/elements';
391
-
392
- const UserProfile = useCache(($binder) => {
393
- // Simple value bindings
394
- const email = $binder.value('email');
395
-
396
- // Computed value bindings - function receives user object directly
397
- const fullName = $binder.value((user) => {
398
- return `${user.firstName} ${user.lastName}`;
399
- });
400
-
401
- const memberSince = $binder.value((user) => {
402
- const date = new Date(user.joinedAt);
403
- return date.toLocaleDateString();
404
- });
405
-
406
- // Attribute binding - function receives user object directly
407
- const avatarSrc = $binder.attr((user) => {
408
- return user.avatarUrl;
409
- });
410
-
411
- // Class binding - function receives user object directly
412
- const isPremium = $binder.class((user) => {
413
- return user.plan === 'premium';
414
- });
415
-
416
- return Div({
417
- class: {
418
- 'user-profile': true,
419
- 'premium': isPremium
420
- }
421
- }, [
422
- Img({ src: avatarSrc, class: 'avatar' }),
423
- H3(fullName),
424
- Span({ class: 'email' }, email),
425
- Span({ class: 'member-since' }, ['Member since: ', memberSince])
426
- ]);
427
- });
308
+ ---
428
309
 
429
- // Usage
430
- const profile = UserProfile({
431
- firstName: 'Alice',
432
- lastName: 'Johnson',
433
- email: 'alice@example.com',
434
- avatarUrl: '/avatars/alice.jpg',
435
- plan: 'premium',
436
- joinedAt: '2023-01-15'
437
- });
438
- ```
310
+ ## `useSingleton()` - Singleton Views
439
311
 
440
- ### Style Bindings
441
- ```javascript
442
- import { useCache } from 'native-document';
443
- import { Div } from 'native-document/src/elements';
444
-
445
- const ColoredBox = useCache(($binder) => {
446
- const text = $binder.value('text');
447
-
448
- // Multiple style bindings - each receives box object
449
- const bgColor = $binder.style((box) => {
450
- return box.color;
451
- });
452
-
453
- const boxWidth = $binder.style((box) => {
454
- return box.width + 'px';
455
- });
456
-
457
- const boxHeight = $binder.style((box) => {
458
- return box.height + 'px';
459
- });
460
-
461
- return Div({
462
- style: {
463
- backgroundColor: bgColor,
464
- width: boxWidth,
465
- height: boxHeight,
466
- display: 'flex',
467
- alignItems: 'center',
468
- justifyContent: 'center'
469
- }
470
- }, text);
471
- });
472
-
473
- // Usage
474
- const box = ColoredBox({
475
- text: 'Hello',
476
- color: '#3498db',
477
- width: 200,
478
- height: 100
479
- });
480
- ```
481
-
482
- ### Conditional Content
483
- ```javascript
484
- import { useCache } from 'native-document';
485
- import { Div, Span, Badge } from 'native-document/src/elements';
486
-
487
- const ProductCard = useCache(($binder) => {
488
- const name = $binder.value('name');
489
-
490
- // Computed price - receives product object
491
- const displayPrice = $binder.value((product) => {
492
- return product.salePrice
493
- ? `$${product.salePrice} (was $${product.price})`
494
- : `$${product.price}`;
495
- });
496
-
497
- // Conditional classes - receive product object
498
- const isOnSale = $binder.class((product) => {
499
- return product.salePrice !== null;
500
- });
501
-
502
- const isOutOfStock = $binder.class((product) => {
503
- return product.stock === 0;
504
- });
505
-
506
- // Stock badge text - receives product object
507
- const stockBadge = $binder.value((product) => {
508
- if (product.stock === 0) return 'Out of Stock';
509
- if (product.stock < 5) return 'Low Stock';
510
- return 'In Stock';
511
- });
512
-
513
- return Div({
514
- class: {
515
- 'product': true,
516
- 'on-sale': isOnSale,
517
- 'out-of-stock': isOutOfStock
518
- }
519
- }, [
520
- Span({ class: 'name' }, name),
521
- Span({ class: 'price' }, displayPrice),
522
- Badge(stockBadge)
523
- ]);
524
- });
525
-
526
- // Usage
527
- const product = ProductCard({
528
- name: 'Phone',
529
- price: 599,
530
- salePrice: 499,
531
- stock: 3
532
- });
533
- ```
534
-
535
- ### Multiple Arguments
536
- ```javascript
537
- import { useCache } from 'native-document';
538
- import { Div, H3, P } from 'native-document/src/elements';
539
-
540
- // Component that accepts multiple arguments
541
- const ArticleCard = useCache(($binder) => {
542
- // When using multiple args, functions receive them all
543
- const title = $binder.value((article, options) => {
544
- return options.uppercase ? article.title.toUpperCase() : article.title;
545
- });
546
-
547
- const excerpt = $binder.value((article, options) => {
548
- const length = options.excerptLength || 100;
549
- return article.content.substring(0, length) + '...';
550
- });
551
-
552
- return Div({ class: 'article' }, [
553
- H3(title),
554
- P(excerpt)
555
- ]);
556
- });
557
-
558
- // Usage with multiple arguments
559
- const card = ArticleCard(
560
- { title: 'My Article', content: 'Long article content...' },
561
- { uppercase: true, excerptLength: 150 }
562
- );
563
- ```
564
-
565
- ## useSingleton() - Singleton Views
566
-
567
- `useSingleton()` creates a view that is rendered only once, then updated through named sections.
312
+ `useSingleton()` returns a **function**. The first call (no args) renders and returns the cached DOM node. Subsequent calls accept an object with named section keys to update only the sections you want:
568
313
 
569
314
  ### Basic Usage
315
+
570
316
  ```javascript
571
317
  import { useSingleton } from 'native-document';
572
- import { Div, H1 } from 'native-document/src/elements';
318
+ import { Div, H1 } from 'native-document/elements';
573
319
 
574
- const Dashboard = useSingleton((view) => {
320
+ const AppLayout = useSingleton((view) => {
575
321
  return Div({ class: 'dashboard' }, [
576
322
  H1('Dashboard'),
577
-
578
- // Named section - can be updated later
579
323
  view.createSection('content'),
580
-
581
324
  Div({ class: 'footer' }, 'Footer')
582
325
  ]);
583
326
  });
584
327
 
585
- // First render - creates the view
586
- const dashboard = Dashboard.render();
587
- document.body.appendChild(dashboard);
328
+ // First call - renders the cached DOM node
329
+ const layout = AppLayout();
330
+ document.body.appendChild(layout);
588
331
 
589
- // Update specific section
590
- Dashboard.render([
591
- Div(['Updated content at ', new Date().toLocaleTimeString()])
592
- ]);
332
+ // Update a specific section by name
333
+ AppLayout({ content: Div(['Updated at ', new Date().toLocaleTimeString()]) });
593
334
  ```
594
335
 
595
- ### createSection(name, transformFn?)
336
+ ### `view.createSection(name, transformFn?)`
337
+
338
+ Creates a named section. An optional transform function wraps the content before insertion:
596
339
 
597
- Creates a named section that can be updated. Optional transform function wraps the content:
598
340
  ```javascript
599
341
  import { useSingleton } from 'native-document';
600
- import { Div, Section, Header, Main } from 'native-document/src/elements';
342
+ import { Div, Header, Main, Section } from 'native-document/elements';
601
343
 
602
- const Layout = useSingleton((view) => {
344
+ const AppLayout = useSingleton((view) => {
603
345
  return Div([
604
346
  Header([
605
- // Section without wrapper
606
347
  view.createSection('header')
607
348
  ]),
608
349
  Main([
609
- // Section with wrapper function
610
- view.createSection('main', (content) => {
611
- return Section({ class: 'main-section' }, content);
612
- })
350
+ view.createSection('main', content =>
351
+ Section({ class: 'main-section' }, content)
352
+ )
613
353
  ])
614
354
  ]);
615
355
  });
616
356
 
617
- // Render layout
618
- const layout = Layout.render();
357
+ const layout = AppLayout();
619
358
  document.body.appendChild(layout);
620
359
 
621
- // Update header - content inserted directly
622
- Layout.render([H1('My App')]);
623
-
624
- // Update main - content wrapped in Section
625
- Layout.render([P('Main content')]);
626
- // Result: <section class="main-section"><p>Main content</p></section>
360
+ AppLayout({ header: H1('My App') }); // updates header only
361
+ AppLayout({ main: P('Main content') }); // updates main only - wrapped in Section
627
362
  ```
628
363
 
629
364
  ### Multiple Sections
365
+
630
366
  ```javascript
631
367
  import { useSingleton } from 'native-document';
632
- import { Div, Header, Main, Aside, H1, P, Ul, Li } from 'native-document/src/elements';
368
+ import { Div, Header, Main, Aside, H1, P, Ul, Li } from 'native-document/elements';
633
369
 
634
370
  const AppLayout = useSingleton((view) => {
635
371
  return Div({ class: 'app' }, [
636
- Header([
637
- view.createSection('header')
638
- ]),
372
+ Header([view.createSection('header')]),
639
373
  Div({ class: 'container' }, [
640
- Main([
641
- view.createSection('main')
642
- ]),
643
- Aside([
644
- view.createSection('sidebar')
645
- ])
374
+ Main([view.createSection('main')]),
375
+ Aside([view.createSection('sidebar')])
646
376
  ])
647
377
  ]);
648
378
  });
649
379
 
650
- // Render initial layout
651
- const app = AppLayout.render();
380
+ const app = AppLayout();
652
381
  document.body.appendChild(app);
653
382
 
654
- // Update sections sequentially
655
- Dashboard.render([H1('My App')]); // Updates header
656
- Dashboard.render([P('Main content')]); // Updates main
657
- Dashboard.render([Ul([Li('Item 1')])]); // Updates sidebar
658
- ```
659
-
660
- ## Best Practices
661
-
662
- ### 1. Use Descriptive Binding Names
663
- ```javascript
664
- import { useCache } from 'native-document';
665
-
666
- // ✅ Good: Clear variable names
667
- const UserCard = useCache(($binder) => {
668
- const userName = $binder.value('name');
669
- const userEmail = $binder.value('email');
670
- const isPremiumUser = $binder.class((user) => user.premium);
671
-
672
- return Div({ class: { 'premium': isPremiumUser } }, [
673
- Span(userName),
674
- Span(userEmail)
675
- ]);
676
- });
677
-
678
- // ❌ Bad: Generic names
679
- const UserCard = useCache(($binder) => {
680
- const v1 = $binder.value('name');
681
- const v2 = $binder.value('email');
682
- const c1 = $binder.class((user) => user.premium);
683
- });
684
- ```
685
-
686
- ### 2. Remember Arguments Are Passed Directly
687
- ```javascript
688
- import { useCache } from 'native-document';
689
-
690
- // ✅ Good: Direct access to data
691
- const Card = useCache(($binder) => {
692
- const value = $binder.value((item) => {
693
- return item.property; // item is the object passed
694
- });
695
- });
696
-
697
- // Component call
698
- Card({ property: 'value' });
699
- ```
700
-
701
- ### 3. Use .nd.attach() for Event Handlers
702
- ```javascript
703
- import { useCache } from 'native-document';
704
- import { Button } from 'native-document/src/elements';
705
-
706
- // ✅ Good: Use binder.attach with .nd.attach()
707
- const ActionButton = useCache(($binder) => {
708
- const label = $binder.value('label');
709
-
710
- // Handler receives (event, data)
711
- const handleClick = $binder.attach((event, data) => {
712
- console.log('Clicked:', data);
713
- });
714
-
715
- return Button(label).nd.attach('onClick', handleClick);
716
- });
717
-
718
- // ❌ Bad: Direct event handler (won't receive data)
719
- const ActionButton = useCache(($binder) => {
720
- const label = $binder.value('label');
721
-
722
- return Button(label).nd.onClick((event) => {
723
- // No access to data here
724
- });
725
- });
726
- ```
727
-
728
- ### 4. Combine with Observable.when()
729
- ```javascript
730
- import { useCache } from 'native-document';
731
- import { Observable } from 'native-document';
732
- import { Li } from 'native-document/src/elements';
733
-
734
- const activeId = Observable(null);
735
-
736
- // ✅ Good: Use Observable.when() for reactive class binding
737
- const ListItem = useCache(($binder) => {
738
- const label = $binder.value('label');
739
-
740
- // Class updates automatically when activeId changes
741
- const isActive = $binder.class((item) => {
742
- return activeId.when(item.id);
743
- });
744
-
745
- return Li({ class: { 'active': isActive } }, label);
746
- });
747
- ```
748
-
749
- ### 5. Keep Transform Functions Simple
750
- ```javascript
751
- import { useCache } from 'native-document';
383
+ // Update sections individually
384
+ AppLayout({ header: H1('My App') });
385
+ AppLayout({ main: P('Main content') });
386
+ AppLayout({ sidebar: Ul([Li('Item 1')]) });
752
387
 
753
- // Good: Simple, focused transforms
754
- const Card = useCache(($binder) => {
755
- const price = $binder.value((product) => {
756
- return `$${product.price.toFixed(2)}`;
757
- });
758
-
759
- const inStock = $binder.class((product) => {
760
- return product.stock > 0;
761
- });
762
-
763
- return Div({ class: { 'in-stock': inStock } }, price);
764
- });
765
-
766
- // ❌ Bad: Complex logic
767
- const Card = useCache(($binder) => {
768
- const value = $binder.value((product) => {
769
- // Multiple API calls
770
- // Complex calculations
771
- // Side effects
772
- return result;
773
- });
388
+ // Or update multiple sections at once
389
+ AppLayout({
390
+ header: H1('New Title'),
391
+ main: P('New content')
774
392
  });
775
393
  ```
776
394
 
777
- ## Binder API Reference
778
-
779
- | Method | Parameters | Returns | Description |
780
- |--------|------------|---------|-------------|
781
- | `$binder.value(key)` | `key: string` | `TemplateBinding` | Bind to property by name |
782
- | `$binder.value(fn)` | `fn: (...args) => any` | `TemplateBinding` | Bind with transform function |
783
- | `$binder.property(key)` | `key: string` | `TemplateBinding` | Alias for value() |
784
- | `$binder.class(fn)` | `fn: (...args) => boolean` | `TemplateBinding` | Bind CSS class |
785
- | `$binder.style(fn)` | `fn: (...args) => string` | `TemplateBinding` | Bind inline style value |
786
- | `$binder.attr(fn)` | `fn: (...args) => string` | `TemplateBinding` | Bind attribute value |
787
- | `$binder.attach(fn)` | `fn: (event, ...args) => void` | `TemplateBinding` | Bind event handler |
395
+ ---
788
396
 
789
- **Note:** All binder methods (except `attach`) receive arguments exactly as passed by the user. The `attach` method receives the DOM event as the first parameter, followed by user arguments.
397
+ ## Best Practices
790
398
 
791
- ## Next Steps
399
+ 1. Use `$binder.attach()` with `.nd.attach()` for event handlers - direct `.nd.onClick()` won't receive the data object
400
+ 2. Use `Observable.when()` inside `$binder.class()` for reactive class binding that responds to external state
401
+ 3. Keep transform functions simple and free of side effects
402
+ 4. Use `ForEachArray` (not `ForEach`) with `useCache` - it's designed for cached component cloning
403
+ 5. Use `useSingleton` for layouts and shells that are rendered once but have dynamic inner regions
792
404
 
793
- Explore related concepts and utilities:
405
+ ---
794
406
 
795
407
  ## Next Steps
796
408
 
797
- - **[Getting Started](getting-started.md)** - Installation and first steps
798
- - **[Core Concepts](core-concepts.md)** - Understanding the fundamentals
799
- - **[Observables](observables.md)** - Reactive state management
800
- - **[Elements](elements.md)** - Creating and composing UI
801
- - **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
802
- - **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
803
- - **[Routing](routing.md)** - Navigation and URL management
804
- - **[State Management](state-management.md)** - Global state patterns
805
- - **[NDElement](native-document-element.md)** - Native Document Element
806
- - **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
807
- - **[Args Validation](validation.md)** - Function Argument Validation
808
- - **[Memory Management](memory-management.md)** - Memory management
409
+ - **[Elements](./elements.md)** - Creating and composing UI
410
+ - **[List Rendering](./list-rendering.md)** - ForEach and ForEachArray
411
+ - **[NDElement](./native-document-element.md)** - `.nd.attach()` reference
412
+ - **[Observables](./observables.md)** - Reactive state management
413
+ - **[State Management](./state-management.md)** - Global state patterns
809
414
 
810
415
  ## Utilities
811
416
 
812
- - **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
813
- - **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
814
- - **[Filters](docs/utils/filters.md)** - Data filtering helpers
417
+ - **[Cache](./cache.md)** - Lazy initialization and singleton patterns
418
+ - **[NativeFetch](./native-fetch.md)** - HTTP client with interceptors
419
+ - **[Filters](./filters.md)** - Data filtering helpers