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,5 +1,5 @@
1
- import ObservableItem from "./ObservableItem";
2
- import {debounce} from "../utils/helpers";
1
+ import ObservableItem from './ObservableItem';
2
+ import {debounce} from '../utils/helpers';
3
3
 
4
4
  const STATE = {
5
5
  UNRESOLVED: 'unresolved',
@@ -9,6 +9,28 @@ const STATE = {
9
9
  ERRORED: 'errored',
10
10
  };
11
11
 
12
+ /**
13
+ * Reactive async data fetcher with built-in state management.
14
+ * Tracks loading, ready, refreshing, and error states automatically.
15
+ * Use Observable.resource() rather than instantiating directly.
16
+ *
17
+ * @constructor
18
+ * @param {(...depValues: any[], signal?: AbortSignal) => Promise<*>} fn - Async function to fetch data. Receives dependency values as arguments. If its arity exceeds the number of dependencies, an AbortSignal is passed as the last argument.
19
+ * @param {ObservableItem[]} deps - Observable dependencies — resource re-fetches when any changes
20
+ * @param {{ auto?: boolean, lazy?: boolean, debounce?: number, into?: ObservableItem, apply?: Function }} config - Configuration
21
+ * @param {boolean} [config.auto=false] - If true, fetch runs automatically on creation (or when deps change)
22
+ * @param {boolean} [config.lazy=false] - If true with deps, does not fetch immediately — waits for first dep change
23
+ * @param {number} [config.debounce=0] - Debounce delay in ms for dependency-triggered re-fetches
24
+ * @param {ObservableItem} [config.into] - Observable to write results into instead of creating a new one
25
+ * @param {Function} [config.apply] - Custom function to apply the result to this.data
26
+ * @example
27
+ * const userId = Observable(1);
28
+ * const user = Observable.resource(
29
+ * async (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),
30
+ * [userId],
31
+ * { auto: true }
32
+ * );
33
+ */
12
34
  export default function ObservableResource(fn, deps, config) {
13
35
  this.$fn = (config.debounce > 0) ? debounce(fn, config.debounce) : fn;
14
36
  this.$dependencies = deps;
@@ -22,7 +44,7 @@ export default function ObservableResource(fn, deps, config) {
22
44
 
23
45
  this.loading = ObservableItem.computed(
24
46
  (state) => state === STATE.PENDING || state === STATE.REFRESHING,
25
- [this.state]
47
+ [this.state],
26
48
  );
27
49
 
28
50
  if (config.auto) {
@@ -104,6 +126,11 @@ ObservableResource.prototype.$runWithoutAbortController = function(isRefetch = f
104
126
  });
105
127
  };
106
128
 
129
+ ObservableResource.prototype.into = function($observable) {
130
+ this.data = $observable;
131
+ return this;
132
+ };
133
+
107
134
  ObservableResource.prototype.$run = function(isRefetch = false) {
108
135
  const needsSignal = this.$fn.length > this.$dependencies.length;
109
136
  if(needsSignal) {
@@ -129,58 +156,138 @@ ObservableResource.prototype.$watchDependencies = function() {
129
156
  }
130
157
  };
131
158
 
159
+ /**
160
+ * Sets a custom function to apply fetched results to this.data.
161
+ * Useful when the raw response needs transformation before storing.
162
+ *
163
+ * @param {(result: *, data: ObservableItem) => void} fn - Function receiving the result and the data observable
164
+ * @returns {this}
165
+ * @example
166
+ * resource.apply((result, data) => data.set(result.items));
167
+ */
132
168
  ObservableResource.prototype.apply = function(fn) {
133
169
  this.$config.apply = fn;
134
170
  return this;
135
171
  };
172
+
173
+ /**
174
+ * Redirects fetched results into an existing ObservableItem instead of the default internal one.
175
+ * Updates both this.data reference and config.into.
176
+ *
177
+ * @param {ObservableItem} $observable - Target observable to write results into
178
+ * @returns {this}
179
+ * @example
180
+ * const items = Observable([]);
181
+ * resource.into(items);
182
+ */
136
183
  ObservableResource.prototype.into = function($observable) {
137
184
  this.$config.into = $observable;
138
185
  this.data = $observable;
139
186
  return this;
140
187
  };
141
188
 
189
+ /**
190
+ * Triggers a fresh fetch (state transitions to 'pending').
191
+ * Use when no prior data exists or when a full reload is needed.
192
+ *
193
+ * @returns {this}
194
+ */
142
195
  ObservableResource.prototype.fetch = function() {
143
196
  this.$run(false);
144
197
  return this;
145
198
  };
146
199
 
200
+ /**
201
+ * Triggers a re-fetch (state transitions to 'refreshing' if data already exists).
202
+ * Use when you want to reload while keeping the previous data visible.
203
+ *
204
+ * @returns {this}
205
+ */
147
206
  ObservableResource.prototype.refetch = function() {
148
207
  this.$run(true);
149
208
  return this;
150
209
  };
151
210
 
211
+ /**
212
+ * Manually sets the data value and marks the state as 'ready'.
213
+ * Useful for optimistic updates or seeding initial data without a network call.
214
+ *
215
+ * @param {*} value - New value to set on this.data
216
+ * @returns {this}
217
+ * @example
218
+ * resource.mutate([...resource.data.val(), newItem]);
219
+ */
152
220
  ObservableResource.prototype.mutate = function(value) {
153
221
  this.data.set(value);
154
222
  this.state.set(STATE.READY);
155
223
  return this;
156
224
  };
157
225
 
226
+ /**
227
+ * Cancels any pending request, unsubscribes from all dependencies, and clears subscriptions.
228
+ * Call this when the component using this resource is unmounted.
229
+ *
230
+ * @returns {void}
231
+ */
158
232
  ObservableResource.prototype.destroy = function() {
159
233
  this.$abort();
160
234
  this.$subscriptions.forEach(unsub => unsub());
161
235
  this.$subscriptions = [];
162
236
  };
163
237
 
238
+ /**
239
+ * Returns a derived observable that emits true when the state is 'ready'.
240
+ *
241
+ * @returns {ObservableChecker<boolean>}
242
+ */
164
243
  ObservableResource.prototype.isReady = function() {
165
244
  return this.state.isEqualTo(STATE.READY);
166
245
  };
167
246
 
247
+ /**
248
+ * Returns a derived observable that emits true when the state is 'pending' (initial load).
249
+ *
250
+ * @returns {ObservableChecker<boolean>}
251
+ */
168
252
  ObservableResource.prototype.isPending = function() {
169
253
  return this.state.isEqualTo(STATE.PENDING);
170
254
  };
171
255
 
256
+ /**
257
+ * Returns a derived observable that emits true when the state is 'refreshing' (reload with existing data).
258
+ *
259
+ * @returns {ObservableChecker<boolean>}
260
+ */
172
261
  ObservableResource.prototype.isRefreshing = function() {
173
262
  return this.state.isEqualTo(STATE.REFRESHING);
174
263
  };
175
264
 
265
+ /**
266
+ * Returns a derived observable that emits true when the state is 'errored'.
267
+ *
268
+ * @returns {ObservableChecker<boolean>}
269
+ */
176
270
  ObservableResource.prototype.isErrored = function() {
177
271
  return this.state.isEqualTo(STATE.ERRORED);
178
272
  };
179
273
 
274
+ /**
275
+ * Returns a derived observable that emits true when no fetch has been triggered yet.
276
+ *
277
+ * @returns {ObservableChecker<boolean>}
278
+ */
180
279
  ObservableResource.prototype.isUnresolved = function() {
181
280
  return this.state.isEqualTo(STATE.UNRESOLVED);
182
281
  };
183
282
 
283
+ /**
284
+ * Registers a callback that is called every time a fetch completes successfully.
285
+ *
286
+ * @param {(value: *) => void} callback - Called with the fetched data value
287
+ * @returns {this}
288
+ * @example
289
+ * resource.onSuccess((data) => console.log('Loaded:', data));
290
+ */
184
291
  ObservableResource.prototype.onSuccess = function(callback) {
185
292
  this.data.subscribe((value) => {
186
293
  if (this.state.val() === STATE.READY) {
@@ -190,6 +297,14 @@ ObservableResource.prototype.onSuccess = function(callback) {
190
297
  return this;
191
298
  };
192
299
 
300
+ /**
301
+ * Registers a callback called every time a fetch fails.
302
+ *
303
+ * @param {(error: Error) => void} callback - Called with the error object
304
+ * @returns {this}
305
+ * @example
306
+ * resource.onError((err) => console.error('Failed:', err.message));
307
+ */
193
308
  ObservableResource.prototype.onError = function(callback) {
194
309
  this.error.subscribe((err) => {
195
310
  if (err !== null) {
@@ -1,7 +1,14 @@
1
- import { Observable } from "./Observable";
2
- import NativeDocumentError from "../errors/NativeDocumentError";
3
- import DebugManager from "../utils/debug-manager";
4
- import {$getFromStorage, $saveToStorage, LocalStorage} from "../utils/localstorage";
1
+ import { Observable } from './Observable';
2
+ import NativeDocumentError from '../errors/NativeDocumentError';
3
+ import DebugManager from '../utils/debug-manager';
4
+ import {$getFromStorage, $saveToStorage, LocalStorage} from '../utils/localstorage';
5
+ import Validator from '../utils/validator';
6
+
7
+
8
+ const WRITE_METHODS = [
9
+ 'use', 'get', 'create', 'createResettable', 'createComposed',
10
+ 'createPersistent', 'createPersistentResettable', 'delete', 'reset',
11
+ ];
5
12
 
6
13
  export const StoreFactory = function() {
7
14
 
@@ -16,7 +23,7 @@ export const StoreFactory = function() {
16
23
  if (!item) {
17
24
  DebugManager.error('Store', `Store.${method}('${name}') : store not found. Did you call Store.create('${name}') first?`);
18
25
  throw new NativeDocumentError(
19
- `Store.${method}('${name}') : store not found.`
26
+ `Store.${method}('${name}') : store not found.`,
20
27
  );
21
28
  }
22
29
  return item;
@@ -29,7 +36,7 @@ export const StoreFactory = function() {
29
36
  const readOnlyError = (method) => () => {
30
37
  DebugManager.error('Store', `Store.${context}('${name}') is read-only. '${method}()' is not allowed.`);
31
38
  throw new NativeDocumentError(
32
- `Store.${context}('${name}') is read-only.`
39
+ `Store.${context}('${name}') is read-only.`,
33
40
  );
34
41
  };
35
42
  observer.set = readOnlyError('set');
@@ -41,11 +48,11 @@ export const StoreFactory = function() {
41
48
  if(Array.isArray(value)) {
42
49
  return Observable.array(value, options);
43
50
  }
44
- if(typeof value === 'object') {
51
+ if(Validator.isJson(value)) {
45
52
  return Observable.object(value, options);
46
53
  }
47
54
  return Observable(value, options);
48
- }
55
+ };
49
56
 
50
57
  const $api = {
51
58
  /**
@@ -60,10 +67,10 @@ export const StoreFactory = function() {
60
67
  if ($stores.has(name)) {
61
68
  DebugManager.warn('Store', `Store.create('${name}') : a store with this name already exists. Use Store.get('${name}') to retrieve it.`);
62
69
  throw new NativeDocumentError(
63
- `Store.create('${name}') : a store with this name already exists.`
70
+ `Store.create('${name}') : a store with this name already exists.`,
64
71
  );
65
72
  }
66
- const observer = $createObservable(value)
73
+ const observer = $createObservable(value);
67
74
  $stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
68
75
  return observer;
69
76
  },
@@ -81,7 +88,7 @@ export const StoreFactory = function() {
81
88
  if ($stores.has(name)) {
82
89
  DebugManager.warn('Store', `Store.createResettable('${name}') : a store with this name already exists.`);
83
90
  throw new NativeDocumentError(
84
- `Store.createResettable('${name}') : a store with this name already exists.`
91
+ `Store.createResettable('${name}') : a store with this name already exists.`,
85
92
  );
86
93
  }
87
94
  const observer = $createObservable(value, { reset: true });
@@ -117,17 +124,17 @@ export const StoreFactory = function() {
117
124
  if ($stores.has(name)) {
118
125
  DebugManager.warn('Store', `Store.createComposed('${name}') : a store with this name already exists.`);
119
126
  throw new NativeDocumentError(
120
- `Store.createComposed('${name}') : a store with this name already exists.`
127
+ `Store.createComposed('${name}') : a store with this name already exists.`,
121
128
  );
122
129
  }
123
130
  if (typeof computation !== 'function') {
124
131
  throw new NativeDocumentError(
125
- `Store.createComposed('${name}') : computation must be a function.`
132
+ `Store.createComposed('${name}') : computation must be a function.`,
126
133
  );
127
134
  }
128
135
  if (!Array.isArray(dependencies) || dependencies.length === 0) {
129
136
  throw new NativeDocumentError(
130
- `Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`
137
+ `Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`,
131
138
  );
132
139
  }
133
140
 
@@ -140,7 +147,7 @@ export const StoreFactory = function() {
140
147
  if (!depItem) {
141
148
  DebugManager.error('Store', `Store.createComposed('${name}') : dependency '${depName}' not found. Create it first.`);
142
149
  throw new NativeDocumentError(
143
- `Store.createComposed('${name}') : dependency store '${depName}' not found.`
150
+ `Store.createComposed('${name}') : dependency store '${depName}' not found.`,
144
151
  );
145
152
  }
146
153
  return depItem.observer;
@@ -174,13 +181,13 @@ export const StoreFactory = function() {
174
181
  if (item.composed) {
175
182
  DebugManager.error('Store', `Store.reset('${name}') : composed stores cannot be reset. Their value is derived from dependencies.`);
176
183
  throw new NativeDocumentError(
177
- `Store.reset('${name}') : composed stores cannot be reset.`
184
+ `Store.reset('${name}') : composed stores cannot be reset.`,
178
185
  );
179
186
  }
180
187
  if (!item.resettable) {
181
188
  DebugManager.error('Store', `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`);
182
189
  throw new NativeDocumentError(
183
- `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`
190
+ `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`,
184
191
  );
185
192
  }
186
193
  item.observer.reset();
@@ -201,7 +208,7 @@ export const StoreFactory = function() {
201
208
  if (item.composed) {
202
209
  DebugManager.error('Store', `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`);
203
210
  throw new NativeDocumentError(
204
- `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`
211
+ `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`,
205
212
  );
206
213
  }
207
214
 
@@ -239,7 +246,9 @@ export const StoreFactory = function() {
239
246
  const { observer: originalObserver, subscribers } = $getStoreOrThrow('follow', name);
240
247
  const observerFollower = $createObservable(originalObserver.val());
241
248
 
242
- const onStoreChange = value => observerFollower.set(value);
249
+ const originalSet = observerFollower.set.bind(observerFollower);
250
+ const onStoreChange = value => originalSet(value);
251
+
243
252
  originalObserver.subscribe(onStoreChange);
244
253
 
245
254
  $applyReadOnly(observerFollower, name, 'follow');
@@ -295,6 +304,7 @@ export const StoreFactory = function() {
295
304
  item.subscribers.clear();
296
305
  item.observer.cleanup();
297
306
  $stores.delete(name);
307
+ $followersCache.delete(name);
298
308
  },
299
309
  /**
300
310
  * Creates an isolated store group with its own state namespace.
@@ -349,18 +359,60 @@ export const StoreFactory = function() {
349
359
  callback && callback(store);
350
360
  return store;
351
361
  },
362
+
363
+ /**
364
+ * Creates a store that is automatically persisted to localStorage.
365
+ * On creation, the store is initialized with the value from localStorage
366
+ * if it exists, otherwise falls back to the provided default value.
367
+ * Every mutation is automatically saved to localStorage.
368
+ *
369
+ * @param {string} name - Store name
370
+ * @param {*} value - Default value if nothing is found in localStorage
371
+ * @param {string} [localstorage_key] - Custom localStorage key. Defaults to the store name.
372
+ * @returns {ObservableItem}
373
+ *
374
+ * @example
375
+ * const $theme = Store.createPersistent('theme', 'light');
376
+ *
377
+ * $theme.set('dark'); // saved to localStorage automatically
378
+ *
379
+ * // With a custom key
380
+ * const $lang = Store.createPersistent('language', 'en', 'nd:lang');
381
+ */
352
382
  createPersistent(name, value, localstorage_key) {
353
383
  localstorage_key = localstorage_key || name;
354
384
  const observer = this.create(name, $getFromStorage(localstorage_key, value));
355
- const saver = $saveToStorage(value)
385
+ const saver = $saveToStorage(value);
356
386
 
357
387
  observer.subscribe((val) => saver(localstorage_key, val));
358
388
  return observer;
359
389
  },
390
+
391
+ /**
392
+ * Creates a resettable store that is automatically persisted to localStorage.
393
+ * On creation, the store is initialized with the value from localStorage
394
+ * if it exists, otherwise falls back to the provided default value.
395
+ * Every mutation is automatically saved to localStorage.
396
+ * Calling reset() restores the initial value AND removes the localStorage entry.
397
+ *
398
+ * @param {string} name - Store name
399
+ * @param {*} value - Default value if nothing is found in localStorage
400
+ * @param {string} [localstorage_key] - Custom localStorage key. Defaults to the store name.
401
+ * @returns {ObservableItem}
402
+ *
403
+ * @example
404
+ * const $filters = Store.createPersistentResettable('filters', { category: null, date: null });
405
+ *
406
+ * $filters.set({ category: 'news', date: '2024-01-01' }); // saved to localStorage
407
+ * $filters.reset(); // restored to { category: null, date: null } + localStorage entry removed
408
+ *
409
+ * // With a custom key
410
+ * const $prefs = Store.createPersistentResettable('preferences', { lang: 'en' }, 'nd:prefs');
411
+ */
360
412
  createPersistentResettable(name, value, localstorage_key) {
361
413
  localstorage_key = localstorage_key || name;
362
414
  const observer = this.createResettable(name, $getFromStorage(localstorage_key, value));
363
- const saver = $saveToStorage(value)
415
+ const saver = $saveToStorage(value);
364
416
  observer.subscribe((val) => saver(localstorage_key, val));
365
417
 
366
418
  const originalReset = observer.reset.bind(observer);
@@ -370,7 +422,68 @@ export const StoreFactory = function() {
370
422
  };
371
423
 
372
424
  return observer;
373
- }
425
+ },
426
+ /**
427
+ * Returns a read-only proxy of this store.
428
+ * All write operations (use, get, create, createResettable, createComposed,
429
+ * createPersistent, createPersistentResettable, delete, reset) will throw.
430
+ * Property access returns a read-only follower via follow().
431
+ *
432
+ * The recommended pattern is to keep the original store private
433
+ * and export only the protected version as the public contract.
434
+ *
435
+ * @returns {Proxy}
436
+ *
437
+ * @example
438
+ * // store/user.store.js
439
+ *
440
+ * const PrivateUserStore = Store.group('user', (group) => {
441
+ * group.create('profile', null);
442
+ * group.create('role', 'viewer');
443
+ * group.create('token', null);
444
+ * });
445
+ *
446
+ * // Only the read-only version is exported
447
+ * export const UserStore = PrivateUserStore.protected();
448
+ *
449
+ * // --- In any other module ---
450
+ *
451
+ * import { UserStore } from './store/user.store.js';
452
+ *
453
+ * UserStore.profile // ✅ follower read-only
454
+ * UserStore.follow('role') // ✅
455
+ * UserStore.has('token') // ✅
456
+ *
457
+ * UserStore.use('profile') // ❌ throws — read-only store
458
+ * UserStore.get('profile') // ❌ throws — read-only store
459
+ * UserStore.create('x', 1) // ❌ throws — read-only store
460
+ */
461
+ protected() {
462
+ return new Proxy($api, {
463
+ get(target, prop) {
464
+ if (typeof prop === 'symbol' || prop.startsWith('$')) {
465
+ return target[prop];
466
+ }
467
+ if (WRITE_METHODS.includes(prop)) {
468
+ return () => {
469
+ throw new NativeDocumentError(
470
+ `Store.${prop}() is not allowed on a read-only store. Use the original store reference instead.`
471
+ );
472
+ };
473
+ }
474
+ if (target.has(prop)) {
475
+ return target.follow(prop);
476
+ }
477
+ return target[prop];
478
+ },
479
+ set() {
480
+ throw new NativeDocumentError('This store is read-only.');
481
+ },
482
+ deleteProperty() {
483
+ throw new NativeDocumentError('This store is read-only.');
484
+ },
485
+ });
486
+ },
374
487
  };
375
488
 
376
489
 
@@ -391,14 +504,17 @@ export const StoreFactory = function() {
391
504
  },
392
505
  set(target, prop, value) {
393
506
  DebugManager.error('Store', `Forbidden: You cannot overwrite the store key '${String(prop)}'. Use .use('${String(prop)}').set(value) instead.`);
394
- throw new NativeDocumentError(`Store structure is immutable. Use .set() on the observable.`);
507
+ throw new NativeDocumentError('Store structure is immutable. Use .set() on the observable.');
395
508
  },
396
509
  deleteProperty(target, prop) {
397
- throw new NativeDocumentError(`Store keys cannot be deleted.`);
398
- }
510
+ throw new NativeDocumentError('Store keys cannot be deleted.');
511
+ },
399
512
  });
400
513
  };
401
514
 
402
515
  export const Store = StoreFactory();
403
516
 
404
- Store.create('locale', navigator.language.split('-')[0] || 'en');
517
+ Store.create(
518
+ 'locale',
519
+ (typeof navigator !== 'undefined' ? navigator.language.split('-')[0] : 'en') || 'en'
520
+ );