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.
- package/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.d.ts +2 -0
- package/components.js +2 -1
- package/devtools/widget.js +1 -1
- package/dist/native-document.components.min.js +11589 -2983
- package/dist/native-document.dev.js +2280 -396
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/eslint.config.js +28 -33
- package/i18n.js +1 -1
- package/i18n.ts +2 -0
- package/index.js +3 -0
- package/package.json +36 -14
- package/readme.md +269 -89
- package/src/components/$traits/has-draggable/HasDraggable.d.ts +4 -0
- package/src/components/$traits/has-draggable/HasDraggable.js +13 -0
- package/src/components/$traits/has-items/HasItems.d.ts +9 -0
- package/src/components/$traits/has-items/HasItems.js +6 -6
- package/src/components/$traits/has-position/HasFullPosition.d.ts +14 -0
- package/src/components/$traits/has-position/HasFullPosition.js +44 -0
- package/src/components/$traits/has-position/HasPosition.d.ts +7 -0
- package/src/components/$traits/has-position/HasPosition.js +23 -1
- package/src/components/$traits/has-resizable/HasResizable.d.ts +13 -0
- package/src/components/$traits/has-resizable/HasResizable.js +9 -0
- package/src/components/$traits/has-validation/HasValidation.d.ts +17 -0
- package/src/components/$traits/has-validation/HasValidation.js +54 -7
- package/src/components/BaseComponent.d.ts +32 -0
- package/src/components/BaseComponent.js +65 -9
- package/src/components/accordion/Accordion.js +39 -14
- package/src/components/accordion/AccordionItem.js +45 -14
- package/src/components/accordion/index.js +2 -2
- package/src/components/accordion/types/Accordion.d.ts +47 -0
- package/src/components/accordion/types/AccordionItem.d.ts +48 -0
- package/src/components/alert/Alert.js +70 -38
- package/src/components/alert/index.js +2 -2
- package/src/components/alert/types/Alert.d.ts +62 -0
- package/src/components/avatar/Avatar.js +49 -12
- package/src/components/avatar/AvatarGroup.js +50 -2
- package/src/components/avatar/index.js +2 -2
- package/src/components/avatar/types/Avatar.d.ts +74 -0
- package/src/components/avatar/types/AvatarGroup.d.ts +32 -0
- package/src/components/badge/Badge.js +125 -5
- package/src/components/badge/index.js +2 -2
- package/src/components/badge/types/Badge.d.ts +51 -0
- package/src/components/breadcrumb/BreadCrumb.js +61 -5
- package/src/components/breadcrumb/index.js +2 -2
- package/src/components/breadcrumb/types/BreadCrumb.d.ts +42 -0
- package/src/components/button/Button.js +164 -9
- package/src/components/button/index.js +1 -1
- package/src/components/button/types/Button.d.ts +62 -0
- package/src/components/card/Card.js +204 -32
- package/src/components/card/index.js +4 -4
- package/src/components/card/types/Card.d.ts +42 -0
- package/src/components/context-menu/ContextMenu.js +49 -5
- package/src/components/context-menu/ContextMenuGroup.js +15 -2
- package/src/components/context-menu/ContextMenuItem.js +14 -2
- package/src/components/context-menu/index.js +5 -5
- package/src/components/context-menu/types/ContextMenu.d.ts +30 -0
- package/src/components/context-menu/types/ContextMenuGroup.d.ts +18 -0
- package/src/components/context-menu/types/ContextMenuItem.d.ts +18 -0
- package/src/components/divider/Divider.js +120 -4
- package/src/components/divider/index.js +3 -3
- package/src/components/divider/types/Divider.d.ts +55 -0
- package/src/components/dropdown/Dropdown.js +239 -16
- package/src/components/dropdown/DropdownDivider.js +22 -2
- package/src/components/dropdown/DropdownGroup.js +44 -5
- package/src/components/dropdown/DropdownItem.js +76 -3
- package/src/components/dropdown/DropdownTrigger.js +49 -20
- package/src/components/dropdown/helpers.js +1 -1
- package/src/components/dropdown/index.js +6 -6
- package/src/components/dropdown/types/Dropdown.d.ts +88 -0
- package/src/components/dropdown/types/DropdownDivider.d.ts +20 -0
- package/src/components/dropdown/types/DropdownGroup.d.ts +25 -0
- package/src/components/dropdown/types/DropdownItem.d.ts +41 -0
- package/src/components/dropdown/types/DropdownTrigger.d.ts +32 -0
- package/src/components/form/FormControl.js +156 -13
- package/src/components/form/field/Field.js +172 -9
- package/src/components/form/field/FieldCollection.js +116 -12
- package/src/components/form/field/types/AutocompleteField.js +92 -2
- package/src/components/form/field/types/CheckboxField.js +43 -2
- package/src/components/form/field/types/CheckboxGroupField.js +83 -6
- package/src/components/form/field/types/ColorField.js +56 -3
- package/src/components/form/field/types/DateField.js +155 -4
- package/src/components/form/field/types/EmailField.js +54 -4
- package/src/components/form/field/types/FileField.js +140 -6
- package/src/components/form/field/types/HiddenField.js +27 -1
- package/src/components/form/field/types/ImageField.js +82 -3
- package/src/components/form/field/types/NumberField.js +97 -4
- package/src/components/form/field/types/PasswordField.js +103 -7
- package/src/components/form/field/types/RadioField.js +75 -4
- package/src/components/form/field/types/RangeField.js +67 -1
- package/src/components/form/field/types/SearchField.js +41 -2
- package/src/components/form/field/types/SelectField.js +133 -4
- package/src/components/form/field/types/StringField.js +91 -2
- package/src/components/form/field/types/TelField.js +55 -4
- package/src/components/form/field/types/TextAreaField.js +76 -2
- package/src/components/form/field/types/TimeField.js +120 -5
- package/src/components/form/field/types/UrlField.js +59 -4
- package/src/components/form/field/types/file-field-mode/FileAvatarMode.js +83 -4
- package/src/components/form/field/types/file-field-mode/FileDropzoneMode.js +61 -3
- package/src/components/form/field/types/file-field-mode/FileItemPreview.js +79 -3
- package/src/components/form/field/types/file-field-mode/FileNativeMode.js +24 -2
- package/src/components/form/field/types/file-field-mode/FileUploadButtonMode.js +64 -3
- package/src/components/form/field/types/file-field-mode/FileWallMode.js +56 -3
- package/src/components/form/index.js +28 -28
- package/src/components/form/types/Field.d.ts +73 -0
- package/src/components/form/types/FieldCollection.d.ts +53 -0
- package/src/components/form/types/FormControl.d.ts +64 -0
- package/src/components/form/types/fields/AutocompleteField.d.ts +48 -0
- package/src/components/form/types/fields/CheckboxField.d.ts +33 -0
- package/src/components/form/types/fields/CheckboxGroupField.d.ts +49 -0
- package/src/components/form/types/fields/ColorField.d.ts +37 -0
- package/src/components/form/types/fields/DateField.d.ts +70 -0
- package/src/components/form/types/fields/EmailField.d.ts +35 -0
- package/src/components/form/types/fields/FileAvatarMode.d.ts +46 -0
- package/src/components/form/types/fields/FileDropzoneMode.d.ts +28 -0
- package/src/components/form/types/fields/FileField.d.ts +56 -0
- package/src/components/form/types/fields/FileItemPreview.d.ts +35 -0
- package/src/components/form/types/fields/FileNativeMode.d.ts +21 -0
- package/src/components/form/types/fields/FileUploadButtonMode.d.ts +34 -0
- package/src/components/form/types/fields/FileWallMode.d.ts +32 -0
- package/src/components/form/types/fields/HiddenField.d.ts +26 -0
- package/src/components/form/types/fields/ImageField.d.ts +45 -0
- package/src/components/form/types/fields/NumberField.d.ts +48 -0
- package/src/components/form/types/fields/PasswordField.d.ts +46 -0
- package/src/components/form/types/fields/RadioField.d.ts +48 -0
- package/src/components/form/types/fields/RangeField.d.ts +44 -0
- package/src/components/form/types/fields/SearchField.d.ts +34 -0
- package/src/components/form/types/fields/SelectField.d.ts +71 -0
- package/src/components/form/types/fields/StringField.d.ts +48 -0
- package/src/components/form/types/fields/TelField.d.ts +37 -0
- package/src/components/form/types/fields/TextAreaField.d.ts +44 -0
- package/src/components/form/types/fields/TimeField.d.ts +51 -0
- package/src/components/form/types/fields/UrlField.d.ts +35 -0
- package/src/components/form/validation/Validation.js +54 -54
- package/src/components/index.d.ts +160 -0
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +85 -67
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +105 -38
- package/src/components/list/ListItem.js +158 -49
- package/src/components/list/index.js +8 -6
- package/src/components/list/types/List.d.ts +43 -0
- package/src/components/list/types/ListGroup.d.ts +37 -0
- package/src/components/list/types/ListItem.d.ts +53 -0
- package/src/components/menu/HasMenuItem.js +55 -6
- package/src/components/menu/Menu.js +113 -22
- package/src/components/menu/MenuDivider.js +18 -2
- package/src/components/menu/MenuGroup.js +61 -6
- package/src/components/menu/MenuItem.js +95 -11
- package/src/components/menu/MenuLink.js +27 -2
- package/src/components/menu/index.js +6 -6
- package/src/components/menu/types/Menu.d.ts +60 -0
- package/src/components/menu/types/MenuDivider.d.ts +19 -0
- package/src/components/menu/types/MenuGroup.d.ts +44 -0
- package/src/components/menu/types/MenuItem.d.ts +46 -0
- package/src/components/menu/types/MenuLink.d.ts +16 -0
- package/src/components/modal/Modal.js +258 -17
- package/src/components/modal/index.js +3 -3
- package/src/components/modal/types/Modal.d.ts +94 -0
- package/src/components/pagination/Pagination.js +155 -7
- package/src/components/pagination/index.js +3 -3
- package/src/components/pagination/types/Pagination.d.ts +68 -0
- package/src/components/popover/Popover.js +198 -11
- package/src/components/popover/PopoverFooter.js +33 -9
- package/src/components/popover/PopoverHeader.js +33 -8
- package/src/components/popover/index.js +4 -4
- package/src/components/popover/types/Popover.d.ts +83 -0
- package/src/components/popover/types/PopoverFooter.d.ts +24 -0
- package/src/components/popover/types/PopoverHeader.d.ts +26 -0
- package/src/components/progress/Progress.js +182 -13
- package/src/components/progress/index.js +3 -3
- package/src/components/progress/types/Progress.d.ts +77 -0
- package/src/components/skeleton/Skeleton.js +117 -49
- package/src/components/skeleton/index.js +3 -3
- package/src/components/skeleton/types/Skeleton.d.ts +55 -0
- package/src/components/slider/Slider.js +207 -10
- package/src/components/slider/index.js +2 -2
- package/src/components/slider/types/Slider.d.ts +82 -0
- package/src/components/spacer/Spacer.js +12 -3
- package/src/components/spacer/index.js +2 -2
- package/src/components/spacer/types/Spacer.d.ts +19 -0
- package/src/components/spinner/Spinner.js +180 -9
- package/src/components/spinner/index.js +3 -3
- package/src/components/spinner/types/Spinner.d.ts +71 -0
- package/src/components/splitter/Splitter.js +76 -13
- package/src/components/splitter/SplitterGutter.js +67 -5
- package/src/components/splitter/SplitterPanel.js +69 -2
- package/src/components/splitter/index.js +5 -5
- package/src/components/splitter/types/Splitter.d.ts +38 -0
- package/src/components/splitter/types/SplitterGutter.d.ts +38 -0
- package/src/components/splitter/types/SplitterPanel.d.ts +41 -0
- package/src/components/stacks/AbsoluteStack.js +23 -3
- package/src/components/stacks/FixedStack.js +23 -3
- package/src/components/stacks/HStack.js +24 -3
- package/src/components/stacks/PositionStack.js +111 -3
- package/src/components/stacks/RelativeStack.js +23 -3
- package/src/components/stacks/Stack.js +73 -2
- package/src/components/stacks/VStack.js +24 -4
- package/src/components/stacks/index.js +7 -7
- package/src/components/stacks/types/AbsoluteStack.d.ts +16 -0
- package/src/components/stacks/types/FixedStack.d.ts +16 -0
- package/src/components/stacks/types/HStack.d.ts +16 -0
- package/src/components/stacks/types/PositionStack.d.ts +54 -0
- package/src/components/stacks/types/RelativeStack.d.ts +17 -0
- package/src/components/stacks/types/Stack.d.ts +39 -0
- package/src/components/stacks/types/VStack.d.ts +16 -0
- package/src/components/stepper/Stepper.js +152 -12
- package/src/components/stepper/StepperStep.js +104 -3
- package/src/components/stepper/index.js +4 -4
- package/src/components/stepper/types/Stepper.d.ts +68 -0
- package/src/components/stepper/types/StepperStep.d.ts +54 -0
- package/src/components/switch/Switch.js +143 -6
- package/src/components/switch/index.js +1 -1
- package/src/components/switch/types/Switch.d.ts +55 -0
- package/src/components/table/Column.js +105 -6
- package/src/components/table/ColumnGroup.js +48 -3
- package/src/components/table/DataTable.js +256 -19
- package/src/components/table/SimpleTable.js +58 -4
- package/src/components/table/index.js +2 -2
- package/src/components/table/types/Column.d.ts +49 -0
- package/src/components/table/types/ColumnGroup.d.ts +28 -0
- package/src/components/table/types/DataTable.d.ts +97 -0
- package/src/components/table/types/SimpleTable.d.ts +40 -0
- package/src/components/tabs/Tabs.js +192 -5
- package/src/components/tabs/index.js +3 -3
- package/src/components/tabs/types/Tabs.d.ts +78 -0
- package/src/components/toast/Toast.js +133 -5
- package/src/components/toast/index.js +3 -3
- package/src/components/toast/types/Toast.d.ts +57 -0
- package/src/components/toast/types/ToastError.d.ts +7 -0
- package/src/components/toast/types/ToastInfo.d.ts +7 -0
- package/src/components/toast/types/ToastSuccess.d.ts +7 -0
- package/src/components/toast/types/ToastWarning.d.ts +7 -0
- package/src/components/tooltip/Tooltip.js +157 -13
- package/src/components/tooltip/index.js +2 -2
- package/src/components/tooltip/prototypes.js +1 -1
- package/src/components/tooltip/types/Tooltip.d.ts +65 -0
- package/src/core/data/MemoryManager.js +2 -2
- package/src/core/data/Observable.js +15 -18
- package/src/core/data/ObservableArray.js +118 -46
- package/src/core/data/ObservableChecker.js +2 -2
- package/src/core/data/ObservableItem.js +135 -21
- package/src/core/data/ObservableObject.js +126 -35
- package/src/core/data/ObservableResource.js +118 -3
- package/src/core/data/Store.js +142 -26
- package/src/core/data/observable-helpers/observable.is-to.js +196 -1
- package/src/core/data/observable-helpers/observable.prototypes.js +35 -8
- package/src/core/elements/anchor/anchor-with-sentinel.js +23 -2
- package/src/core/elements/anchor/anchor.js +16 -7
- package/src/core/elements/anchor/one-child-anchor-overwriting.js +2 -2
- package/src/core/elements/content-formatter.js +1 -1
- package/src/core/elements/control/for-each-array.js +9 -9
- package/src/core/elements/control/for-each.js +14 -14
- package/src/core/elements/control/show-if.js +11 -11
- package/src/core/elements/control/show-when.js +5 -5
- package/src/core/elements/control/switch.js +14 -14
- package/src/core/elements/description-list.js +1 -1
- package/src/core/elements/form.js +2 -2
- package/src/core/elements/fragment.js +1 -1
- package/src/core/elements/html5-semantics.js +1 -1
- package/src/core/elements/img.js +3 -3
- package/src/core/elements/interactive.js +1 -1
- package/src/core/elements/list.js +1 -1
- package/src/core/elements/medias.js +1 -1
- package/src/core/elements/meta-data.js +1 -1
- package/src/core/elements/svg.js +1 -1
- package/src/core/elements/table.js +1 -1
- package/src/core/errors/ArgTypesError.js +1 -1
- package/src/core/utils/HasEventEmitter.js +36 -2
- package/src/core/utils/args-types.js +9 -9
- package/src/core/utils/cache.js +1 -1
- package/src/core/utils/callback-handler.js +29 -0
- package/src/core/utils/debug-manager.js +6 -6
- package/src/core/utils/events.js +139 -139
- package/src/core/utils/filters/date.js +84 -3
- package/src/core/utils/filters/standard.js +136 -11
- package/src/core/utils/filters/strings.js +34 -2
- package/src/core/utils/filters/utils.js +40 -4
- package/src/core/utils/formatters.js +4 -4
- package/src/core/utils/helpers.js +39 -7
- package/src/core/utils/localstorage.js +11 -11
- package/src/core/utils/memoize.js +56 -3
- package/src/core/utils/plugins-manager.js +3 -3
- package/src/core/utils/property-accumulator.js +6 -6
- package/src/core/utils/prototypes.js +26 -1
- package/src/core/utils/shortcut-manager.js +2 -2
- package/src/core/utils/validator.js +8 -8
- package/src/core/wrappers/AttributesWrapper.js +32 -22
- package/src/core/wrappers/DocumentObserver.js +3 -3
- package/src/core/wrappers/ElementCreator.js +5 -5
- package/src/core/wrappers/HtmlElementWrapper.js +38 -12
- package/src/core/wrappers/NDElement.js +328 -22
- package/src/core/wrappers/NdPrototype.js +60 -16
- package/src/core/wrappers/SingletonView.js +50 -2
- package/src/core/wrappers/SvgElementWrapper.js +1 -1
- package/src/core/wrappers/constants.js +35 -2
- package/src/core/wrappers/prototypes/attributes-extensions.js +7 -7
- package/src/core/wrappers/prototypes/nd-element-extensions.js +72 -6
- package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +42 -2
- package/src/core/wrappers/template-cloner/NodeCloner.js +53 -8
- package/src/core/wrappers/template-cloner/TemplateCloner.js +75 -6
- package/src/core/wrappers/template-cloner/attributes-hydrator.js +58 -2
- package/src/core/wrappers/template-cloner/utils.js +42 -6
- package/src/fetch/NativeFetch.js +3 -3
- package/src/i18n/bin/scan.js +6 -6
- package/src/i18n/index.d.ts +2 -0
- package/src/i18n/service/I18nService.d.ts +27 -0
- package/src/i18n/service/I18nService.js +5 -5
- package/src/i18n/service/functions.d.ts +22 -0
- package/src/i18n/service/functions.js +2 -2
- package/src/router/Route.js +3 -3
- package/src/router/RouteGroupHelper.js +2 -2
- package/src/router/Router.js +15 -15
- package/src/router/RouterComponent.js +33 -7
- package/src/router/link.js +4 -4
- package/src/router/modes/HashRouter.js +2 -2
- package/src/router/modes/HistoryRouter.js +2 -2
- package/src/router/modes/MemoryRouter.js +1 -1
- package/src/ui/components/accordion/AccordionItemRender.js +3 -3
- package/src/ui/components/accordion/AccordionRender.js +1 -1
- package/src/ui/components/alert/AlertRender.js +10 -10
- package/src/ui/components/avatar/avata-group/AvatarGroupRender.js +1 -1
- package/src/ui/components/avatar/avatar/AvatarRender.js +1 -1
- package/src/ui/components/breadcrumb/BreadcrumbRender.js +2 -2
- package/src/ui/components/button/ButtonRender.js +1 -1
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +6 -6
- package/src/ui/components/dropdown/DropdownRender.js +8 -8
- package/src/ui/components/dropdown/group/DropdownGroupRender.js +2 -2
- package/src/ui/components/dropdown/item/DropdownItemRender.js +1 -1
- package/src/ui/components/form/FieldCollectionRender.js +2 -2
- package/src/ui/components/form/FormControlRender.js +5 -5
- package/src/ui/components/form/fields/AutocompleteFieldRender.js +3 -3
- package/src/ui/components/form/fields/CheckboxFieldRender.js +1 -1
- package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +1 -1
- package/src/ui/components/form/fields/DateFieldRender.js +7 -7
- package/src/ui/components/form/fields/EmailFieldRender.js +1 -1
- package/src/ui/components/form/fields/FieldRender.js +4 -4
- package/src/ui/components/form/fields/FileFieldRender.js +1 -1
- package/src/ui/components/form/fields/PasswordFieldRender.js +2 -2
- package/src/ui/components/form/fields/RadioFieldRender.js +1 -1
- package/src/ui/components/form/fields/RangeFieldRender.js +1 -1
- package/src/ui/components/form/fields/SelectFieldRender.js +2 -2
- package/src/ui/components/form/fields/SliderFieldRender.js +6 -6
- package/src/ui/components/form/fields/StringFieldRender.js +1 -1
- package/src/ui/components/form/fields/TelFieldRender.js +1 -1
- package/src/ui/components/form/fields/TextAreaFieldRender.js +1 -1
- package/src/ui/components/form/fields/TimeFieldRender.js +3 -3
- package/src/ui/components/form/fields/UrlFieldRender.js +1 -1
- package/src/ui/components/form/file-upload-mode/FileAvatarModeRender.js +1 -1
- package/src/ui/components/form/file-upload-mode/FileDropzoneModeRender.js +2 -2
- package/src/ui/components/form/file-upload-mode/FileUploadButtonModeRender.js +2 -2
- package/src/ui/components/form/file-upload-mode/FileWallModeRender.js +1 -1
- package/src/ui/components/form/helpers.js +8 -8
- package/src/ui/components/form/index.js +27 -27
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/menu/MenuDividerRender.js +1 -1
- package/src/ui/components/menu/MenuGroupRender.js +3 -3
- package/src/ui/components/menu/MenuItemRender.js +2 -2
- package/src/ui/components/menu/MenuLinkRender.js +3 -3
- package/src/ui/components/menu/helpers.js +4 -4
- package/src/ui/components/modal/ModalRender.js +4 -4
- package/src/ui/components/pagination/PaginationRender.js +9 -9
- package/src/ui/components/popover/PopoverRender.js +7 -7
- package/src/ui/components/progress/ProgressRender.js +12 -12
- package/src/ui/components/skeleton/SkeletonRender.js +56 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/components/splitter/SplitterGutterRender.js +1 -1
- package/src/ui/components/splitter/SplitterPanelRender.js +2 -2
- package/src/ui/components/stacks/PositionStackRender.js +1 -1
- package/src/ui/components/stacks/StackRender.js +1 -1
- package/src/ui/components/stacks/absolute-stack/AbsoluteStackRender.js +1 -1
- package/src/ui/components/stacks/fixed-stack/FixedStackRender.js +1 -1
- package/src/ui/components/stacks/h-stack/HStackRender.js +1 -1
- package/src/ui/components/stacks/index.js +5 -5
- package/src/ui/components/stacks/relative-stack/RelativeStackRender.js +1 -1
- package/src/ui/components/stacks/v-stack/VStackRender.js +1 -1
- package/src/ui/components/stepper/StepperRender.js +2 -2
- package/src/ui/components/stepper/StepperStepRender.js +4 -4
- package/src/ui/components/switch/SwitchRender.js +4 -4
- package/src/ui/components/table/data-table/DataTableRender.js +5 -5
- package/src/ui/components/table/data-table/bulk-actions.js +7 -7
- package/src/ui/components/table/data-table/pagination.js +6 -6
- package/src/ui/components/table/data-table/tables.js +25 -25
- package/src/ui/components/table/data-table/toolbar.js +3 -3
- package/src/ui/components/table/simple-table/SimpleTableRender.js +8 -8
- package/src/ui/components/tabs/TabsRender.js +11 -11
- package/src/ui/components/toast/ToastRender.js +3 -3
- package/src/ui/components/tooltip/TooltipRender.js +1 -1
- package/src/ui/index.js +44 -36
- package/types/elements.d.ts +163 -1037
- package/types/forms.d.ts +16 -20
- package/types/globals.d.ts +543 -0
- package/types/images.d.ts +2 -2
- package/types/observable-resource.d.ts +3 -0
- package/types/property-accumulator.d.ts +4 -4
- package/types/store.d.ts +26 -2
- package/types/validator.ts +3 -3
- package/ui.js +1 -0
- package/src/components/form/field/DefaultRender.js +0 -77
- package/src/components/form/field/FieldFactory.js +0 -107
- package/src/components/skeleton/SkeletonList.js +0 -0
- package/src/components/skeleton/SkeletonParagraph.js +0 -0
- package/src/components/skeleton/SkeletonTable.js +0 -0
- /package/{src/components/skeleton/SkeletonCard.js → docs/tutorials/.gitkeep} +0 -0
package/docs/filters.md
CHANGED
|
@@ -1,1216 +1,247 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Filters
|
|
3
|
+
description: Composable, reactive filter helpers for ObservableArray.where() - comparison, string, date, array, and logical operators
|
|
4
|
+
---
|
|
4
5
|
|
|
5
|
-
|
|
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 {
|
|
17
|
-
const {
|
|
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
|
-
|
|
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
|
-
//
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
---
|
|
69
24
|
|
|
70
|
-
|
|
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: '
|
|
84
|
-
{ name: '
|
|
85
|
-
{ name: '
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
44
|
+
All accept observables as arguments - filters update automatically when they change:
|
|
102
45
|
|
|
103
|
-
### between()
|
|
104
46
|
```javascript
|
|
105
|
-
|
|
106
|
-
const
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
})
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
const {
|
|
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
|
-
//
|
|
238
|
-
|
|
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
|
-
|
|
86
|
+
---
|
|
257
87
|
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
302
|
-
|
|
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
|
-
|
|
98
|
+
---
|
|
308
99
|
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
351
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
});
|
|
359
|
-
// Result: Task 2
|
|
111
|
+
// not - inverts
|
|
112
|
+
products.where({ status: not(equals('banned')) })
|
|
360
113
|
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
120
|
+
`all` is an alias for `and`, `any` is an alias for `or`.
|
|
369
121
|
|
|
370
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
})
|
|
464
|
-
|
|
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
|
-
|
|
136
|
+
---
|
|
480
137
|
|
|
481
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
565
|
-
const { dateTimeBetween } = filters;
|
|
566
|
-
import { Observable } from 'native-document';
|
|
145
|
+
import { dateEquals, dateBefore, dateAfter, dateBetween } from 'native-document/filters';
|
|
567
146
|
|
|
568
|
-
const
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
-
###
|
|
155
|
+
### Time (ignores date)
|
|
585
156
|
|
|
586
|
-
#### Filter Events by Today/This Week
|
|
587
157
|
```javascript
|
|
588
|
-
import {
|
|
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
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
//
|
|
649
|
-
|
|
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
|
-
|
|
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
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
| `
|
|
1022
|
-
| `
|
|
1023
|
-
| `
|
|
1024
|
-
| `
|
|
1025
|
-
| `
|
|
1026
|
-
| `
|
|
1027
|
-
|
|
1028
|
-
###
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
| `
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
| `
|
|
1040
|
-
| `
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
|
1061
|
-
|
|
1062
|
-
| `
|
|
1063
|
-
| `
|
|
1064
|
-
| `
|
|
1065
|
-
| `
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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
|
-
|
|
240
|
+
---
|
|
1195
241
|
|
|
1196
242
|
## Next Steps
|
|
1197
243
|
|
|
1198
|
-
- **[
|
|
1199
|
-
- **[
|
|
1200
|
-
- **[
|
|
1201
|
-
- **[
|
|
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
|