native-document 1.0.165 → 1.0.168

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.d.ts +2 -0
  4. package/components.js +2 -1
  5. package/devtools/widget.js +1 -1
  6. package/dist/native-document.components.min.js +11589 -2983
  7. package/dist/native-document.dev.js +2280 -396
  8. package/dist/native-document.dev.js.map +1 -1
  9. package/dist/native-document.min.js +1 -1
  10. package/docs/advanced-components.md +213 -608
  11. package/docs/anchor.md +173 -312
  12. package/docs/cache.md +95 -803
  13. package/docs/cli.md +179 -0
  14. package/docs/components/accordion.md +172 -0
  15. package/docs/components/alert.md +99 -0
  16. package/docs/components/avatar.md +160 -0
  17. package/docs/components/badge.md +102 -0
  18. package/docs/components/breadcrumb.md +89 -0
  19. package/docs/components/button.md +183 -0
  20. package/docs/components/card.md +69 -0
  21. package/docs/components/context-menu.md +118 -0
  22. package/docs/components/data-table.md +345 -0
  23. package/docs/components/dropdown.md +214 -0
  24. package/docs/components/form/autocomplete-field.md +81 -0
  25. package/docs/components/form/checkbox-field.md +41 -0
  26. package/docs/components/form/checkbox-group-field.md +54 -0
  27. package/docs/components/form/color-field.md +64 -0
  28. package/docs/components/form/date-field.md +92 -0
  29. package/docs/components/form/field-collection.md +63 -0
  30. package/docs/components/form/file-field.md +203 -0
  31. package/docs/components/form/form-control.md +87 -0
  32. package/docs/components/form/image-field.md +90 -0
  33. package/docs/components/form/index.md +115 -0
  34. package/docs/components/form/number-field.md +65 -0
  35. package/docs/components/form/radio-field.md +51 -0
  36. package/docs/components/form/select-field.md +123 -0
  37. package/docs/components/form/slider.md +136 -0
  38. package/docs/components/form/string-field.md +134 -0
  39. package/docs/components/form/textarea-field.md +65 -0
  40. package/docs/components/form-fields.md +372 -0
  41. package/docs/components/getting-started.md +264 -0
  42. package/docs/components/index.md +337 -0
  43. package/docs/components/layout.md +279 -0
  44. package/docs/components/list.md +73 -0
  45. package/docs/components/menu.md +215 -0
  46. package/docs/components/modal.md +156 -0
  47. package/docs/components/pagination.md +95 -0
  48. package/docs/components/popover.md +131 -0
  49. package/docs/components/progress.md +111 -0
  50. package/docs/components/shortcut-manager.md +221 -0
  51. package/docs/components/simple-table.md +107 -0
  52. package/docs/components/skeleton.md +155 -0
  53. package/docs/components/spinner.md +100 -0
  54. package/docs/components/splitter.md +133 -0
  55. package/docs/components/stepper.md +163 -0
  56. package/docs/components/switch.md +113 -0
  57. package/docs/components/tabs.md +153 -0
  58. package/docs/components/toast.md +119 -0
  59. package/docs/components/tooltip.md +151 -0
  60. package/docs/components/traits.md +261 -0
  61. package/docs/conditional-rendering.md +170 -588
  62. package/docs/contributing.md +300 -25
  63. package/docs/core-concepts.md +205 -374
  64. package/docs/elements.md +251 -367
  65. package/docs/extending-native-document-element.md +192 -207
  66. package/docs/filters.md +153 -1122
  67. package/docs/getting-started.md +193 -267
  68. package/docs/i18n.md +241 -0
  69. package/docs/index.md +76 -0
  70. package/docs/lifecycle-events.md +143 -75
  71. package/docs/list-rendering.md +227 -852
  72. package/docs/memory-management.md +134 -47
  73. package/docs/native-document-element.md +337 -186
  74. package/docs/native-fetch.md +99 -630
  75. package/docs/observable-resource.md +364 -0
  76. package/docs/observables.md +592 -526
  77. package/docs/routing.md +244 -653
  78. package/docs/state-management.md +134 -241
  79. package/docs/svg-elements.md +231 -0
  80. package/docs/theming.md +409 -0
  81. package/docs/validation.md +95 -97
  82. package/docs/vitepress-conventions.md +219 -0
  83. package/eslint.config.js +28 -33
  84. package/i18n.js +1 -1
  85. package/i18n.ts +2 -0
  86. package/index.js +3 -0
  87. package/package.json +36 -14
  88. package/readme.md +269 -89
  89. package/src/components/$traits/has-draggable/HasDraggable.d.ts +4 -0
  90. package/src/components/$traits/has-draggable/HasDraggable.js +13 -0
  91. package/src/components/$traits/has-items/HasItems.d.ts +9 -0
  92. package/src/components/$traits/has-items/HasItems.js +6 -6
  93. package/src/components/$traits/has-position/HasFullPosition.d.ts +14 -0
  94. package/src/components/$traits/has-position/HasFullPosition.js +44 -0
  95. package/src/components/$traits/has-position/HasPosition.d.ts +7 -0
  96. package/src/components/$traits/has-position/HasPosition.js +23 -1
  97. package/src/components/$traits/has-resizable/HasResizable.d.ts +13 -0
  98. package/src/components/$traits/has-resizable/HasResizable.js +9 -0
  99. package/src/components/$traits/has-validation/HasValidation.d.ts +17 -0
  100. package/src/components/$traits/has-validation/HasValidation.js +54 -7
  101. package/src/components/BaseComponent.d.ts +32 -0
  102. package/src/components/BaseComponent.js +65 -9
  103. package/src/components/accordion/Accordion.js +39 -14
  104. package/src/components/accordion/AccordionItem.js +45 -14
  105. package/src/components/accordion/index.js +2 -2
  106. package/src/components/accordion/types/Accordion.d.ts +47 -0
  107. package/src/components/accordion/types/AccordionItem.d.ts +48 -0
  108. package/src/components/alert/Alert.js +70 -38
  109. package/src/components/alert/index.js +2 -2
  110. package/src/components/alert/types/Alert.d.ts +62 -0
  111. package/src/components/avatar/Avatar.js +49 -12
  112. package/src/components/avatar/AvatarGroup.js +50 -2
  113. package/src/components/avatar/index.js +2 -2
  114. package/src/components/avatar/types/Avatar.d.ts +74 -0
  115. package/src/components/avatar/types/AvatarGroup.d.ts +32 -0
  116. package/src/components/badge/Badge.js +125 -5
  117. package/src/components/badge/index.js +2 -2
  118. package/src/components/badge/types/Badge.d.ts +51 -0
  119. package/src/components/breadcrumb/BreadCrumb.js +61 -5
  120. package/src/components/breadcrumb/index.js +2 -2
  121. package/src/components/breadcrumb/types/BreadCrumb.d.ts +42 -0
  122. package/src/components/button/Button.js +164 -9
  123. package/src/components/button/index.js +1 -1
  124. package/src/components/button/types/Button.d.ts +62 -0
  125. package/src/components/card/Card.js +204 -32
  126. package/src/components/card/index.js +4 -4
  127. package/src/components/card/types/Card.d.ts +42 -0
  128. package/src/components/context-menu/ContextMenu.js +49 -5
  129. package/src/components/context-menu/ContextMenuGroup.js +15 -2
  130. package/src/components/context-menu/ContextMenuItem.js +14 -2
  131. package/src/components/context-menu/index.js +5 -5
  132. package/src/components/context-menu/types/ContextMenu.d.ts +30 -0
  133. package/src/components/context-menu/types/ContextMenuGroup.d.ts +18 -0
  134. package/src/components/context-menu/types/ContextMenuItem.d.ts +18 -0
  135. package/src/components/divider/Divider.js +120 -4
  136. package/src/components/divider/index.js +3 -3
  137. package/src/components/divider/types/Divider.d.ts +55 -0
  138. package/src/components/dropdown/Dropdown.js +239 -16
  139. package/src/components/dropdown/DropdownDivider.js +22 -2
  140. package/src/components/dropdown/DropdownGroup.js +44 -5
  141. package/src/components/dropdown/DropdownItem.js +76 -3
  142. package/src/components/dropdown/DropdownTrigger.js +49 -20
  143. package/src/components/dropdown/helpers.js +1 -1
  144. package/src/components/dropdown/index.js +6 -6
  145. package/src/components/dropdown/types/Dropdown.d.ts +88 -0
  146. package/src/components/dropdown/types/DropdownDivider.d.ts +20 -0
  147. package/src/components/dropdown/types/DropdownGroup.d.ts +25 -0
  148. package/src/components/dropdown/types/DropdownItem.d.ts +41 -0
  149. package/src/components/dropdown/types/DropdownTrigger.d.ts +32 -0
  150. package/src/components/form/FormControl.js +156 -13
  151. package/src/components/form/field/Field.js +172 -9
  152. package/src/components/form/field/FieldCollection.js +116 -12
  153. package/src/components/form/field/types/AutocompleteField.js +92 -2
  154. package/src/components/form/field/types/CheckboxField.js +43 -2
  155. package/src/components/form/field/types/CheckboxGroupField.js +83 -6
  156. package/src/components/form/field/types/ColorField.js +56 -3
  157. package/src/components/form/field/types/DateField.js +155 -4
  158. package/src/components/form/field/types/EmailField.js +54 -4
  159. package/src/components/form/field/types/FileField.js +140 -6
  160. package/src/components/form/field/types/HiddenField.js +27 -1
  161. package/src/components/form/field/types/ImageField.js +82 -3
  162. package/src/components/form/field/types/NumberField.js +97 -4
  163. package/src/components/form/field/types/PasswordField.js +103 -7
  164. package/src/components/form/field/types/RadioField.js +75 -4
  165. package/src/components/form/field/types/RangeField.js +67 -1
  166. package/src/components/form/field/types/SearchField.js +41 -2
  167. package/src/components/form/field/types/SelectField.js +133 -4
  168. package/src/components/form/field/types/StringField.js +91 -2
  169. package/src/components/form/field/types/TelField.js +55 -4
  170. package/src/components/form/field/types/TextAreaField.js +76 -2
  171. package/src/components/form/field/types/TimeField.js +120 -5
  172. package/src/components/form/field/types/UrlField.js +59 -4
  173. package/src/components/form/field/types/file-field-mode/FileAvatarMode.js +83 -4
  174. package/src/components/form/field/types/file-field-mode/FileDropzoneMode.js +61 -3
  175. package/src/components/form/field/types/file-field-mode/FileItemPreview.js +79 -3
  176. package/src/components/form/field/types/file-field-mode/FileNativeMode.js +24 -2
  177. package/src/components/form/field/types/file-field-mode/FileUploadButtonMode.js +64 -3
  178. package/src/components/form/field/types/file-field-mode/FileWallMode.js +56 -3
  179. package/src/components/form/index.js +28 -28
  180. package/src/components/form/types/Field.d.ts +73 -0
  181. package/src/components/form/types/FieldCollection.d.ts +53 -0
  182. package/src/components/form/types/FormControl.d.ts +64 -0
  183. package/src/components/form/types/fields/AutocompleteField.d.ts +48 -0
  184. package/src/components/form/types/fields/CheckboxField.d.ts +33 -0
  185. package/src/components/form/types/fields/CheckboxGroupField.d.ts +49 -0
  186. package/src/components/form/types/fields/ColorField.d.ts +37 -0
  187. package/src/components/form/types/fields/DateField.d.ts +70 -0
  188. package/src/components/form/types/fields/EmailField.d.ts +35 -0
  189. package/src/components/form/types/fields/FileAvatarMode.d.ts +46 -0
  190. package/src/components/form/types/fields/FileDropzoneMode.d.ts +28 -0
  191. package/src/components/form/types/fields/FileField.d.ts +56 -0
  192. package/src/components/form/types/fields/FileItemPreview.d.ts +35 -0
  193. package/src/components/form/types/fields/FileNativeMode.d.ts +21 -0
  194. package/src/components/form/types/fields/FileUploadButtonMode.d.ts +34 -0
  195. package/src/components/form/types/fields/FileWallMode.d.ts +32 -0
  196. package/src/components/form/types/fields/HiddenField.d.ts +26 -0
  197. package/src/components/form/types/fields/ImageField.d.ts +45 -0
  198. package/src/components/form/types/fields/NumberField.d.ts +48 -0
  199. package/src/components/form/types/fields/PasswordField.d.ts +46 -0
  200. package/src/components/form/types/fields/RadioField.d.ts +48 -0
  201. package/src/components/form/types/fields/RangeField.d.ts +44 -0
  202. package/src/components/form/types/fields/SearchField.d.ts +34 -0
  203. package/src/components/form/types/fields/SelectField.d.ts +71 -0
  204. package/src/components/form/types/fields/StringField.d.ts +48 -0
  205. package/src/components/form/types/fields/TelField.d.ts +37 -0
  206. package/src/components/form/types/fields/TextAreaField.d.ts +44 -0
  207. package/src/components/form/types/fields/TimeField.d.ts +51 -0
  208. package/src/components/form/types/fields/UrlField.d.ts +35 -0
  209. package/src/components/form/validation/Validation.js +54 -54
  210. package/src/components/index.d.ts +160 -0
  211. package/src/components/list/HasListItem.js +171 -0
  212. package/src/components/list/List.js +85 -67
  213. package/src/components/list/ListDivider.js +39 -0
  214. package/src/components/list/ListGroup.js +105 -38
  215. package/src/components/list/ListItem.js +158 -49
  216. package/src/components/list/index.js +8 -6
  217. package/src/components/list/types/List.d.ts +43 -0
  218. package/src/components/list/types/ListGroup.d.ts +37 -0
  219. package/src/components/list/types/ListItem.d.ts +53 -0
  220. package/src/components/menu/HasMenuItem.js +55 -6
  221. package/src/components/menu/Menu.js +113 -22
  222. package/src/components/menu/MenuDivider.js +18 -2
  223. package/src/components/menu/MenuGroup.js +61 -6
  224. package/src/components/menu/MenuItem.js +95 -11
  225. package/src/components/menu/MenuLink.js +27 -2
  226. package/src/components/menu/index.js +6 -6
  227. package/src/components/menu/types/Menu.d.ts +60 -0
  228. package/src/components/menu/types/MenuDivider.d.ts +19 -0
  229. package/src/components/menu/types/MenuGroup.d.ts +44 -0
  230. package/src/components/menu/types/MenuItem.d.ts +46 -0
  231. package/src/components/menu/types/MenuLink.d.ts +16 -0
  232. package/src/components/modal/Modal.js +258 -17
  233. package/src/components/modal/index.js +3 -3
  234. package/src/components/modal/types/Modal.d.ts +94 -0
  235. package/src/components/pagination/Pagination.js +155 -7
  236. package/src/components/pagination/index.js +3 -3
  237. package/src/components/pagination/types/Pagination.d.ts +68 -0
  238. package/src/components/popover/Popover.js +198 -11
  239. package/src/components/popover/PopoverFooter.js +33 -9
  240. package/src/components/popover/PopoverHeader.js +33 -8
  241. package/src/components/popover/index.js +4 -4
  242. package/src/components/popover/types/Popover.d.ts +83 -0
  243. package/src/components/popover/types/PopoverFooter.d.ts +24 -0
  244. package/src/components/popover/types/PopoverHeader.d.ts +26 -0
  245. package/src/components/progress/Progress.js +182 -13
  246. package/src/components/progress/index.js +3 -3
  247. package/src/components/progress/types/Progress.d.ts +77 -0
  248. package/src/components/skeleton/Skeleton.js +117 -49
  249. package/src/components/skeleton/index.js +3 -3
  250. package/src/components/skeleton/types/Skeleton.d.ts +55 -0
  251. package/src/components/slider/Slider.js +207 -10
  252. package/src/components/slider/index.js +2 -2
  253. package/src/components/slider/types/Slider.d.ts +82 -0
  254. package/src/components/spacer/Spacer.js +12 -3
  255. package/src/components/spacer/index.js +2 -2
  256. package/src/components/spacer/types/Spacer.d.ts +19 -0
  257. package/src/components/spinner/Spinner.js +180 -9
  258. package/src/components/spinner/index.js +3 -3
  259. package/src/components/spinner/types/Spinner.d.ts +71 -0
  260. package/src/components/splitter/Splitter.js +76 -13
  261. package/src/components/splitter/SplitterGutter.js +67 -5
  262. package/src/components/splitter/SplitterPanel.js +69 -2
  263. package/src/components/splitter/index.js +5 -5
  264. package/src/components/splitter/types/Splitter.d.ts +38 -0
  265. package/src/components/splitter/types/SplitterGutter.d.ts +38 -0
  266. package/src/components/splitter/types/SplitterPanel.d.ts +41 -0
  267. package/src/components/stacks/AbsoluteStack.js +23 -3
  268. package/src/components/stacks/FixedStack.js +23 -3
  269. package/src/components/stacks/HStack.js +24 -3
  270. package/src/components/stacks/PositionStack.js +111 -3
  271. package/src/components/stacks/RelativeStack.js +23 -3
  272. package/src/components/stacks/Stack.js +73 -2
  273. package/src/components/stacks/VStack.js +24 -4
  274. package/src/components/stacks/index.js +7 -7
  275. package/src/components/stacks/types/AbsoluteStack.d.ts +16 -0
  276. package/src/components/stacks/types/FixedStack.d.ts +16 -0
  277. package/src/components/stacks/types/HStack.d.ts +16 -0
  278. package/src/components/stacks/types/PositionStack.d.ts +54 -0
  279. package/src/components/stacks/types/RelativeStack.d.ts +17 -0
  280. package/src/components/stacks/types/Stack.d.ts +39 -0
  281. package/src/components/stacks/types/VStack.d.ts +16 -0
  282. package/src/components/stepper/Stepper.js +152 -12
  283. package/src/components/stepper/StepperStep.js +104 -3
  284. package/src/components/stepper/index.js +4 -4
  285. package/src/components/stepper/types/Stepper.d.ts +68 -0
  286. package/src/components/stepper/types/StepperStep.d.ts +54 -0
  287. package/src/components/switch/Switch.js +143 -6
  288. package/src/components/switch/index.js +1 -1
  289. package/src/components/switch/types/Switch.d.ts +55 -0
  290. package/src/components/table/Column.js +105 -6
  291. package/src/components/table/ColumnGroup.js +48 -3
  292. package/src/components/table/DataTable.js +256 -19
  293. package/src/components/table/SimpleTable.js +58 -4
  294. package/src/components/table/index.js +2 -2
  295. package/src/components/table/types/Column.d.ts +49 -0
  296. package/src/components/table/types/ColumnGroup.d.ts +28 -0
  297. package/src/components/table/types/DataTable.d.ts +97 -0
  298. package/src/components/table/types/SimpleTable.d.ts +40 -0
  299. package/src/components/tabs/Tabs.js +192 -5
  300. package/src/components/tabs/index.js +3 -3
  301. package/src/components/tabs/types/Tabs.d.ts +78 -0
  302. package/src/components/toast/Toast.js +133 -5
  303. package/src/components/toast/index.js +3 -3
  304. package/src/components/toast/types/Toast.d.ts +57 -0
  305. package/src/components/toast/types/ToastError.d.ts +7 -0
  306. package/src/components/toast/types/ToastInfo.d.ts +7 -0
  307. package/src/components/toast/types/ToastSuccess.d.ts +7 -0
  308. package/src/components/toast/types/ToastWarning.d.ts +7 -0
  309. package/src/components/tooltip/Tooltip.js +157 -13
  310. package/src/components/tooltip/index.js +2 -2
  311. package/src/components/tooltip/prototypes.js +1 -1
  312. package/src/components/tooltip/types/Tooltip.d.ts +65 -0
  313. package/src/core/data/MemoryManager.js +2 -2
  314. package/src/core/data/Observable.js +15 -18
  315. package/src/core/data/ObservableArray.js +118 -46
  316. package/src/core/data/ObservableChecker.js +2 -2
  317. package/src/core/data/ObservableItem.js +135 -21
  318. package/src/core/data/ObservableObject.js +126 -35
  319. package/src/core/data/ObservableResource.js +118 -3
  320. package/src/core/data/Store.js +142 -26
  321. package/src/core/data/observable-helpers/observable.is-to.js +196 -1
  322. package/src/core/data/observable-helpers/observable.prototypes.js +35 -8
  323. package/src/core/elements/anchor/anchor-with-sentinel.js +23 -2
  324. package/src/core/elements/anchor/anchor.js +16 -7
  325. package/src/core/elements/anchor/one-child-anchor-overwriting.js +2 -2
  326. package/src/core/elements/content-formatter.js +1 -1
  327. package/src/core/elements/control/for-each-array.js +9 -9
  328. package/src/core/elements/control/for-each.js +14 -14
  329. package/src/core/elements/control/show-if.js +11 -11
  330. package/src/core/elements/control/show-when.js +5 -5
  331. package/src/core/elements/control/switch.js +14 -14
  332. package/src/core/elements/description-list.js +1 -1
  333. package/src/core/elements/form.js +2 -2
  334. package/src/core/elements/fragment.js +1 -1
  335. package/src/core/elements/html5-semantics.js +1 -1
  336. package/src/core/elements/img.js +3 -3
  337. package/src/core/elements/interactive.js +1 -1
  338. package/src/core/elements/list.js +1 -1
  339. package/src/core/elements/medias.js +1 -1
  340. package/src/core/elements/meta-data.js +1 -1
  341. package/src/core/elements/svg.js +1 -1
  342. package/src/core/elements/table.js +1 -1
  343. package/src/core/errors/ArgTypesError.js +1 -1
  344. package/src/core/utils/HasEventEmitter.js +36 -2
  345. package/src/core/utils/args-types.js +9 -9
  346. package/src/core/utils/cache.js +1 -1
  347. package/src/core/utils/callback-handler.js +29 -0
  348. package/src/core/utils/debug-manager.js +6 -6
  349. package/src/core/utils/events.js +139 -139
  350. package/src/core/utils/filters/date.js +84 -3
  351. package/src/core/utils/filters/standard.js +136 -11
  352. package/src/core/utils/filters/strings.js +34 -2
  353. package/src/core/utils/filters/utils.js +40 -4
  354. package/src/core/utils/formatters.js +4 -4
  355. package/src/core/utils/helpers.js +39 -7
  356. package/src/core/utils/localstorage.js +11 -11
  357. package/src/core/utils/memoize.js +56 -3
  358. package/src/core/utils/plugins-manager.js +3 -3
  359. package/src/core/utils/property-accumulator.js +6 -6
  360. package/src/core/utils/prototypes.js +26 -1
  361. package/src/core/utils/shortcut-manager.js +2 -2
  362. package/src/core/utils/validator.js +8 -8
  363. package/src/core/wrappers/AttributesWrapper.js +32 -22
  364. package/src/core/wrappers/DocumentObserver.js +3 -3
  365. package/src/core/wrappers/ElementCreator.js +5 -5
  366. package/src/core/wrappers/HtmlElementWrapper.js +38 -12
  367. package/src/core/wrappers/NDElement.js +328 -22
  368. package/src/core/wrappers/NdPrototype.js +60 -16
  369. package/src/core/wrappers/SingletonView.js +50 -2
  370. package/src/core/wrappers/SvgElementWrapper.js +1 -1
  371. package/src/core/wrappers/constants.js +35 -2
  372. package/src/core/wrappers/prototypes/attributes-extensions.js +7 -7
  373. package/src/core/wrappers/prototypes/nd-element-extensions.js +72 -6
  374. package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +42 -2
  375. package/src/core/wrappers/template-cloner/NodeCloner.js +53 -8
  376. package/src/core/wrappers/template-cloner/TemplateCloner.js +75 -6
  377. package/src/core/wrappers/template-cloner/attributes-hydrator.js +58 -2
  378. package/src/core/wrappers/template-cloner/utils.js +42 -6
  379. package/src/fetch/NativeFetch.js +3 -3
  380. package/src/i18n/bin/scan.js +6 -6
  381. package/src/i18n/index.d.ts +2 -0
  382. package/src/i18n/service/I18nService.d.ts +27 -0
  383. package/src/i18n/service/I18nService.js +5 -5
  384. package/src/i18n/service/functions.d.ts +22 -0
  385. package/src/i18n/service/functions.js +2 -2
  386. package/src/router/Route.js +3 -3
  387. package/src/router/RouteGroupHelper.js +2 -2
  388. package/src/router/Router.js +15 -15
  389. package/src/router/RouterComponent.js +33 -7
  390. package/src/router/link.js +4 -4
  391. package/src/router/modes/HashRouter.js +2 -2
  392. package/src/router/modes/HistoryRouter.js +2 -2
  393. package/src/router/modes/MemoryRouter.js +1 -1
  394. package/src/ui/components/accordion/AccordionItemRender.js +3 -3
  395. package/src/ui/components/accordion/AccordionRender.js +1 -1
  396. package/src/ui/components/alert/AlertRender.js +10 -10
  397. package/src/ui/components/avatar/avata-group/AvatarGroupRender.js +1 -1
  398. package/src/ui/components/avatar/avatar/AvatarRender.js +1 -1
  399. package/src/ui/components/breadcrumb/BreadcrumbRender.js +2 -2
  400. package/src/ui/components/button/ButtonRender.js +1 -1
  401. package/src/ui/components/card/CardRender.js +133 -0
  402. package/src/ui/components/card/card.css +169 -0
  403. package/src/ui/components/contextmenu/ContextmenuRender.js +6 -6
  404. package/src/ui/components/dropdown/DropdownRender.js +8 -8
  405. package/src/ui/components/dropdown/group/DropdownGroupRender.js +2 -2
  406. package/src/ui/components/dropdown/item/DropdownItemRender.js +1 -1
  407. package/src/ui/components/form/FieldCollectionRender.js +2 -2
  408. package/src/ui/components/form/FormControlRender.js +5 -5
  409. package/src/ui/components/form/fields/AutocompleteFieldRender.js +3 -3
  410. package/src/ui/components/form/fields/CheckboxFieldRender.js +1 -1
  411. package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +1 -1
  412. package/src/ui/components/form/fields/DateFieldRender.js +7 -7
  413. package/src/ui/components/form/fields/EmailFieldRender.js +1 -1
  414. package/src/ui/components/form/fields/FieldRender.js +4 -4
  415. package/src/ui/components/form/fields/FileFieldRender.js +1 -1
  416. package/src/ui/components/form/fields/PasswordFieldRender.js +2 -2
  417. package/src/ui/components/form/fields/RadioFieldRender.js +1 -1
  418. package/src/ui/components/form/fields/RangeFieldRender.js +1 -1
  419. package/src/ui/components/form/fields/SelectFieldRender.js +2 -2
  420. package/src/ui/components/form/fields/SliderFieldRender.js +6 -6
  421. package/src/ui/components/form/fields/StringFieldRender.js +1 -1
  422. package/src/ui/components/form/fields/TelFieldRender.js +1 -1
  423. package/src/ui/components/form/fields/TextAreaFieldRender.js +1 -1
  424. package/src/ui/components/form/fields/TimeFieldRender.js +3 -3
  425. package/src/ui/components/form/fields/UrlFieldRender.js +1 -1
  426. package/src/ui/components/form/file-upload-mode/FileAvatarModeRender.js +1 -1
  427. package/src/ui/components/form/file-upload-mode/FileDropzoneModeRender.js +2 -2
  428. package/src/ui/components/form/file-upload-mode/FileUploadButtonModeRender.js +2 -2
  429. package/src/ui/components/form/file-upload-mode/FileWallModeRender.js +1 -1
  430. package/src/ui/components/form/helpers.js +8 -8
  431. package/src/ui/components/form/index.js +27 -27
  432. package/src/ui/components/list/ListRender.js +18 -0
  433. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  434. package/src/ui/components/list/divider/list-divider.css +12 -0
  435. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  436. package/src/ui/components/list/group/list-group.css +62 -0
  437. package/src/ui/components/list/item/ListItemRender.js +238 -0
  438. package/src/ui/components/list/item/list-item.css +191 -0
  439. package/src/ui/components/list/list.css +24 -0
  440. package/src/ui/components/menu/MenuDividerRender.js +1 -1
  441. package/src/ui/components/menu/MenuGroupRender.js +3 -3
  442. package/src/ui/components/menu/MenuItemRender.js +2 -2
  443. package/src/ui/components/menu/MenuLinkRender.js +3 -3
  444. package/src/ui/components/menu/helpers.js +4 -4
  445. package/src/ui/components/modal/ModalRender.js +4 -4
  446. package/src/ui/components/pagination/PaginationRender.js +9 -9
  447. package/src/ui/components/popover/PopoverRender.js +7 -7
  448. package/src/ui/components/progress/ProgressRender.js +12 -12
  449. package/src/ui/components/skeleton/SkeletonRender.js +56 -0
  450. package/src/ui/components/spacer/SpacerRender.js +10 -0
  451. package/src/ui/components/splitter/SplitterGutterRender.js +1 -1
  452. package/src/ui/components/splitter/SplitterPanelRender.js +2 -2
  453. package/src/ui/components/stacks/PositionStackRender.js +1 -1
  454. package/src/ui/components/stacks/StackRender.js +1 -1
  455. package/src/ui/components/stacks/absolute-stack/AbsoluteStackRender.js +1 -1
  456. package/src/ui/components/stacks/fixed-stack/FixedStackRender.js +1 -1
  457. package/src/ui/components/stacks/h-stack/HStackRender.js +1 -1
  458. package/src/ui/components/stacks/index.js +5 -5
  459. package/src/ui/components/stacks/relative-stack/RelativeStackRender.js +1 -1
  460. package/src/ui/components/stacks/v-stack/VStackRender.js +1 -1
  461. package/src/ui/components/stepper/StepperRender.js +2 -2
  462. package/src/ui/components/stepper/StepperStepRender.js +4 -4
  463. package/src/ui/components/switch/SwitchRender.js +4 -4
  464. package/src/ui/components/table/data-table/DataTableRender.js +5 -5
  465. package/src/ui/components/table/data-table/bulk-actions.js +7 -7
  466. package/src/ui/components/table/data-table/pagination.js +6 -6
  467. package/src/ui/components/table/data-table/tables.js +25 -25
  468. package/src/ui/components/table/data-table/toolbar.js +3 -3
  469. package/src/ui/components/table/simple-table/SimpleTableRender.js +8 -8
  470. package/src/ui/components/tabs/TabsRender.js +11 -11
  471. package/src/ui/components/toast/ToastRender.js +3 -3
  472. package/src/ui/components/tooltip/TooltipRender.js +1 -1
  473. package/src/ui/index.js +44 -36
  474. package/types/elements.d.ts +163 -1037
  475. package/types/forms.d.ts +16 -20
  476. package/types/globals.d.ts +543 -0
  477. package/types/images.d.ts +2 -2
  478. package/types/observable-resource.d.ts +3 -0
  479. package/types/property-accumulator.d.ts +4 -4
  480. package/types/store.d.ts +26 -2
  481. package/types/validator.ts +3 -3
  482. package/ui.js +1 -0
  483. package/src/components/form/field/DefaultRender.js +0 -77
  484. package/src/components/form/field/FieldFactory.js +0 -107
  485. package/src/components/skeleton/SkeletonList.js +0 -0
  486. package/src/components/skeleton/SkeletonParagraph.js +0 -0
  487. package/src/components/skeleton/SkeletonTable.js +0 -0
  488. /package/{src/components/skeleton/SkeletonCard.js → docs/tutorials/.gitkeep} +0 -0
package/docs/filters.md CHANGED
@@ -1,1216 +1,247 @@
1
- # Filters
2
-
3
- NativeDocument provides a comprehensive set of filter helpers for creating reactive, type-safe data filtering with Observable arrays. These filters work seamlessly with the `where()`, `whereSome()`, and `whereEvery()` methods.
1
+ ---
2
+ title: Filters
3
+ description: Composable, reactive filter helpers for ObservableArray.where() - comparison, string, date, array, and logical operators
4
+ ---
4
5
 
5
- ## Overview
6
+ # Filters
6
7
 
7
- Filter helpers enable:
8
- - **Reactive filtering** - Filters update automatically when observables change
9
- - **Type-safe comparisons** - Validate data types and formats
10
- - **Composable logic** - Combine filters with `and`, `or`, `not`
11
- - **Date/Time handling** - Specialized filters for temporal data
12
- - **Custom filters** - Create your own filter logic
8
+ Filter helpers enable reactive, composable filtering on `ObservableArray` via `.where()`, `.whereSome()`, and `.whereEvery()`. Filters update automatically when observable values change.
13
9
 
14
- ## Import
15
10
  ```javascript
16
- import { filters } from 'native-document/utils';
17
- const { equals, greaterThan, between, includes, and, or, not } = filters;
18
-
19
- // Or destructure directly
20
- import { filters: { equals, greaterThan, between, includes } } from 'native-document/utils';
21
- ```
11
+ import { utils } from 'native-document';
12
+ const { filters } = utils;
22
13
 
23
- ## Basic Filters
24
-
25
- ### Comparison Filters
26
- ```javascript
14
+ // Or
27
15
  import { filters } from 'native-document/utils';
28
- const { equals, notEquals, greaterThan, lessThan } = filters;
29
- import { Observable } from 'native-document';
30
-
31
- const products = Observable.array([
32
- { id: 1, name: 'Phone', price: 599, stock: 10 },
33
- { id: 2, name: 'Laptop', price: 999, stock: 5 },
34
- { id: 3, name: 'Tablet', price: 399, stock: 0 },
35
- { id: 4, name: 'Watch', price: 299, stock: 15 }
36
- ]);
37
16
 
38
- // Filter by exact value
39
- const expensive = products.where({
40
- price: equals(999)
41
- });
42
- // Result: [{ id: 2, name: 'Laptop', price: 999, stock: 5 }]
43
-
44
- // Filter by inequality
45
- const notTablet = products.where({
46
- name: notEquals('Tablet')
47
- });
48
- // Result: All except Tablet
49
-
50
- // Greater than
51
- const expensiveProducts = products.where({
52
- price: greaterThan(500)
53
- });
54
- // Result: Phone and Laptop
55
-
56
- // Less than
57
- const affordable = products.where({
58
- price: lessThan(400)
59
- });
60
- // Result: Watch and Tablet
17
+ // Or named imports
18
+ import { equals, greaterThan, between, match, and, or, not } from 'native-document/filters';
61
19
  ```
62
20
 
63
- ### Shortcuts
64
- ```javascript
65
- import { filters } from 'native-document/utils';
66
- const { eq, neq, gt, gte, lt, lte } = filters;
21
+ > All filters are for **`ObservableArray` only** - see [Observables](./observables.md) for `.where()` usage.
67
22
 
68
- const products = Observable.array([...]);
23
+ ---
69
24
 
70
- // Short aliases
71
- const expensive = products.where({ price: gt(500) });
72
- const affordable = products.where({ price: lte(400) });
73
- const notPhone = products.where({ name: neq('Phone') });
74
- ```
25
+ ## Comparison
75
26
 
76
- ### greaterThanOrEqual() / lessThanOrEqual()
77
27
  ```javascript
78
- import { filters } from 'native-document/utils';
79
- const { greaterThanOrEqual, lessThanOrEqual } = filters;
80
- import { Observable } from 'native-document';
81
-
82
28
  const products = Observable.array([
83
- { name: 'Budget', price: 100 },
84
- { name: 'Standard', price: 500 },
85
- { name: 'Premium', price: 1000 }
29
+ { name: 'Phone', price: 599, stock: 10 },
30
+ { name: 'Laptop', price: 999, stock: 0 },
31
+ { name: 'Tablet', price: 399, stock: 5 },
86
32
  ]);
87
33
 
88
- // Greater than or equal to
89
- const standardOrBetter = products.where({
90
- price: greaterThanOrEqual(500)
91
- });
92
- // Result: Standard, Premium
93
-
94
- // Less than or equal to
95
- const budgetFriendly = products.where({
96
- price: lessThanOrEqual(500)
97
- });
98
- // Result: Budget, Standard
34
+ products.where({ price: equals(599) }) // Phone
35
+ products.where({ name: notEquals('Phone') }) // Laptop, Tablet
36
+ products.where({ price: greaterThan(500) }) // Phone, Laptop
37
+ products.where({ price: greaterThanOrEqual(399) }) // all
38
+ products.where({ price: lessThan(600) }) // Tablet
39
+ products.where({ price: lessThanOrEqual(399) }) // Tablet
40
+ products.where({ price: between(400, 700) }) // Phone
41
+ products.where({ stock: equals(0) }) // Laptop
99
42
  ```
100
43
 
101
- ## Range Filters
44
+ All accept observables as arguments - filters update automatically when they change:
102
45
 
103
- ### between()
104
46
  ```javascript
105
- import { filters } from 'native-document/utils';
106
- const { between } = filters;
107
- import { Observable } from 'native-document';
108
-
109
- const products = Observable.array([
110
- { name: 'Budget Phone', price: 199 },
111
- { name: 'Mid Phone', price: 499 },
112
- { name: 'Premium Phone', price: 999 },
113
- { name: 'Luxury Phone', price: 1499 }
114
- ]);
115
-
116
- // Static range
117
- const midRange = products.where({
118
- price: between(400, 800)
119
- });
120
- // Result: [{ name: 'Mid Phone', price: 499 }]
121
-
122
- // Reactive range with observables
123
- const minPrice = Observable(200);
124
- const maxPrice = Observable(1000);
47
+ const minPrice = Observable(300);
48
+ const maxPrice = Observable(800);
125
49
 
126
50
  const filtered = products.where({
127
51
  price: between(minPrice, maxPrice)
128
52
  });
129
53
 
130
- // Updates automatically when bounds change
131
- minPrice.set(500); // Now shows only Premium Phone
132
- maxPrice.set(600); // Now shows nothing
54
+ minPrice.set(500); // filtered updates automatically
133
55
  ```
134
56
 
135
- ## String Filters
136
-
137
- ### includes() / contains()
138
- ```javascript
139
- import { filters } from 'native-document/utils';
140
- const { includes, contains } = filters;
141
- import { Observable } from 'native-document';
142
-
143
- const products = Observable.array([
144
- { name: 'iPhone 15 Pro' },
145
- { name: 'Samsung Galaxy' },
146
- { name: 'Google Pixel' },
147
- { name: 'OnePlus Phone' }
148
- ]);
57
+ ---
149
58
 
150
- // Case-insensitive by default
151
- const phones = products.where({
152
- name: includes('phone')
153
- });
154
- // Result: iPhone 15 Pro, OnePlus Phone
155
-
156
- // Reactive search
157
- const searchTerm = Observable('galaxy');
158
- const results = products.where({
159
- name: includes(searchTerm)
160
- });
161
- // Result: Samsung Galaxy
162
-
163
- searchTerm.set('pixel');
164
- // Result: Google Pixel
165
-
166
- // contains is an alias
167
- const sameResults = products.where({
168
- name: contains('phone')
169
- });
170
- ```
59
+ ## String
171
60
 
172
- ### startsWith()
173
61
  ```javascript
174
- import { filters } from 'native-document/utils';
175
- const { startsWith } = filters;
176
- import { Observable } from 'native-document';
177
-
178
62
  const users = Observable.array([
179
63
  { name: 'Alice Johnson' },
180
64
  { name: 'Bob Smith' },
181
- { name: 'Alice Brown' },
182
65
  { name: 'Charlie Wilson' }
183
66
  ]);
184
67
 
185
- // Case-insensitive by default
186
- const alices = users.where({
187
- name: startsWith('alice')
188
- });
189
- // Result: Alice Johnson, Alice Brown
190
-
191
- // Case-sensitive (second parameter)
192
- const caseSensitive = users.where({
193
- name: startsWith('Alice', true)
194
- });
195
- // Result: Alice Johnson, Alice Brown
68
+ users.where({ name: includes('alice') }) // Alice Johnson (case-insensitive)
69
+ users.where({ name: includes('smith', true) }) // Bob Smith (case-sensitive)
70
+ users.where({ name: startsWith('A') }) // Alice Johnson
71
+ users.where({ name: endsWith('son') }) // Alice Johnson, Charlie Wilson
72
+ users.where({ name: match(/^[A-C]/) }) // Alice, Bob, Charlie (regex)
73
+ users.where({ name: match('bob', false) }) // Bob Smith (plain text)
196
74
  ```
197
75
 
198
- ### endsWith()
199
- ```javascript
200
- import { filters } from 'native-document/utils';
201
- const { endsWith } = filters;
202
- import { Observable } from 'native-document';
203
-
204
- const files = Observable.array([
205
- { name: 'document.pdf' },
206
- { name: 'image.jpg' },
207
- { name: 'report.pdf' },
208
- { name: 'photo.png' }
209
- ]);
76
+ Reactive search:
210
77
 
211
- // Case-insensitive by default
212
- const pdfs = files.where({
213
- name: endsWith('.pdf')
214
- });
215
- // Result: document.pdf, report.pdf
216
-
217
- // Case-sensitive
218
- const pdfsCaseSensitive = files.where({
219
- name: endsWith('.PDF', true)
220
- });
221
- // Result: [] (none match uppercase)
222
- ```
223
-
224
- ### match()
225
78
  ```javascript
226
- import { filters } from 'native-document/utils';
227
- const { match } = filters;
228
- import { Observable } from 'native-document';
229
-
230
- const products = Observable.array([
231
- { sku: 'ABC-123' },
232
- { sku: 'DEF-456' },
233
- { sku: 'GHI-789' },
234
- { sku: 'INVALID' }
235
- ]);
79
+ const search = Observable('');
80
+ const results = users.where({ name: includes(search) });
236
81
 
237
- // Regex pattern
238
- const validSKUs = products.where({
239
- sku: match(/^[A-Z]{3}-\d{3}$/, true)
240
- });
241
- // Result: ABC-123, DEF-456, GHI-789
242
-
243
- // Simple text match (no regex)
244
- const containsABC = products.where({
245
- sku: match('ABC', false)
246
- });
247
- // Result: ABC-123
248
-
249
- // With flags
250
- const caseInsensitive = products.where({
251
- sku: match(/abc/, true, 'i')
252
- });
253
- // Result: ABC-123
82
+ search.set('ali'); // Alice Johnson
83
+ search.set(''); // all users
254
84
  ```
255
85
 
256
- ## Array Filters
86
+ ---
257
87
 
258
- ### inArray()
259
- ```javascript
260
- import { filters } from 'native-document/utils';
261
- const { inArray } = filters;
262
- import { Observable } from 'native-document';
263
-
264
- const products = Observable.array([
265
- { id: 1, category: 'electronics' },
266
- { id: 2, category: 'books' },
267
- { id: 3, category: 'clothing' },
268
- { id: 4, category: 'electronics' }
269
- ]);
270
-
271
- // Static array
272
- const allowed = products.where({
273
- category: inArray(['electronics', 'books'])
274
- });
275
- // Result: items 1, 2, 4
276
-
277
- // Reactive array
278
- const allowedCategories = Observable.array(['electronics']);
279
- const filtered = products.where({
280
- category: inArray(allowedCategories)
281
- });
282
-
283
- // Updates when array changes
284
- allowedCategories.push('books');
285
- // Now includes books too
286
- ```
88
+ ## Array membership
287
89
 
288
- ### notIn()
289
90
  ```javascript
290
- import { filters } from 'native-document/utils';
291
- const { notIn } = filters;
292
- import { Observable } from 'native-document';
293
-
294
- const users = Observable.array([
295
- { id: 1, status: 'active' },
296
- { id: 2, status: 'banned' },
297
- { id: 3, status: 'inactive' },
298
- { id: 4, status: 'active' }
299
- ]);
91
+ const allowed = Observable.array(['electronics', 'books']);
300
92
 
301
- const validUsers = users.where({
302
- status: notIn(['banned', 'deleted'])
303
- });
304
- // Result: items 1, 3, 4
93
+ products.where({ category: inArray(allowed) }) // reactive
94
+ products.where({ category: inArray(['books']) }) // static
95
+ products.where({ status: notIn(['banned', 'deleted']) })
305
96
  ```
306
97
 
307
- ## Empty/Existence Filters
98
+ ---
308
99
 
309
- ### isEmpty()
310
- ```javascript
311
- import { filters } from 'native-document/utils';
312
- const { isEmpty } = filters;
313
- import { Observable } from 'native-document';
314
-
315
- const tasks = Observable.array([
316
- { title: 'Task 1', description: '' },
317
- { title: 'Task 2', description: 'Details' },
318
- { title: 'Task 3', description: null },
319
- { title: 'Task 4', tags: [] }
320
- ]);
100
+ ## Logical operators
321
101
 
322
- // Empty string or null
323
- const noDescription = tasks.where({
324
- description: isEmpty()
325
- });
326
- // Result: Task 1, Task 3
102
+ `and`, `or`, `not` operate **within a single field**. For cross-field logic use the `_` key:
327
103
 
328
- // Empty arrays
329
- const noTags = tasks.where({
330
- tags: isEmpty()
331
- });
332
- // Result: Task 4
333
-
334
- // Conditional empty
335
- const shouldBeEmpty = Observable(true);
336
- const filtered = tasks.where({
337
- description: isEmpty(shouldBeEmpty)
338
- });
339
-
340
- shouldBeEmpty.set(false);
341
- // Now returns items with non-empty descriptions
342
- ```
343
-
344
- ### isNotEmpty()
345
104
  ```javascript
346
- import { filters } from 'native-document/utils';
347
- const { isNotEmpty } = filters;
348
- import { Observable } from 'native-document';
105
+ // and - field must pass ALL conditions
106
+ products.where({ price: and(greaterThan(100), lessThan(800)) })
349
107
 
350
- const tasks = Observable.array([
351
- { title: 'Task 1', description: '' },
352
- { title: 'Task 2', description: 'Details' },
353
- { title: 'Task 3', description: null }
354
- ]);
108
+ // or - field must pass AT LEAST ONE
109
+ products.where({ category: or(equals('electronics'), equals('books')) })
355
110
 
356
- const withDescription = tasks.where({
357
- description: isNotEmpty()
358
- });
359
- // Result: Task 2
111
+ // not - inverts
112
+ products.where({ status: not(equals('banned')) })
360
113
 
361
- // Conditional
362
- const shouldHaveContent = Observable(true);
363
- const filtered = tasks.where({
364
- description: isNotEmpty(shouldHaveContent)
365
- });
114
+ // cross-field - plain function with _ key
115
+ products.where({
116
+ _: item => item.stock > 0 && item.price < 500
117
+ })
366
118
  ```
367
119
 
368
- ## Date and Time Filters
120
+ `all` is an alias for `and`, `any` is an alias for `or`.
369
121
 
370
- All date and time filters automatically convert values to Date objects using the internal `toDate()` helper. You can pass Date objects, timestamps, or date strings - they will be converted automatically.
371
-
372
- ### Date Comparison
373
- ```javascript
374
- import { filters } from 'native-document/utils';
375
- const { dateEquals, dateBefore, dateAfter, dateBetween } = filters;
376
- import { Observable } from 'native-document';
377
-
378
- const events = Observable.array([
379
- { name: 'Meeting', date: '2024-01-15' },
380
- { name: 'Conference', date: '2024-06-20' },
381
- { name: 'Workshop', date: '2024-09-10' },
382
- { name: 'Seminar', date: '2024-12-05' }
383
- ]);
384
-
385
- // Specific date (string automatically converted to Date)
386
- const januaryEvents = events.where({
387
- date: dateEquals('2024-01-15')
388
- });
389
- // Result: Meeting
390
-
391
- // Or with Date object
392
- const januaryEvents2 = events.where({
393
- date: dateEquals(new Date('2024-01-15'))
394
- });
395
- // Result: Meeting
396
-
397
- // Before a date
398
- const firstHalf = events.where({
399
- date: dateBefore('2024-07-01')
400
- });
401
- // Result: Meeting, Conference
402
-
403
- // After a date
404
- const secondHalf = events.where({
405
- date: dateAfter('2024-07-01')
406
- });
407
- // Result: Workshop, Seminar
408
-
409
- // Date range
410
- const summerEvents = events.where({
411
- date: dateBetween('2024-06-01', '2024-08-31')
412
- });
413
- // Result: Conference
122
+ ---
414
123
 
415
- // Reactive date filtering with observables
416
- const startDate = Observable('2024-01-01');
417
- const endDate = Observable('2024-06-30');
124
+ ## Custom filter
418
125
 
419
- const filtered = events.where({
420
- date: dateBetween(startDate, endDate)
421
- });
422
-
423
- // Updates when dates change
424
- endDate.set('2024-12-31');
425
- // Now includes all events
426
-
427
- // Works with timestamps too
428
- const timestamp = Date.now();
429
- const recentEvents = events.where({
430
- date: dateAfter(timestamp)
431
- });
432
- ```
433
-
434
- ### Time Comparison (Ignores Date)
435
-
436
- Time filters extract and compare only the time portion (hours, minutes, seconds), ignoring the date.
437
126
  ```javascript
438
- import { filters } from 'native-document/utils';
439
- const { timeEquals, timeBefore, timeAfter, timeBetween } = filters;
440
- import { Observable } from 'native-document';
441
-
442
- const appointments = Observable.array([
443
- { name: 'Breakfast', time: '2024-01-15 08:00:00' },
444
- { name: 'Meeting', time: '2024-01-15 14:00:00' },
445
- { name: 'Dinner', time: '2024-01-15 19:00:00' }
446
- ]);
447
-
448
- // Specific time (date is ignored, only time matters)
449
- const lunchTime = appointments.where({
450
- time: timeEquals('2024-01-01 14:00:00')
451
- });
452
- // Result: Meeting (date doesn't need to match)
453
-
454
- // Before a time
455
- const morning = appointments.where({
456
- time: timeBefore('12:00:00')
457
- });
458
- // Result: Breakfast
127
+ const minRating = Observable(4);
459
128
 
460
- // After a time
461
- const evening = appointments.where({
462
- time: timeAfter('18:00:00')
463
- });
464
- // Result: Dinner
465
-
466
- // Time range (9 AM to 5 PM)
467
- const businessHours = appointments.where({
468
- time: timeBetween('09:00:00', '17:00:00')
469
- });
470
- // Result: Meeting
471
-
472
- // Works with any date - only time is compared
473
- const businessHours2 = appointments.where({
474
- time: timeBetween('2025-12-25 09:00:00', '2025-12-25 17:00:00')
475
- });
476
- // Result: Meeting (same result, date is ignored)
129
+ products.where({
130
+ _: custom((item, min) => {
131
+ return item.rating >= min && item.reviews > 10;
132
+ }, minRating) // observables as extra dependencies
133
+ })
477
134
  ```
478
135
 
479
- ### DateTime Comparison (Date + Time)
136
+ ---
480
137
 
481
- DateTime filters compare both date and time together for exact timestamp matching.
482
- ```javascript
483
- import { filters } from 'native-document/utils';
484
- const { dateTimeEquals, dateTimeBefore, dateTimeAfter, dateTimeBetween } = filters;
485
- import { Observable } from 'native-document';
138
+ ## Date and time filters
486
139
 
487
- const logs = Observable.array([
488
- { message: 'Start', timestamp: '2024-01-15 08:30:00' },
489
- { message: 'Process', timestamp: '2024-01-15 14:45:00' },
490
- { message: 'End', timestamp: '2024-01-15 18:20:00' }
491
- ]);
140
+ All date/time values are automatically converted to `Date` objects.
492
141
 
493
- // Exact timestamp (both date and time must match)
494
- const exactLog = logs.where({
495
- timestamp: dateTimeEquals('2024-01-15 14:45:00')
496
- });
497
- // Result: Process
498
-
499
- // Before timestamp
500
- const earlyLogs = logs.where({
501
- timestamp: dateTimeBefore('2024-01-15 15:00:00')
502
- });
503
- // Result: Start, Process
504
-
505
- // After timestamp
506
- const lateLogs = logs.where({
507
- timestamp: dateTimeAfter('2024-01-15 15:00:00')
508
- });
509
- // Result: End
510
-
511
- // Timestamp range (work hours: 9 AM to 5 PM)
512
- const workHours = logs.where({
513
- timestamp: dateTimeBetween(
514
- '2024-01-15 09:00:00',
515
- '2024-01-15 17:00:00'
516
- )
517
- });
518
- // Result: Process
519
-
520
- // Reactive datetime filtering
521
- const startTime = Observable('2024-01-15 08:00:00');
522
- const endTime = Observable('2024-01-15 16:00:00');
523
-
524
- const filtered = logs.where({
525
- timestamp: dateTimeBetween(startTime, endTime)
526
- });
527
-
528
- // Updates when times change
529
- endTime.set('2024-01-15 20:00:00');
530
- // Now includes all logs
531
- ```
532
-
533
- ### Date Format Examples
534
-
535
- All date/time filters accept multiple formats:
536
- ```javascript
537
- import { filters } from 'native-document/utils';
538
- const { dateEquals, timeEquals, dateTimeEquals } = filters;
539
-
540
- const events = Observable.array([...]);
541
-
542
- // ISO 8601 string
543
- events.where({ date: dateEquals('2024-01-15') });
544
-
545
- // Date object
546
- events.where({ date: dateEquals(new Date('2024-01-15')) });
547
-
548
- // Timestamp (milliseconds)
549
- events.where({ date: dateEquals(1705276800000) });
550
-
551
- // Full datetime string
552
- events.where({ timestamp: dateTimeEquals('2024-01-15T14:30:00') });
553
-
554
- // Time only (date portion ignored)
555
- events.where({ time: timeEquals('14:30:00') });
556
-
557
- // Observable with any format
558
- const targetDate = Observable('2024-01-15');
559
- events.where({ date: dateEquals(targetDate) });
560
- ```
142
+ ### Date (ignores time)
561
143
 
562
- ### Working with Different Timezones
563
144
  ```javascript
564
- import { filters } from 'native-document/utils';
565
- const { dateTimeBetween } = filters;
566
- import { Observable } from 'native-document';
145
+ import { dateEquals, dateBefore, dateAfter, dateBetween } from 'native-document/filters';
567
146
 
568
- const events = Observable.array([
569
- { name: 'Meeting', time: '2024-01-15T14:00:00Z' }, // UTC
570
- { name: 'Call', time: '2024-01-15T09:00:00-05:00' }, // EST
571
- { name: 'Workshop', time: '2024-01-15T16:00:00+01:00' } // CET
572
- ]);
147
+ const today = Observable(new Date());
573
148
 
574
- // All dates are converted to Date objects internally
575
- // Local timezone is used for comparison
576
- const todayEvents = events.where({
577
- time: dateTimeBetween(
578
- '2024-01-15T00:00:00',
579
- '2024-01-15T23:59:59'
580
- )
581
- });
149
+ events.where({ date: dateEquals('2024-03-15') })
150
+ events.where({ date: dateAfter(new Date()) })
151
+ events.where({ date: dateBefore(today) })
152
+ events.where({ date: dateBetween('2024-06-01', '2024-08-31') })
582
153
  ```
583
154
 
584
- ### Practical Date/Time Examples
155
+ ### Time (ignores date)
585
156
 
586
- #### Filter Events by Today/This Week
587
157
  ```javascript
588
- import { filters } from 'native-document/utils';
589
- const { dateEquals, dateBetween } = filters;
590
- import { Observable } from 'native-document';
591
-
592
- const events = Observable.array([...]);
158
+ import { timeBefore, timeAfter, timeBetween } from 'native-document/filters';
593
159
 
594
- // Today's events
595
- const today = new Date().toISOString().split('T')[0]; // "2024-01-15"
596
- const todayEvents = events.where({
597
- date: dateEquals(today)
598
- });
599
-
600
- // This week's events
601
- const startOfWeek = new Date();
602
- startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay());
603
-
604
- const endOfWeek = new Date(startOfWeek);
605
- endOfWeek.setDate(endOfWeek.getDate() + 6);
606
-
607
- const thisWeekEvents = events.where({
608
- date: dateBetween(
609
- startOfWeek.toISOString().split('T')[0],
610
- endOfWeek.toISOString().split('T')[0]
611
- )
612
- });
160
+ // Only the time portion is compared - date is ignored
161
+ calls.where({ time: timeBetween('09:00:00', '17:00:00') }) // business hours
162
+ calls.where({ time: timeAfter('18:00:00') }) // evening
613
163
  ```
614
164
 
615
- #### Filter by Business Hours
616
- ```javascript
617
- import { filters } from 'native-document/utils';
618
- const { timeBetween } = filters;
619
- import { Observable } from 'native-document';
620
-
621
- const calls = Observable.array([
622
- { caller: 'Alice', time: '2024-01-15 08:30:00' },
623
- { caller: 'Bob', time: '2024-01-15 14:30:00' },
624
- { caller: 'Charlie', time: '2024-01-15 20:00:00' }
625
- ]);
165
+ ### DateTime (date + time)
626
166
 
627
- // Business hours: 9 AM - 6 PM
628
- const businessHoursCalls = calls.where({
629
- time: timeBetween('09:00:00', '18:00:00')
630
- });
631
- // Result: Bob (14:30 is within business hours)
632
- ```
633
-
634
- #### Filter Recent Activity
635
167
  ```javascript
636
- import { filters } from 'native-document/utils';
637
- const { dateTimeAfter } = filters;
638
- import { Observable } from 'native-document';
639
-
640
- const activities = Observable.array([...]);
641
-
642
- // Last 24 hours
643
- const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
644
- const recentActivity = activities.where({
645
- timestamp: dateTimeAfter(oneDayAgo)
646
- });
168
+ import { dateTimeAfter, dateTimeBetween } from 'native-document/filters';
647
169
 
648
- // Last hour
649
- const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
650
- const veryRecentActivity = activities.where({
651
- timestamp: dateTimeAfter(oneHourAgo)
652
- });
653
- ```
654
-
655
- ### Time Comparison (Ignores Date)
656
- ```javascript
657
- import { filters } from 'native-document/utils';
658
- const { timeEquals, timeBefore, timeAfter, timeBetween } = filters;
659
- import { Observable } from 'native-document';
660
-
661
- const appointments = Observable.array([
662
- { name: 'Breakfast', time: new Date('2024-01-15 08:00:00') },
663
- { name: 'Meeting', time: new Date('2024-01-15 14:00:00') },
664
- { name: 'Dinner', time: new Date('2024-01-15 19:00:00') }
665
- ]);
666
-
667
- // Specific time (ignores date)
668
- const lunchTime = appointments.where({
669
- time: timeEquals(new Date('2024-01-01 14:00:00'))
670
- });
671
- // Result: Meeting (even though date is different)
672
-
673
- // Before a time
674
- const morning = appointments.where({
675
- time: timeBefore(new Date('2024-01-01 12:00:00'))
676
- });
677
- // Result: Breakfast
678
-
679
- // After a time
680
- const evening = appointments.where({
681
- time: timeAfter(new Date('2024-01-01 18:00:00'))
682
- });
683
- // Result: Dinner
684
-
685
- // Time range
686
- const businessHours = appointments.where({
687
- time: timeBetween(
688
- new Date('2024-01-01 09:00:00'),
689
- new Date('2024-01-01 17:00:00')
690
- )
691
- });
692
- // Result: Meeting
693
- ```
694
-
695
- ### DateTime Comparison (Date + Time)
696
- ```javascript
697
- import { filters } from 'native-document/utils';
698
- const { dateTimeEquals, dateTimeBefore, dateTimeAfter, dateTimeBetween } = filters;
699
- import { Observable } from 'native-document';
700
-
701
- const logs = Observable.array([
702
- { message: 'Start', timestamp: new Date('2024-01-15 08:30:00') },
703
- { message: 'Process', timestamp: new Date('2024-01-15 14:45:00') },
704
- { message: 'End', timestamp: new Date('2024-01-15 18:20:00') }
705
- ]);
706
-
707
- // Exact timestamp
708
- const exactLog = logs.where({
709
- timestamp: dateTimeEquals(new Date('2024-01-15 14:45:00'))
710
- });
711
- // Result: Process
712
-
713
- // Before timestamp
714
- const earlyLogs = logs.where({
715
- timestamp: dateTimeBefore(new Date('2024-01-15 15:00:00'))
716
- });
717
- // Result: Start, Process
718
-
719
- // After timestamp
720
- const lateLogs = logs.where({
721
- timestamp: dateTimeAfter(new Date('2024-01-15 15:00:00'))
722
- });
723
- // Result: End
724
-
725
- // Timestamp range
726
- const workHours = logs.where({
727
- timestamp: dateTimeBetween(
728
- new Date('2024-01-15 09:00:00'),
729
- new Date('2024-01-15 17:00:00')
730
- )
731
- });
732
- // Result: Process
733
- ```
734
-
735
- ## Logical Operators
736
-
737
- ### and() / all()
738
- ```javascript
739
- import { filters } from 'native-document/utils';
740
- const { and, all, greaterThan, lessThan } = filters;
741
- import { Observable } from 'native-document';
742
-
743
- const products = Observable.array([
744
- { name: 'Phone', price: 599, stock: 10 },
745
- { name: 'Laptop', price: 999, stock: 5 },
746
- { name: 'Tablet', price: 399, stock: 0 },
747
- { name: 'Watch', price: 299, stock: 15 }
748
- ]);
749
-
750
- // Combine multiple conditions
751
- const midRangeInStock = products.where({
752
- price: and(
753
- greaterThan(300),
754
- lessThan(700)
755
- ),
756
- stock: greaterThan(0)
757
- });
758
- // Result: Phone
759
-
760
- // 'all' is an alias for 'and'
761
- const sameResult = products.where({
762
- price: all(
763
- greaterThan(300),
764
- lessThan(700)
765
- )
766
- });
767
- ```
768
-
769
- ### or() / any()
770
- ```javascript
771
- import { filters } from 'native-document/utils';
772
- const { or, any, lessThan, greaterThan } = filters;
773
- import { Observable } from 'native-document';
774
-
775
- const products = Observable.array([
776
- { name: 'Budget Phone', price: 199 },
777
- { name: 'Mid Phone', price: 499 },
778
- { name: 'Premium Phone', price: 999 }
779
- ]);
780
-
781
- // Either cheap OR expensive
782
- const dealsOrPremium = products.where({
783
- price: or(
784
- lessThan(300),
785
- greaterThan(800)
786
- )
787
- });
788
- // Result: Budget Phone, Premium Phone
789
-
790
- // 'any' is an alias for 'or'
791
- const sameResult = products.where({
792
- price: any(
793
- lessThan(300),
794
- greaterThan(800)
795
- )
796
- });
797
- ```
798
-
799
- ### not()
800
- ```javascript
801
- import { filters } from 'native-document/utils';
802
- const { not, equals } = filters;
803
- import { Observable } from 'native-document';
804
-
805
- const users = Observable.array([
806
- { name: 'Alice', status: 'active' },
807
- { name: 'Bob', status: 'inactive' },
808
- { name: 'Charlie', status: 'active' }
809
- ]);
810
-
811
- // Invert condition
812
- const notActive = users.where({
813
- status: not(equals('active'))
814
- });
815
- // Result: Bob
816
-
817
- // Can combine with other filters
818
- const notActiveOrBanned = users.where({
819
- status: not(inArray(['active', 'banned']))
820
- });
821
- ```
822
-
823
- ## Complex Filtering
824
-
825
- ### Nested Conditions
826
- ```javascript
827
- import { filters } from 'native-document/utils';
828
- const { and, or, greaterThan, lessThan, equals } = filters;
829
- import { Observable } from 'native-document';
830
-
831
- const products = Observable.array([
832
- { name: 'Phone', price: 599, category: 'electronics', stock: 10 },
833
- { name: 'Book', price: 29, category: 'books', stock: 50 },
834
- { name: 'Laptop', price: 999, category: 'electronics', stock: 5 },
835
- { name: 'Magazine', price: 9, category: 'books', stock: 100 }
836
- ]);
837
-
838
- // (electronics AND expensive) OR (books AND cheap)
839
- const filtered = products.where({
840
- _: or(
841
- and(
842
- (item) => item.category === 'electronics',
843
- (item) => item.price > 500
844
- ),
845
- and(
846
- (item) => item.category === 'books',
847
- (item) => item.price < 20
848
- )
849
- )
850
- });
851
- // Result: Phone, Laptop, Magazine
852
- ```
853
-
854
- ### Multiple Property Filters
855
- ```javascript
856
- import { filters } from 'native-document/utils';
857
- const { greaterThan, includes, equals } = filters;
858
- import { Observable } from 'native-document';
859
-
860
- const products = Observable.array([
861
- { name: 'Gaming Phone', price: 799, category: 'electronics', tags: ['gaming', 'mobile'] },
862
- { name: 'Office Laptop', price: 1299, category: 'electronics', tags: ['work', 'productivity'] },
863
- { name: 'Budget Tablet', price: 299, category: 'electronics', tags: ['entertainment'] }
864
- ]);
865
-
866
- // Filter on multiple properties
867
- const filtered = products.where({
868
- price: greaterThan(500),
869
- category: equals('electronics'),
870
- name: includes('gaming')
871
- });
872
- // Result: Gaming Phone
873
- ```
874
-
875
- ### Reactive Multi-Condition Filters
876
- ```javascript
877
- import { filters } from 'native-document/utils';
878
- const { and, greaterThan, lessThan, includes, custom } = filters;
879
- import { Observable } from 'native-document';
880
-
881
- const products = Observable.array([...]);
882
-
883
- // Reactive filter values
884
- const searchTerm = Observable('');
885
- const minPrice = Observable(0);
886
- const maxPrice = Observable(10000);
887
- const showInStockOnly = Observable(false);
888
-
889
- const filtered = products.where({
890
- name: includes(searchTerm),
891
- price: and(
892
- greaterThan(minPrice),
893
- lessThan(maxPrice)
894
- ),
895
- stock: custom((value, showInStock) => {
896
- return !showInStock || value > 0;
897
- }, showInStockOnly)
898
- });
899
-
900
- // Updates automatically when any filter changes
901
- searchTerm.set('phone');
902
- minPrice.set(500);
903
- maxPrice.set(1000);
904
- showInStockOnly.set(true);
170
+ logs.where({ timestamp: dateTimeAfter(new Date(Date.now() - 86400000)) }) // last 24h
171
+ logs.where({ timestamp: dateTimeBetween(start, end) })
905
172
  ```
906
173
 
907
- ## Custom Filters
908
-
909
- ### createFilter()
910
- ```javascript
911
- import { filters } from 'native-document/utils';
912
- const { createFilter } = filters;
913
- import { Observable } from 'native-document';
914
-
915
- // Create email validator
916
- const isValidEmail = createFilter(
917
- true, // static value or observable
918
- (value, shouldBeValid) => {
919
- const isValid = /\S+@\S+\.\S+/.test(value);
920
- return shouldBeValid ? isValid : !isValid;
921
- }
922
- );
923
-
924
- const users = Observable.array([
925
- { email: 'alice@example.com' },
926
- { email: 'invalid-email' },
927
- { email: 'bob@example.com' }
928
- ]);
929
-
930
- const validUsers = users.where({
931
- email: isValidEmail
932
- });
933
- // Result: alice@example.com, bob@example.com
934
-
935
- // Reactive validation
936
- const shouldValidate = Observable(true);
937
- const emailFilter = createFilter(
938
- shouldValidate,
939
- (value, validate) => {
940
- if (!validate) return true; // Skip validation
941
- return /\S+@\S+\.\S+/.test(value);
942
- }
943
- );
944
-
945
- const filtered = users.where({
946
- email: emailFilter
947
- });
948
-
949
- shouldValidate.set(false);
950
- // Now returns all users (validation disabled)
951
- ```
952
-
953
- ### createMultiSourceFilter()
954
- ```javascript
955
- import { filters } from 'native-document/utils';
956
- const { createMultiSourceFilter } = filters;
957
- import { Observable } from 'native-document';
958
-
959
- const minValue = Observable(0);
960
- const maxValue = Observable(100);
961
- const multiplier = Observable(1);
962
-
963
- // Filter using multiple observables
964
- const complexFilter = createMultiSourceFilter(
965
- [minValue, maxValue, multiplier],
966
- (value, [min, max, mult]) => {
967
- const adjusted = value * mult;
968
- return adjusted >= min && adjusted <= max;
969
- }
970
- );
971
-
972
- const numbers = Observable.array([
973
- { value: 10 },
974
- { value: 50 },
975
- { value: 150 }
976
- ]);
977
-
978
- const filtered = numbers.where({
979
- value: complexFilter
980
- });
981
-
982
- // All observables update the filter
983
- multiplier.set(2); // Now filters based on value * 2
984
- minValue.set(50); // Now requires value * 2 >= 50
985
- ```
986
-
987
- ### custom()
988
- ```javascript
989
- import { filters } from 'native-document/utils';
990
- const { custom } = filters;
991
- import { Observable } from 'native-document';
992
-
993
- const products = Observable.array([
994
- { name: 'Phone', price: 599, discount: 0.1 },
995
- { name: 'Laptop', price: 999, discount: 0.15 },
996
- { name: 'Tablet', price: 399, discount: 0.05 }
997
- ]);
998
-
999
- const maxBudget = Observable(600);
1000
-
1001
- // Custom filter with observable dependency
1002
- const withinBudget = products.where({
1003
- _: custom((product, budget) => {
1004
- const finalPrice = product.price * (1 - product.discount);
1005
- return finalPrice <= budget;
1006
- }, maxBudget)
1007
- });
1008
- // Result: Phone, Tablet
1009
-
1010
- // Updates when budget changes
1011
- maxBudget.set(400);
1012
- // Result: Tablet only
1013
- ```
174
+ ---
1014
175
 
1015
176
  ## Filter Reference
1016
177
 
1017
- ### Comparison Filters
1018
-
1019
- | Filter | Alias | Description | Example |
1020
- |--------|-------|-------------|---------|
1021
- | `equals(value)` | `eq(value)` | Exact match | `equals(10)` |
1022
- | `notEquals(value)` | `neq(value)` | Not equal | `notEquals('test')` |
1023
- | `greaterThan(value)` | `gt(value)` | Greater than | `gt(100)` |
1024
- | `greaterThanOrEqual(value)` | `gte(value)` | Greater or equal | `gte(50)` |
1025
- | `lessThan(value)` | `lt(value)` | Less than | `lt(1000)` |
1026
- | `lessThanOrEqual(value)` | `lte(value)` | Less or equal | `lte(500)` |
1027
-
1028
- ### Range Filters
1029
-
1030
- | Filter | Description | Example |
1031
- |--------|-------------|---------|
1032
- | `between(min, max)` | Value within range (inclusive) | `between(10, 100)` |
1033
-
1034
- ### String Filters
1035
-
1036
- | Filter | Description | Example |
1037
- |--------|-------------|---------|
1038
- | `includes(text, caseSensitive?)` | Contains substring | `includes('hello')` |
1039
- | `contains(text, caseSensitive?)` | Alias for includes | `contains('world')` |
1040
- | `startsWith(text, caseSensitive?)` | Starts with prefix | `startsWith('Mr')` |
1041
- | `endsWith(text, caseSensitive?)` | Ends with suffix | `endsWith('.pdf')` |
1042
- | `match(pattern, asRegex?, flags?)` | Pattern matching | `match(/\d+/, true)` |
1043
-
1044
- ### Array Filters
1045
-
1046
- | Filter | Description | Example |
1047
- |--------|-------------|---------|
1048
- | `inArray(array)` | Value in array | `inArray(['a', 'b'])` |
1049
- | `notIn(array)` | Value not in array | `notIn(['banned'])` |
1050
-
1051
- ### Empty Filters
1052
-
1053
- | Filter | Description | Example |
1054
- |--------|-------------|---------|
1055
- | `isEmpty(shouldBeEmpty?)` | Value is empty/null | `isEmpty()` |
1056
- | `isNotEmpty(shouldBeNotEmpty?)` | Value is not empty | `isNotEmpty()` |
1057
-
1058
- ### Date Filters
1059
-
1060
- | Filter | Description | Example |
1061
- |--------|-------------|---------|
1062
- | `dateEquals(date)` | Same date (ignores time) | `dateEquals(new Date())` |
1063
- | `dateBefore(date)` | Before date | `dateBefore(new Date())` |
1064
- | `dateAfter(date)` | After date | `dateAfter(new Date())` |
1065
- | `dateBetween(start, end)` | Date range | `dateBetween(start, end)` |
1066
-
1067
- ### Time Filters
1068
-
1069
- | Filter | Description | Example |
1070
- |--------|-------------|---------|
1071
- | `timeEquals(time)` | Same time (ignores date) | `timeEquals(new Date())` |
1072
- | `timeBefore(time)` | Before time | `timeBefore(new Date())` |
1073
- | `timeAfter(time)` | After time | `timeAfter(new Date())` |
1074
- | `timeBetween(start, end)` | Time range | `timeBetween(start, end)` |
1075
-
1076
- ### DateTime Filters
1077
-
1078
- | Filter | Description | Example |
1079
- |--------|-------------|---------|
1080
- | `dateTimeEquals(datetime)` | Exact timestamp | `dateTimeEquals(new Date())` |
1081
- | `dateTimeBefore(datetime)` | Before timestamp | `dateTimeBefore(new Date())` |
1082
- | `dateTimeAfter(datetime)` | After timestamp | `dateTimeAfter(new Date())` |
1083
- | `dateTimeBetween(start, end)` | Timestamp range | `dateTimeBetween(start, end)` |
1084
-
1085
- ### Logical Operators
1086
-
1087
- | Filter | Alias | Description | Example |
1088
- |--------|-------|-------------|---------|
1089
- | `and(...filters)` | `all(...filters)` | All conditions must match | `and(gt(10), lt(100))` |
1090
- | `or(...filters)` | `any(...filters)` | Any condition must match | `or(eq('a'), eq('b'))` |
1091
- | `not(filter)` | - | Invert condition | `not(equals('test'))` |
1092
-
1093
- ### Custom Filters
1094
-
1095
- | Filter | Description | Example |
1096
- |--------|-------------|---------|
1097
- | `createFilter(value, callback)` | Single source custom filter | See above |
1098
- | `createMultiSourceFilter(sources, callback)` | Multi-source custom filter | See above |
1099
- | `custom(callback, ...observables)` | Custom logic with dependencies | See above |
178
+ ### Comparison
179
+ | Filter | Alias | Description |
180
+ |---|---|---|
181
+ | `equals(value)` | `eq` | Exact match |
182
+ | `notEquals(value)` | `neq` | Not equal |
183
+ | `greaterThan(value)` | `gt` | > value |
184
+ | `greaterThanOrEqual(value)` | `gte` | >= value |
185
+ | `lessThan(value)` | `lt` | < value |
186
+ | `lessThanOrEqual(value)` | `lte` | <= value |
187
+ | `between(min, max)` | - | min <= value <= max |
188
+
189
+ ### String
190
+ | Filter | Description |
191
+ |---|---|
192
+ | `includes(text, caseSensitive?)` | Contains substring |
193
+ | `startsWith(text, caseSensitive?)` | Starts with |
194
+ | `endsWith(text, caseSensitive?)` | Ends with |
195
+ | `match(pattern, asRegex?, flags?)` | Regex or plain text match |
196
+
197
+ ### Array
198
+ | Filter | Description |
199
+ |---|---|
200
+ | `inArray(array)` | Value is in array (observable-aware) |
201
+ | `notIn(array)` | Value is not in array |
202
+
203
+ ### Logical
204
+ | Filter | Alias | Description |
205
+ |---|---|---|
206
+ | `and(...filters)` | `all` | All conditions pass |
207
+ | `or(...filters)` | `any` | At least one passes |
208
+ | `not(filter)` | - | Inverts the filter |
209
+
210
+ ### Custom
211
+ | Filter | Description |
212
+ |---|---|
213
+ | `custom(fn, ...observables)` | Custom logic with reactive dependencies |
214
+
215
+ ### Date
216
+ | Filter | Description |
217
+ |---|---|
218
+ | `dateEquals(date)` | Same date (ignores time) |
219
+ | `dateBefore(date)` | Before date |
220
+ | `dateAfter(date)` | After date |
221
+ | `dateBetween(start, end)` | Date range |
222
+ | `timeEquals(time)` | Same time (ignores date) |
223
+ | `timeBefore(time)` | Before time |
224
+ | `timeAfter(time)` | After time |
225
+ | `timeBetween(start, end)` | Time range |
226
+ | `dateTimeEquals(dt)` | Exact timestamp |
227
+ | `dateTimeBefore(dt)` | Before timestamp |
228
+ | `dateTimeAfter(dt)` | After timestamp |
229
+ | `dateTimeBetween(start, end)` | Timestamp range |
230
+
231
+ ---
1100
232
 
1101
233
  ## Best Practices
1102
234
 
1103
- ### 1. Use Specific Filters
1104
- ```javascript
1105
- import { filters } from 'native-document/utils';
1106
- const { equals, greaterThan } = filters;
1107
-
1108
- // ✅ Good: Specific property filters
1109
- const filtered = products.where({
1110
- price: greaterThan(100),
1111
- category: equals('electronics')
1112
- });
1113
-
1114
- // ❌ Less efficient: Generic filter
1115
- const filtered = products.where({
1116
- _: (product) => product.price > 100 && product.category === 'electronics'
1117
- });
1118
- ```
1119
-
1120
- ### 2. Reuse Observable Filters
1121
- ```javascript
1122
- import { filters } from 'native-document/utils';
1123
- const { between } = filters;
1124
- import { Observable } from 'native-document';
1125
-
1126
- // ✅ Good: Reuse observables
1127
- const minPrice = Observable(0);
1128
- const maxPrice = Observable(1000);
1129
-
1130
- const products1Filtered = products1.where({ price: between(minPrice, maxPrice) });
1131
- const products2Filtered = products2.where({ price: between(minPrice, maxPrice) });
1132
-
1133
- // Both update when observables change
1134
- minPrice.set(500);
1135
- ```
1136
-
1137
- ### 3. Combine Related Filters
1138
- ```javascript
1139
- import { filters } from 'native-document/utils';
1140
- const { and, greaterThan, lessThan } = filters;
1141
-
1142
- // ✅ Good: Use 'and' for multiple conditions
1143
- const filtered = products.where({
1144
- price: and(greaterThan(100), lessThan(500))
1145
- });
1146
-
1147
- // ❌ Bad: Multiple where() calls
1148
- const filtered = products
1149
- .where({ price: greaterThan(100) })
1150
- .where({ price: lessThan(500) });
1151
- ```
1152
-
1153
- ### 4. Document Complex Filters
1154
- ```javascript
1155
- import { filters } from 'native-document/utils';
1156
- const { and, or, greaterThan, equals } = filters;
1157
-
1158
- /**
1159
- * Filters products for flash sale eligibility:
1160
- * - In stock OR coming soon
1161
- * - Price between $50-$500
1162
- * - High rating (4+ stars)
1163
- */
1164
- const flashSaleProducts = products.where({
1165
- stock: or(greaterThan(0), equals('coming-soon')),
1166
- price: and(greaterThan(50), lessThan(500)),
1167
- rating: greaterThan(4)
1168
- });
1169
- ```
1170
-
1171
- ### 5. Avoid Over-Filtering
1172
- ```javascript
1173
- import { filters } from 'native-document/utils';
1174
- const { equals, greaterThan } = filters;
1175
-
1176
- // ❌ Bad: Too many where() calls
1177
- const filtered = products
1178
- .where({ category: equals('electronics') })
1179
- .where({ price: greaterThan(100) })
1180
- .where({ stock: greaterThan(0) })
1181
- .where({ rating: greaterThan(4) });
1182
-
1183
- // ✅ Good: Combine into single where
1184
- const filtered = products.where({
1185
- category: equals('electronics'),
1186
- price: greaterThan(100),
1187
- stock: greaterThan(0),
1188
- rating: greaterThan(4)
1189
- });
1190
- ```
1191
-
1192
- ## Next Steps
235
+ 1. Prefer property-specific filters over `_` + plain function when possible - they are more efficient
236
+ 2. Combine multiple field conditions in a single `.where()` call - not chained `.where()` calls
237
+ 3. Reuse observable filter values across multiple arrays - they all update together
238
+ 4. Use `custom()` for complex logic that involves external observables
1193
239
 
1194
- Explore related utilities and concepts:
240
+ ---
1195
241
 
1196
242
  ## Next Steps
1197
243
 
1198
- - **[Getting Started](getting-started.md)** - Installation and first steps
1199
- - **[Core Concepts](core-concepts.md)** - Understanding the fundamentals
1200
- - **[Observables](observables.md)** - Reactive state management
1201
- - **[Elements](elements.md)** - Creating and composing UI
1202
- - **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
1203
- - **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
1204
- - **[Routing](routing.md)** - Navigation and URL management
1205
- - **[State Management](state-management.md)** - Global state patterns
1206
- - **[NDElement](native-document-element.md)** - Native Document Element
1207
- - **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
1208
- - **[Advanced Components](advanced-components.md)** - Template caching and singleton views
1209
- - **[Args Validation](validation.md)** - Function Argument Validation
1210
- - **[Memory Management](memory-management.md)** - Memory management
1211
-
1212
- ## Utilities
1213
-
1214
- - **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
1215
- - **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
1216
- - **[Filters](docs/utils/filters.md)** - Data filtering helpers
244
+ - **[Observables](./observables.md)** - Full `.where()`, `.whereSome()`, `.whereEvery()` reference
245
+ - **[List Rendering](./list-rendering.md)** - ForEach with filtered arrays
246
+ - **[Cache](./cache.md)** - Lazy initialization and singleton patterns
247
+ - **[NativeFetch](./native-fetch.md)** - HTTP client with interceptors