@livenetworks/ashlar 1.6.0 → 2.0.0
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/LICENSE +641 -0
- package/README.md +202 -86
- package/{js → components}/COMPONENTS.md +1116 -1108
- package/{js → components}/index.js +81 -58
- package/{js → components}/ln-accordion/README.md +7 -6
- package/components/ln-accordion/ln-accordion-dev.scss +8 -0
- package/components/ln-accordion/ln-accordion.js +1 -0
- package/components/ln-accordion/ln-accordion.schema.json +30 -0
- package/components/ln-accordion/ln-accordion.scss +8 -0
- package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
- package/{js → components}/ln-ajax/README.md +171 -160
- package/components/ln-ajax/ln-ajax-dev.scss +8 -0
- package/components/ln-ajax/ln-ajax.js +1 -0
- package/components/ln-ajax/ln-ajax.schema.json +30 -0
- package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
- package/{js → components}/ln-api-connector/README.md +27 -5
- package/components/ln-api-connector/ln-api-connector.js +1 -0
- package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
- package/components/ln-api-connector/src/connector-core.js +97 -0
- package/components/ln-api-connector/src/ln-api-connector.js +447 -0
- package/{js → components}/ln-api-queue/README.md +12 -9
- package/components/ln-api-queue/ln-api-queue.js +1 -0
- package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
- package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
- package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
- package/{js → components}/ln-autoresize/README.md +129 -127
- package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
- package/components/ln-autoresize/ln-autoresize.js +1 -0
- package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
- package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
- package/{js → components}/ln-autosave/README.md +127 -124
- package/components/ln-autosave/ln-autosave-dev.scss +13 -0
- package/components/ln-autosave/ln-autosave.js +1 -0
- package/components/ln-autosave/ln-autosave.schema.json +48 -0
- package/components/ln-autosave/src/autosave-model.js +34 -0
- package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
- package/components/ln-chart/README.md +145 -0
- package/components/ln-chart/ln-chart-dev.scss +20 -0
- package/components/ln-chart/ln-chart.js +1 -0
- package/components/ln-chart/ln-chart.schema.json +146 -0
- package/components/ln-chart/ln-chart.scss +8 -0
- package/components/ln-chart/src/chart-model.js +154 -0
- package/components/ln-chart/src/ln-chart.js +176 -0
- package/{js → components}/ln-circular-progress/README.md +15 -14
- package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
- package/components/ln-circular-progress/ln-circular-progress.js +1 -0
- package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
- package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
- package/components/ln-confirm/README.md +216 -0
- package/components/ln-confirm/ln-confirm-dev.scss +24 -0
- package/components/ln-confirm/ln-confirm.js +1 -0
- package/components/ln-confirm/ln-confirm.schema.json +66 -0
- package/components/ln-confirm/ln-confirm.scss +24 -0
- package/{js → components}/ln-confirm/src/ln-confirm.js +180 -167
- package/{js → components}/ln-core/README.md +374 -149
- package/components/ln-core/attrs.js +273 -0
- package/components/ln-core/compare.js +40 -0
- package/components/ln-core/crypto.js +265 -0
- package/components/ln-core/date.js +50 -0
- package/components/ln-core/dom.js +115 -0
- package/components/ln-core/events.js +122 -0
- package/components/ln-core/form.js +136 -0
- package/{js → components}/ln-core/hash.js +149 -92
- package/components/ln-core/helpers.js +33 -0
- package/components/ln-core/http.js +48 -0
- package/components/ln-core/index.js +11 -0
- package/components/ln-core/lifecycle.js +403 -0
- package/components/ln-core/ln-core.schema.json +109 -0
- package/components/ln-core/locale.js +55 -0
- package/components/ln-core/matching.js +78 -0
- package/components/ln-core/number.js +125 -0
- package/components/ln-core/progress.js +30 -0
- package/components/ln-core/template.js +206 -0
- package/{js → components}/ln-core/window-cache.js +247 -213
- package/{js → components}/ln-couchdb-connector/README.md +7 -5
- package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
- package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
- package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
- package/{js → components}/ln-data-coordinator/README.md +109 -73
- package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
- package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
- package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
- package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
- package/{js → components}/ln-data-store/README.md +42 -17
- package/components/ln-data-store/ln-data-store-dev.scss +13 -0
- package/components/ln-data-store/ln-data-store.js +1 -0
- package/components/ln-data-store/ln-data-store.schema.json +84 -0
- package/components/ln-data-store/src/data-store-model.js +155 -0
- package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
- package/components/ln-data-store/src/window-index.js +119 -0
- package/{js → components}/ln-date/README.md +17 -7
- package/{js → components}/ln-date/ln-date-dev.scss +14 -14
- package/components/ln-date/ln-date.js +1 -0
- package/components/ln-date/ln-date.schema.json +92 -0
- package/components/ln-date/src/date-model.js +186 -0
- package/components/ln-date/src/ln-date.js +486 -0
- package/components/ln-debug/README.md +132 -0
- package/components/ln-debug/ln-debug.js +1 -0
- package/components/ln-debug/ln-debug.schema.json +28 -0
- package/components/ln-debug/src/console-sink.js +19 -0
- package/components/ln-debug/src/debug-verifier.js +331 -0
- package/components/ln-debug/src/gate.js +95 -0
- package/components/ln-debug/src/generated-attributes.js +299 -0
- package/components/ln-debug/src/ln-debug.js +60 -0
- package/{js → components}/ln-dropdown/README.md +150 -140
- package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
- package/components/ln-dropdown/ln-dropdown.js +1 -0
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
- package/components/ln-dropdown/ln-dropdown.scss +18 -0
- package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
- package/{js → components}/ln-editor/README.md +11 -9
- package/components/ln-editor/ln-editor-dev.scss +18 -0
- package/components/ln-editor/ln-editor.js +1 -0
- package/components/ln-editor/ln-editor.schema.json +58 -0
- package/components/ln-editor/ln-editor.scss +13 -0
- package/{js → components}/ln-editor/src/ln-editor.js +59 -11
- package/{js → components}/ln-external-links/README.md +10 -8
- package/components/ln-external-links/ln-external-links.js +1 -0
- package/components/ln-external-links/ln-external-links.schema.json +18 -0
- package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
- package/{js → components}/ln-fill/README.md +6 -8
- package/components/ln-fill/ln-fill.js +1 -0
- package/components/ln-fill/ln-fill.schema.json +23 -0
- package/{js → components}/ln-fill/src/ln-fill.js +7 -1
- package/components/ln-filter/README.md +131 -0
- package/components/ln-filter/ln-filter-dev.scss +13 -0
- package/components/ln-filter/ln-filter.js +1 -0
- package/components/ln-filter/ln-filter.schema.json +153 -0
- package/components/ln-filter/src/filter-model.js +185 -0
- package/components/ln-filter/src/ln-filter.js +435 -0
- package/{js → components}/ln-form/README.md +260 -264
- package/components/ln-form/ln-form-dev.scss +8 -0
- package/components/ln-form/ln-form.js +1 -0
- package/components/ln-form/ln-form.schema.json +45 -0
- package/{js → components}/ln-form/src/ln-form.js +107 -99
- package/{js → components}/ln-http/README.md +11 -6
- package/components/ln-http/ln-http.js +1 -0
- package/components/ln-http/ln-http.schema.json +6 -0
- package/components/ln-http/src/http-core.js +51 -0
- package/{js → components}/ln-http/src/ln-http.js +28 -33
- package/{js → components}/ln-icon/README.md +16 -9
- package/components/ln-icon/ln-icon.schema.json +6 -0
- package/components/ln-include/README.md +75 -0
- package/components/ln-include/ln-include-dev.scss +13 -0
- package/components/ln-include/ln-include.js +1 -0
- package/components/ln-include/ln-include.schema.json +23 -0
- package/components/ln-include/src/ln-include.js +111 -0
- package/components/ln-key/README.md +130 -0
- package/components/ln-key/ln-key-dev.scss +18 -0
- package/components/ln-key/ln-key.js +1 -0
- package/components/ln-key/ln-key.schema.json +56 -0
- package/components/ln-key/src/key-model.js +146 -0
- package/components/ln-key/src/ln-key.js +201 -0
- package/{js → components}/ln-link/README.md +7 -9
- package/components/ln-link/ln-link-dev.scss +8 -0
- package/components/ln-link/ln-link.js +1 -0
- package/components/ln-link/ln-link.schema.json +22 -0
- package/{js → components}/ln-link/src/ln-link.js +16 -11
- package/components/ln-list/README.md +152 -0
- package/components/ln-list/ln-list-dev.scss +8 -0
- package/components/ln-list/ln-list.js +1 -0
- package/components/ln-list/ln-list.schema.json +166 -0
- package/components/ln-list/ln-list.scss +25 -0
- package/{js → components}/ln-list/src/ln-list.js +1301 -1216
- package/{js → components}/ln-modal/README.md +13 -10
- package/components/ln-modal/ln-modal-dev.scss +18 -0
- package/components/ln-modal/ln-modal.js +1 -0
- package/components/ln-modal/ln-modal.schema.json +54 -0
- package/components/ln-modal/ln-modal.scss +51 -0
- package/{js → components}/ln-modal/src/ln-modal.js +164 -137
- package/{js → components}/ln-nav/README.md +11 -11
- package/components/ln-nav/ln-nav.js +1 -0
- package/components/ln-nav/ln-nav.schema.json +25 -0
- package/{js → components}/ln-nav/src/ln-nav.js +25 -14
- package/components/ln-number/README.md +205 -0
- package/{js → components}/ln-number/ln-number-dev.scss +13 -13
- package/components/ln-number/ln-number.js +1 -0
- package/components/ln-number/ln-number.schema.json +63 -0
- package/components/ln-number/src/ln-number.js +400 -0
- package/components/ln-number/src/number-model.js +31 -0
- package/components/ln-obfuscator/README.md +60 -0
- package/components/ln-obfuscator/ln-obfuscator.js +1 -0
- package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
- package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
- package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
- package/{js → components}/ln-options/README.md +3 -1
- package/components/ln-options/ln-options-dev.scss +13 -0
- package/components/ln-options/ln-options.js +1 -0
- package/components/ln-options/ln-options.schema.json +41 -0
- package/{js → components}/ln-options/src/ln-options.js +13 -5
- package/components/ln-persist/README.md +151 -0
- package/components/ln-persist/ln-persist.js +1 -0
- package/components/ln-persist/ln-persist.schema.json +28 -0
- package/components/ln-persist/src/ln-persist.js +83 -0
- package/components/ln-picklist/README.md +230 -0
- package/components/ln-picklist/ln-picklist-dev.scss +22 -0
- package/components/ln-picklist/ln-picklist.js +1 -0
- package/components/ln-picklist/ln-picklist.schema.json +49 -0
- package/components/ln-picklist/src/ln-picklist.js +228 -0
- package/{js → components}/ln-popover/README.md +164 -162
- package/components/ln-popover/ln-popover-dev.scss +13 -0
- package/components/ln-popover/ln-popover.js +1 -0
- package/components/ln-popover/ln-popover.schema.json +82 -0
- package/components/ln-popover/ln-popover.scss +18 -0
- package/{js → components}/ln-popover/src/ln-popover.js +42 -36
- package/{js → components}/ln-progress/README.md +410 -413
- package/components/ln-progress/ln-progress-dev.scss +8 -0
- package/components/ln-progress/ln-progress.js +1 -0
- package/components/ln-progress/ln-progress.schema.json +40 -0
- package/components/ln-progress/src/ln-progress.js +91 -0
- package/components/ln-progress/src/progress-model.js +20 -0
- package/{js → components}/ln-router/README.md +443 -432
- package/components/ln-router/ln-router-dev.scss +13 -0
- package/components/ln-router/ln-router.js +1 -0
- package/components/ln-router/ln-router.schema.json +68 -0
- package/{js → components}/ln-router/src/ln-router.js +585 -539
- package/components/ln-router/src/router-model.js +50 -0
- package/components/ln-scroll/README.md +45 -0
- package/components/ln-scroll/ln-scroll.js +1 -0
- package/components/ln-scroll/ln-scroll.schema.json +85 -0
- package/components/ln-scroll/src/ln-scroll.js +150 -0
- package/components/ln-search/README.md +188 -0
- package/components/ln-search/ln-search-dev.scss +13 -0
- package/components/ln-search/ln-search.js +1 -0
- package/components/ln-search/ln-search.schema.json +103 -0
- package/components/ln-search/src/ln-search.js +330 -0
- package/{js → components}/ln-slug/README.md +6 -3
- package/components/ln-slug/ln-slug-dev.scss +18 -0
- package/components/ln-slug/ln-slug.js +1 -0
- package/components/ln-slug/ln-slug.schema.json +23 -0
- package/{js → components}/ln-slug/src/ln-slug.js +20 -13
- package/components/ln-slug/src/slug-model.js +21 -0
- package/{js → components}/ln-sort/README.md +178 -170
- package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
- package/components/ln-sort/ln-sort.js +1 -0
- package/components/ln-sort/ln-sort.schema.json +104 -0
- package/components/ln-sort/src/ln-sort.js +321 -0
- package/components/ln-sort/src/sort-model.js +117 -0
- package/{js → components}/ln-sortable/README.md +7 -11
- package/components/ln-sortable/ln-sortable-dev.scss +13 -0
- package/components/ln-sortable/ln-sortable.js +1 -0
- package/components/ln-sortable/ln-sortable.schema.json +36 -0
- package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
- package/{js → components}/ln-stat/README.md +3 -1
- package/components/ln-stat/ln-stat-dev.scss +8 -0
- package/components/ln-stat/ln-stat.js +1 -0
- package/components/ln-stat/ln-stat.schema.json +32 -0
- package/{js → components}/ln-stat/src/ln-stat.js +14 -18
- package/components/ln-stat/src/stat-model.js +28 -0
- package/{js → components}/ln-table/README.md +37 -24
- package/{js → components}/ln-table/ln-table-dev.scss +17 -17
- package/components/ln-table/ln-table.js +1 -0
- package/components/ln-table/ln-table.schema.json +211 -0
- package/components/ln-table/ln-table.scss +15 -0
- package/{js → components}/ln-table/src/ln-table.js +1365 -1493
- package/components/ln-table/src/table-model.js +98 -0
- package/components/ln-table-coordinator/README.md +61 -0
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
- package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
- package/{js → components}/ln-tabs/README.md +32 -14
- package/components/ln-tabs/ln-tabs-dev.scss +24 -0
- package/components/ln-tabs/ln-tabs.js +1 -0
- package/components/ln-tabs/ln-tabs.schema.json +75 -0
- package/components/ln-tabs/ln-tabs.scss +8 -0
- package/components/ln-tabs/src/ln-tabs.js +207 -0
- package/components/ln-tabs/src/tabs-model.js +82 -0
- package/{js → components}/ln-time/README.md +96 -95
- package/components/ln-time/ln-time-dev.scss +13 -0
- package/components/ln-time/ln-time.js +1 -0
- package/components/ln-time/ln-time.schema.json +38 -0
- package/{js → components}/ln-time/src/ln-time.js +206 -223
- package/components/ln-time/src/time-model.js +64 -0
- package/{js → components}/ln-toast/README.md +23 -5
- package/components/ln-toast/ln-toast-dev.scss +8 -0
- package/components/ln-toast/ln-toast.js +1 -0
- package/components/ln-toast/ln-toast.schema.json +65 -0
- package/components/ln-toast/ln-toast.scss +59 -0
- package/{js → components}/ln-toast/src/ln-toast.js +233 -232
- package/{js → components}/ln-toggle/README.md +198 -196
- package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
- package/components/ln-toggle/ln-toggle.js +1 -0
- package/components/ln-toggle/ln-toggle.schema.json +52 -0
- package/components/ln-toggle/ln-toggle.scss +37 -0
- package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
- package/{js → components}/ln-tooltip/README.md +6 -6
- package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
- package/components/ln-tooltip/ln-tooltip.js +1 -0
- package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
- package/components/ln-tooltip/ln-tooltip.scss +9 -0
- package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
- package/{js → components}/ln-translations/README.md +3 -1
- package/components/ln-translations/ln-translations-dev.scss +13 -0
- package/components/ln-translations/ln-translations.js +1 -0
- package/components/ln-translations/ln-translations.schema.json +120 -0
- package/{js → components}/ln-translations/src/ln-translations.js +25 -10
- package/components/ln-ui-coordinator/README.md +101 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
- package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
- package/components/ln-upload/README.md +133 -0
- package/components/ln-upload/ln-upload-dev.scss +18 -0
- package/components/ln-upload/ln-upload.js +1 -0
- package/components/ln-upload/ln-upload.schema.json +193 -0
- package/components/ln-upload/ln-upload.scss +20 -0
- package/components/ln-upload/src/ln-upload.js +652 -0
- package/components/ln-upload/src/upload-model.js +78 -0
- package/{js → components}/ln-validate/README.md +182 -180
- package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
- package/components/ln-validate/ln-validate.js +1 -0
- package/components/ln-validate/ln-validate.schema.json +50 -0
- package/components/ln-validate/ln-validate.scss +7 -0
- package/{js → components}/ln-validate/src/ln-validate.js +23 -45
- package/components/ln-validate/src/validate-model.js +64 -0
- package/components/ln-websocket-connector/README.md +166 -0
- package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
- package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
- package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
- package/ln-ashlar.html-data.json +1796 -0
- package/package.json +84 -61
- package/{scss → theme}/base/_global.scss +1 -2
- package/{scss → theme}/base/_reset.scss +1 -1
- package/{scss → theme}/base/_typography.scss +2 -0
- package/theme/brand.css +73 -0
- package/{scss → theme}/components/_app-shell.scss +8 -0
- package/theme/components/_button.scss +24 -0
- package/theme/components/_chart.scss +5 -0
- package/theme/components/_confirm.scss +6 -0
- package/{scss → theme}/components/_date.scss +30 -22
- package/{scss → theme}/components/_dropdown.scss +6 -1
- package/theme/components/_form.scss +140 -0
- package/{scss → theme}/components/_layout.scss +6 -0
- package/{scss → theme}/components/_ln-table.scss +1 -6
- package/theme/components/_picklist.scss +3 -0
- package/{scss → theme}/components/_sort.scss +10 -10
- package/theme/components/_status-badge.scss +6 -0
- package/{scss → theme}/components/_table.scss +16 -0
- package/{scss → theme}/components/_tabs.scss +0 -4
- package/theme/components/_toast.scss +48 -0
- package/{scss → theme}/components/_toggle.scss +1 -1
- package/theme/components/_upload.scss +45 -0
- package/{scss → theme}/config/_breakpoints.scss +1 -1
- package/theme/config/_contrast.scss +128 -0
- package/{scss → theme}/config/_density.scss +191 -205
- package/theme/config/_palette.scss +274 -0
- package/theme/config/_theme.scss +293 -0
- package/{scss → theme}/config/_tokens.scss +285 -441
- package/{scss → theme}/config/mixins/_accordion.scss +1 -1
- package/{scss → theme}/config/mixins/_alert.scss +1 -0
- package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
- package/{scss → theme}/config/mixins/_borders.scss +2 -0
- package/theme/config/mixins/_btn.scss +175 -0
- package/{scss → theme}/config/mixins/_card.scss +55 -46
- package/theme/config/mixins/_chart.scss +82 -0
- package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
- package/{scss → theme}/config/mixins/_colors.scss +8 -14
- package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
- package/theme/config/mixins/_dropdown.scss +169 -0
- package/{scss → theme}/config/mixins/_editor.scss +0 -7
- package/{scss → theme}/config/mixins/_filter.scss +47 -47
- package/{scss → theme}/config/mixins/_footer.scss +1 -1
- package/{scss → theme}/config/mixins/_form.scss +49 -18
- package/{scss → theme}/config/mixins/_index.scss +62 -61
- package/{scss → theme}/config/mixins/_layout.scss +6 -2
- package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
- package/{scss → theme}/config/mixins/_loader.scss +1 -1
- package/{scss → theme}/config/mixins/_modal.scss +145 -149
- package/{scss → theme}/config/mixins/_motion.scss +13 -0
- package/{scss → theme}/config/mixins/_nav.scss +7 -7
- package/theme/config/mixins/_picklist.scss +66 -0
- package/theme/config/mixins/_popover.scss +96 -0
- package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
- package/{scss → theme}/config/mixins/_sort.scss +43 -42
- package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
- package/{scss → theme}/config/mixins/_table.scss +25 -14
- package/{scss → theme}/config/mixins/_tabs.scss +1 -1
- package/{scss → theme}/config/mixins/_toast.scss +174 -179
- package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
- package/{scss → theme}/config/mixins/_typography.scss +16 -5
- package/{scss → theme}/config/mixins/_upload.scss +8 -6
- package/theme/ln-ashlar-core.scss +24 -0
- package/theme/ln-ashlar-dev.scss +66 -0
- package/theme/ln-ashlar-landing.scss +56 -0
- package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
- package/theme/ln-ashlar.scss +8 -0
- package/theme/presentation/_bento.scss +151 -0
- package/theme/presentation/_cta.scss +111 -0
- package/theme/presentation/_effects.scss +216 -0
- package/theme/presentation/_features.scss +108 -0
- package/theme/presentation/_footer.scss +129 -0
- package/theme/presentation/_hero.scss +158 -0
- package/theme/presentation/_layout.scss +170 -0
- package/theme/presentation/_navbar.scss +140 -0
- package/theme/presentation/_pricing.scss +262 -0
- package/theme/presentation/_stats.scss +87 -0
- package/theme/presentation/_testimonials.scss +124 -0
- package/theme/presentation/_tokens.scss +39 -0
- package/theme/presentation/_typography.scss +163 -0
- package/{scss → theme}/utilities/_animations.scss +38 -0
- package/{scss → theme}/utilities/_utilities.scss +6 -0
- package/web-types.json +2312 -0
- package/js/ln-accordion/ln-accordion.js +0 -1
- package/js/ln-ajax/ln-ajax.js +0 -1
- package/js/ln-api-connector/ln-api-connector.js +0 -1
- package/js/ln-api-connector/src/ln-api-connector.js +0 -368
- package/js/ln-api-queue/ln-api-queue.js +0 -1
- package/js/ln-autoresize/ln-autoresize.js +0 -1
- package/js/ln-autosave/ln-autosave.js +0 -1
- package/js/ln-circular-progress/ln-circular-progress.js +0 -1
- package/js/ln-confirm/README.md +0 -162
- package/js/ln-confirm/_ln-confirm.scss +0 -24
- package/js/ln-confirm/ln-confirm.js +0 -1
- package/js/ln-core/crypto.js +0 -83
- package/js/ln-core/helpers.js +0 -788
- package/js/ln-core/index.js +0 -7
- package/js/ln-core/persist.js +0 -97
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
- package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
- package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
- package/js/ln-data-store/ln-data-store.js +0 -1
- package/js/ln-date/ln-date.js +0 -1
- package/js/ln-date/src/ln-date.js +0 -603
- package/js/ln-debug/README.md +0 -58
- package/js/ln-debug/ln-debug.js +0 -1
- package/js/ln-debug/src/ln-debug.js +0 -19
- package/js/ln-dropdown/ln-dropdown.js +0 -1
- package/js/ln-dropdown/ln-dropdown.scss +0 -15
- package/js/ln-editor/ln-editor.js +0 -1
- package/js/ln-editor/ln-editor.scss +0 -43
- package/js/ln-external-links/ln-external-links.js +0 -1
- package/js/ln-fill/ln-fill.js +0 -1
- package/js/ln-filter/README.md +0 -189
- package/js/ln-filter/ln-filter-dev.scss +0 -8
- package/js/ln-filter/ln-filter.js +0 -1
- package/js/ln-filter/src/ln-filter.js +0 -419
- package/js/ln-form/ln-form.js +0 -1
- package/js/ln-http/ln-http.js +0 -1
- package/js/ln-link/ln-link.js +0 -1
- package/js/ln-list/README.md +0 -129
- package/js/ln-list/ln-list.js +0 -1
- package/js/ln-list/ln-list.scss +0 -28
- package/js/ln-modal/ln-modal-dev.scss +0 -13
- package/js/ln-modal/ln-modal.js +0 -1
- package/js/ln-modal/ln-modal.scss +0 -35
- package/js/ln-modal-coordinator/README.md +0 -99
- package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
- package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
- package/js/ln-nav/ln-nav.js +0 -1
- package/js/ln-number/README.md +0 -149
- package/js/ln-number/ln-number.js +0 -1
- package/js/ln-number/src/ln-number.js +0 -443
- package/js/ln-options/ln-options.js +0 -1
- package/js/ln-popover/ln-popover.js +0 -1
- package/js/ln-progress/ln-progress.js +0 -1
- package/js/ln-progress/src/ln-progress.js +0 -98
- package/js/ln-router/ln-router.js +0 -1
- package/js/ln-search/README.md +0 -158
- package/js/ln-search/ln-search-dev.scss +0 -8
- package/js/ln-search/ln-search.js +0 -1
- package/js/ln-search/src/ln-search.js +0 -117
- package/js/ln-slug/ln-slug.js +0 -1
- package/js/ln-sort/ln-sort.js +0 -1
- package/js/ln-sort/src/ln-sort.js +0 -178
- package/js/ln-sortable/ln-sortable.js +0 -1
- package/js/ln-stat/ln-stat.js +0 -1
- package/js/ln-table/ln-table.js +0 -1
- package/js/ln-table/ln-table.scss +0 -11
- package/js/ln-table-coordinator/README.md +0 -73
- package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
- package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
- package/js/ln-tabs/ln-tabs-dev.scss +0 -18
- package/js/ln-tabs/ln-tabs.js +0 -1
- package/js/ln-tabs/src/ln-tabs.js +0 -188
- package/js/ln-time/ln-time.js +0 -1
- package/js/ln-toast/ln-toast.js +0 -1
- package/js/ln-toggle/ln-toggle.js +0 -1
- package/js/ln-tooltip/ln-tooltip.js +0 -1
- package/js/ln-tooltip/ln-tooltip.scss +0 -9
- package/js/ln-translations/ln-translations.js +0 -1
- package/js/ln-upload/README.md +0 -255
- package/js/ln-upload/ln-upload.js +0 -1
- package/js/ln-upload/ln-upload.scss +0 -20
- package/js/ln-upload/src/ln-upload.js +0 -423
- package/js/ln-validate/ln-validate.js +0 -1
- package/scss/components/_button.scss +0 -20
- package/scss/components/_confirm.scss +0 -5
- package/scss/components/_form.scss +0 -107
- package/scss/components/_status-badge.scss +0 -4
- package/scss/components/_toast.scss +0 -68
- package/scss/components/_upload.scss +0 -51
- package/scss/config/_theme.scss +0 -216
- package/scss/config/mixins/_btn.scss +0 -179
- package/scss/config/mixins/_confirm.scss +0 -31
- package/scss/config/mixins/_dropdown.scss +0 -141
- package/scss/config/mixins/_popover.scss +0 -64
- package/scss/ln-ashlar-dev.scss +0 -39
- /package/{js → components}/ln-core/positioning.js +0 -0
- /package/{js → components}/ln-core/reactive.js +0 -0
- /package/{js → components}/ln-filter/ln-filter.scss +0 -0
- /package/{js → components}/ln-icon/ln-icon.js +0 -0
- /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
- /package/{js → components}/ln-search/ln-search.scss +0 -0
- /package/{js → components}/ln-toast/template.html +0 -0
- /package/{scss → theme}/components/_accordion.scss +0 -0
- /package/{scss → theme}/components/_ajax.scss +0 -0
- /package/{scss → theme}/components/_alert.scss +0 -0
- /package/{scss → theme}/components/_avatar.scss +0 -0
- /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
- /package/{scss → theme}/components/_card.scss +0 -0
- /package/{scss → theme}/components/_chip.scss +0 -0
- /package/{scss → theme}/components/_circular-progress.scss +0 -0
- /package/{scss → theme}/components/_editor.scss +0 -0
- /package/{scss → theme}/components/_empty-state.scss +0 -0
- /package/{scss → theme}/components/_filter.scss +0 -0
- /package/{scss → theme}/components/_link.scss +0 -0
- /package/{scss → theme}/components/_loader.scss +0 -0
- /package/{scss → theme}/components/_modal.scss +0 -0
- /package/{scss → theme}/components/_nav.scss +0 -0
- /package/{scss → theme}/components/_page-header.scss +0 -0
- /package/{scss → theme}/components/_popover.scss +0 -0
- /package/{scss → theme}/components/_progress.scss +0 -0
- /package/{scss → theme}/components/_prose.scss +0 -0
- /package/{scss → theme}/components/_scrollbar.scss +0 -0
- /package/{scss → theme}/components/_sections.scss +0 -0
- /package/{scss → theme}/components/_sidebar.scss +0 -0
- /package/{scss → theme}/components/_stat-card.scss +0 -0
- /package/{scss → theme}/components/_stepper.scss +0 -0
- /package/{scss → theme}/components/_timeline.scss +0 -0
- /package/{scss → theme}/components/_tooltip.scss +0 -0
- /package/{scss → theme}/components/_translations.scss +0 -0
- /package/{scss → theme}/config/_icons.scss +0 -0
- /package/{scss → theme}/config/_mixins.scss +0 -0
- /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
- /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
- /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
- /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
- /package/{scss → theme}/config/mixins/_chip.scss +0 -0
- /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
- /package/{scss → theme}/config/mixins/_display.scss +0 -0
- /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
- /package/{scss → theme}/config/mixins/_focus.scss +0 -0
- /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
- /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
- /package/{scss → theme}/config/mixins/_link.scss +0 -0
- /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
- /package/{scss → theme}/config/mixins/_position.scss +0 -0
- /package/{scss → theme}/config/mixins/_progress.scss +0 -0
- /package/{scss → theme}/config/mixins/_prose.scss +0 -0
- /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
- /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
- /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
- /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
- /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
- /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
- /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
- /package/{scss → theme}/config/mixins/_translations.scss +0 -0
- /package/{scss → theme}/tabler-icons.txt +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, decryptData } from '../../ln-core';
|
|
1
|
+
import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, decryptData, defineAttrs, attrSpec, attrInt, attrBool, attrList } from '../../ln-core';
|
|
2
|
+
import { createWindowIndex } from './window-index.js';
|
|
3
|
+
import { aggregateRecords, decorateRecords, filterRecords, queryRecords } from './data-store-model.js';
|
|
2
4
|
|
|
3
5
|
(function () {
|
|
4
6
|
const DOM_SELECTOR = 'data-ln-data-store';
|
|
@@ -6,6 +8,29 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
6
8
|
|
|
7
9
|
if (window[DOM_ATTRIBUTE] !== undefined) return;
|
|
8
10
|
|
|
11
|
+
// ─── Custom Attribute Readers ───────────────────────────
|
|
12
|
+
// 'never' / '-1' mean "no staleness", everything unparsable means the default.
|
|
13
|
+
function _readStale(el, name, fallback) {
|
|
14
|
+
const raw = el.getAttribute(name);
|
|
15
|
+
if (raw === 'never' || raw === '-1') return -1;
|
|
16
|
+
const parsed = parseInt(raw, 10);
|
|
17
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ─── Attribute Contract (SSOT) ──────────────────────────
|
|
21
|
+
const ATTRIBUTES = {
|
|
22
|
+
'data-ln-data-store': { type: 'marker', effect: _markFrozen, description: 'Identifies the element as a data-store definition container' },
|
|
23
|
+
'data-ln-data-store-indexes': { type: 'list', effect: _markFrozen, description: 'Comma-separated index field names for the IndexedDB store' },
|
|
24
|
+
'data-ln-data-store-stale': { prop: '_staleThreshold', type: 'integer', read: _readStale, fallback: 300, description: 'Cache staleness threshold in seconds, or -1/never' },
|
|
25
|
+
'data-ln-data-store-search-fields': { prop: '_searchFields', type: 'list', read: attrList, description: 'Record fields to index for client-side search' },
|
|
26
|
+
'data-ln-data-store-no-local-query': { prop: 'noLocalQuery', type: 'boolean', read: attrBool, description: 'Bypasses local IndexedDB query resolution, forcing remote fetching' },
|
|
27
|
+
'data-ln-data-store-window': { prop: '_windowSize', type: 'integer', read: attrInt, fallback: 1000, min: 10, effect: _applyWindowSize, description: 'Virtual scrolling cache window size in records' },
|
|
28
|
+
'data-ln-data-store-window-page': { prop: '_windowPageSize', type: 'integer', read: attrInt, fallback: 200, min: 5, effect: _applyWindowPageSize, description: 'Virtual scrolling slice page size' },
|
|
29
|
+
'data-ln-data-store-frozen': { type: 'marker', description: 'Applied at runtime to indicate store schema is locked in IndexedDB' }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ATTR_SPEC = attrSpec(ATTRIBUTES);
|
|
33
|
+
|
|
9
34
|
const DB_NAME = 'ln_app_cache';
|
|
10
35
|
const META_STORE = '_meta';
|
|
11
36
|
const SCHEMA_VERSION = '1.0';
|
|
@@ -25,7 +50,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
25
50
|
function _getRequiredStores() {
|
|
26
51
|
const required = {};
|
|
27
52
|
for (const el of document.querySelectorAll(`[${DOM_SELECTOR}]`)) {
|
|
28
|
-
const name = el.
|
|
53
|
+
const name = el.id;
|
|
29
54
|
if (name) {
|
|
30
55
|
const indexAttr = el.getAttribute('data-ln-data-store-indexes') || '';
|
|
31
56
|
required[name] = {
|
|
@@ -134,16 +159,14 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
134
159
|
if (!encryptedPayload || !encryptedPayload.encrypted) return record;
|
|
135
160
|
|
|
136
161
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
iv: encryptedPayload.iv,
|
|
140
|
-
data: encryptedPayload.data
|
|
162
|
+
...encryptedPayload,
|
|
163
|
+
id: recordId
|
|
141
164
|
};
|
|
142
165
|
}
|
|
143
166
|
|
|
144
167
|
async function _decryptRecord(record) {
|
|
145
168
|
if (!record || !record.encrypted || !getCryptoKey()) return record;
|
|
146
|
-
return decryptData(record);
|
|
169
|
+
return decryptData(record, { silent: true });
|
|
147
170
|
}
|
|
148
171
|
|
|
149
172
|
// ─── IndexedDB CRUD Helpers ────────────────────────────
|
|
@@ -165,15 +188,59 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
165
188
|
.then(records => getCryptoKey() ? Promise.all(records.map(r => _decryptRecord(r))) : records);
|
|
166
189
|
|
|
167
190
|
const _getRecord = (storeName, id) => _tx(storeName, 'readonly')
|
|
168
|
-
.then(store =>
|
|
191
|
+
.then(store => {
|
|
192
|
+
if (!store) return null;
|
|
193
|
+
return _idbRequest(store.get(id)).then(res => {
|
|
194
|
+
if (res !== undefined) return res;
|
|
195
|
+
if (typeof id === 'string' && id.trim() !== '' && !isNaN(Number(id))) {
|
|
196
|
+
return _idbRequest(store.get(Number(id)));
|
|
197
|
+
}
|
|
198
|
+
if (typeof id === 'number') {
|
|
199
|
+
return _idbRequest(store.get(String(id)));
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
});
|
|
203
|
+
})
|
|
169
204
|
.then(record => record ? _decryptRecord(record) : null);
|
|
170
205
|
|
|
206
|
+
const _getMultipleRecords = (storeName, ids) => _getDb().then(db => {
|
|
207
|
+
if (!db) return [];
|
|
208
|
+
const tx = db.transaction(storeName, 'readonly');
|
|
209
|
+
const store = tx.objectStore(storeName);
|
|
210
|
+
const promises = ids.map(id => _idbRequest(store.get(id)).then(res => {
|
|
211
|
+
if (res !== undefined) return res;
|
|
212
|
+
if (typeof id === 'string' && id.trim() !== '' && !isNaN(Number(id))) {
|
|
213
|
+
return _idbRequest(store.get(Number(id)));
|
|
214
|
+
}
|
|
215
|
+
if (typeof id === 'number') {
|
|
216
|
+
return _idbRequest(store.get(String(id)));
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}));
|
|
220
|
+
return Promise.all(promises).then(records => {
|
|
221
|
+
if (getCryptoKey()) {
|
|
222
|
+
return Promise.all(records.map(r => r ? _decryptRecord(r) : null));
|
|
223
|
+
}
|
|
224
|
+
return records;
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
171
228
|
const _putRecord = (storeName, record) => {
|
|
172
229
|
const prepPromise = getCryptoKey() ? _encryptRecord(record) : Promise.resolve(record);
|
|
173
230
|
return prepPromise.then(prepped => _tx(storeName, 'readwrite').then(store => store ? _idbRequest(store.put(prepped)) : null));
|
|
174
231
|
};
|
|
175
232
|
|
|
176
|
-
const _deleteRecord = (storeName, id) => _tx(storeName, 'readwrite').then(store =>
|
|
233
|
+
const _deleteRecord = (storeName, id) => _tx(storeName, 'readwrite').then(store => {
|
|
234
|
+
if (!store) return null;
|
|
235
|
+
return _idbRequest(store.delete(id)).then(() => {
|
|
236
|
+
if (typeof id === 'string' && id.trim() !== '' && !isNaN(Number(id))) {
|
|
237
|
+
return _idbRequest(store.delete(Number(id)));
|
|
238
|
+
}
|
|
239
|
+
if (typeof id === 'number') {
|
|
240
|
+
return _idbRequest(store.delete(String(id)));
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
177
244
|
const _clearStore = storeName => _tx(storeName, 'readwrite').then(store => store ? _idbRequest(store.clear()) : null);
|
|
178
245
|
const _countRecords = storeName => _tx(storeName, 'readonly').then(store => store ? _idbRequest(store.count()) : 0);
|
|
179
246
|
|
|
@@ -190,23 +257,45 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
190
257
|
|
|
191
258
|
function _component(dom) {
|
|
192
259
|
this.dom = dom;
|
|
193
|
-
this._name = dom.
|
|
260
|
+
this._name = dom.id;
|
|
261
|
+
if (!this._name) console.warn('[ln-data-store] missing id — the store cannot be addressed', dom);
|
|
194
262
|
|
|
195
|
-
|
|
196
|
-
const _parsed = parseInt(staleAttr, 10);
|
|
197
|
-
this._staleThreshold = (staleAttr === 'never' || staleAttr === '-1') ? -1 : (isNaN(_parsed) ? 300 : _parsed);
|
|
198
|
-
|
|
199
|
-
const searchAttr = dom.getAttribute('data-ln-data-store-search-fields') || '';
|
|
200
|
-
this._searchFields = searchAttr.split(',').map(s => s.trim()).filter(Boolean);
|
|
263
|
+
defineAttrs(this, dom, ATTR_SPEC);
|
|
201
264
|
|
|
202
265
|
this._handlers = null;
|
|
203
266
|
|
|
204
267
|
this.isLoaded = false;
|
|
268
|
+
// Two different questions that shared one flag until they were split.
|
|
269
|
+
// isLoaded: a full sync has landed, so the cache is authoritative and sync
|
|
270
|
+
// staleness may be judged against it. canServe: records are held that can
|
|
271
|
+
// answer a read. A page fetch grants the second without granting the first.
|
|
272
|
+
this.canServe = false;
|
|
205
273
|
this.isInitialized = false;
|
|
206
274
|
this.initializationError = null;
|
|
207
275
|
this.hasCache = false;
|
|
208
276
|
this.isSyncing = false;
|
|
209
277
|
this.lastSyncedAt = null;
|
|
278
|
+
this.query = { filters: {}, search: '', sort: null };
|
|
279
|
+
if (dom.hasAttribute('data-ln-data-store-window')) {
|
|
280
|
+
this._windowIndex = createWindowIndex({
|
|
281
|
+
windowSize: this._windowSize,
|
|
282
|
+
pageSize: this._windowPageSize,
|
|
283
|
+
requestPage: (offset, limit, query) => {
|
|
284
|
+
dispatch(this.dom, 'ln-data-store:request-page', {
|
|
285
|
+
store: this._name,
|
|
286
|
+
offset: offset,
|
|
287
|
+
limit: limit,
|
|
288
|
+
query: query,
|
|
289
|
+
queryGen: this._windowIndex.queryGen
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
this._windowIndex = null;
|
|
295
|
+
}
|
|
296
|
+
this.windowed = this._windowIndex !== null;
|
|
297
|
+
// Opt out of answering reads from the cache: queries wait for the server.
|
|
298
|
+
// Read live off the host — see the effects map at registration.
|
|
210
299
|
this.totalCount = 0;
|
|
211
300
|
this.presenters = null;
|
|
212
301
|
this._mutationChain = Promise.resolve();
|
|
@@ -238,6 +327,50 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
238
327
|
for (const [event, fn] of Object.entries(self._handlers)) {
|
|
239
328
|
self.dom.addEventListener(`ln-data-store:request-${event}`, fn);
|
|
240
329
|
}
|
|
330
|
+
|
|
331
|
+
self._queryHandlers = {
|
|
332
|
+
'ln-search:change': e => {
|
|
333
|
+
e.preventDefault();
|
|
334
|
+
const term = (e.detail && e.detail.term != null) ? e.detail.term : '';
|
|
335
|
+
if (term === self.query.search) return;
|
|
336
|
+
self.query.search = term;
|
|
337
|
+
_emitQueryChanged(self);
|
|
338
|
+
},
|
|
339
|
+
'ln-filter:change': e => {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
const key = e.detail && e.detail.key;
|
|
342
|
+
if (!key) return;
|
|
343
|
+
const values = (e.detail.values || []).slice();
|
|
344
|
+
// Same no-op guard the search and sort handlers carry: a reset of a
|
|
345
|
+
// filter that was never set is not a query change, and clear-all
|
|
346
|
+
// resets every filter control on the page at once.
|
|
347
|
+
const prev = self.query.filters[key];
|
|
348
|
+
const unchanged = prev
|
|
349
|
+
? (prev.length === values.length && prev.every((v, i) => v === values[i]))
|
|
350
|
+
: !values.length;
|
|
351
|
+
if (unchanged) return;
|
|
352
|
+
if (values.length) self.query.filters[key] = values;
|
|
353
|
+
else delete self.query.filters[key];
|
|
354
|
+
_emitQueryChanged(self);
|
|
355
|
+
},
|
|
356
|
+
'ln-sort:change': e => {
|
|
357
|
+
e.preventDefault();
|
|
358
|
+
const field = e.detail && e.detail.field;
|
|
359
|
+
const direction = e.detail && e.detail.direction;
|
|
360
|
+
// ln-sort emits direction 'none' to mean "sort removed" — a truthy
|
|
361
|
+
// string, so it has to be excluded explicitly or it travels to the
|
|
362
|
+
// server as sort_dir=none.
|
|
363
|
+
const next = (direction && direction !== 'none') ? { field, direction } : null;
|
|
364
|
+
const prev = self.query.sort;
|
|
365
|
+
const unchanged = (!prev && !next) || (prev && next && prev.field === next.field && prev.direction === next.direction);
|
|
366
|
+
if (unchanged) return;
|
|
367
|
+
self.query.sort = next;
|
|
368
|
+
_emitQueryChanged(self);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
for (const [event, fn] of Object.entries(self._queryHandlers)) {
|
|
372
|
+
self.dom.addEventListener(event, fn);
|
|
373
|
+
}
|
|
241
374
|
}
|
|
242
375
|
|
|
243
376
|
function _queueMutation(self, action, detail, operation) {
|
|
@@ -252,16 +385,22 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
252
385
|
return self._mutationChain;
|
|
253
386
|
}
|
|
254
387
|
|
|
255
|
-
function _persistMutationMeta(self) {
|
|
388
|
+
function _persistMutationMeta(self, deltaCount = 0) {
|
|
256
389
|
return _countRecords(self._name).then(count => {
|
|
257
|
-
self.
|
|
390
|
+
if (self._windowIndex || self.windowed) {
|
|
391
|
+
const current = self.totalCount != null ? self.totalCount : count;
|
|
392
|
+
self.totalCount = Math.max(0, current + deltaCount);
|
|
393
|
+
} else {
|
|
394
|
+
self.totalCount = count;
|
|
395
|
+
}
|
|
258
396
|
self.hasCache = true;
|
|
259
397
|
self.isLoaded = true;
|
|
398
|
+
self.canServe = true;
|
|
260
399
|
return _setMeta(self._name, {
|
|
261
400
|
schema_version: SCHEMA_VERSION,
|
|
262
401
|
last_synced_at: self.lastSyncedAt,
|
|
263
402
|
has_cache: true,
|
|
264
|
-
record_count:
|
|
403
|
+
record_count: self.totalCount
|
|
265
404
|
});
|
|
266
405
|
});
|
|
267
406
|
}
|
|
@@ -271,7 +410,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
271
410
|
function _handleCreateRequest(self, { tempId, data = {}, requestId } = {}) {
|
|
272
411
|
const record = { ...data, id: tempId };
|
|
273
412
|
|
|
274
|
-
return _putRecord(self._name, record).then(() => _persistMutationMeta(self)).then(() => {
|
|
413
|
+
return _putRecord(self._name, record).then(() => _persistMutationMeta(self, 1)).then(() => {
|
|
275
414
|
dispatch(self.dom, 'ln-data-store:created', { store: self._name, record, tempId, requestId });
|
|
276
415
|
});
|
|
277
416
|
}
|
|
@@ -280,16 +419,17 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
280
419
|
return _getRecord(self._name, id).then(existing => {
|
|
281
420
|
if (!existing) throw new Error(`Record not found: ${id}`);
|
|
282
421
|
|
|
283
|
-
const
|
|
422
|
+
const canonicalId = existing.id;
|
|
423
|
+
const updated = { ...existing, ...data, id: canonicalId };
|
|
284
424
|
const newId = data.id;
|
|
285
|
-
const isRekey = newId !== undefined && newId !==
|
|
425
|
+
const isRekey = newId !== undefined && newId !== canonicalId;
|
|
286
426
|
|
|
287
427
|
const write = isRekey
|
|
288
|
-
? _rekeyRecord(self._name,
|
|
428
|
+
? _rekeyRecord(self._name, canonicalId, { ...updated, id: newId })
|
|
289
429
|
: _putRecord(self._name, updated);
|
|
290
430
|
|
|
291
|
-
return write.then(() => _persistMutationMeta(self)).then(() => {
|
|
292
|
-
dispatch(self.dom, 'ln-data-store:updated', { store: self._name, record: updated, previous: existing, requestId });
|
|
431
|
+
return write.then(() => _persistMutationMeta(self, 0)).then(() => {
|
|
432
|
+
dispatch(self.dom, 'ln-data-store:updated', { store: self._name, record: isRekey ? { ...updated, id: newId } : updated, previous: existing, requestId });
|
|
293
433
|
});
|
|
294
434
|
});
|
|
295
435
|
}
|
|
@@ -301,8 +441,9 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
301
441
|
return;
|
|
302
442
|
}
|
|
303
443
|
|
|
304
|
-
|
|
305
|
-
|
|
444
|
+
const canonicalId = existing.id;
|
|
445
|
+
return _deleteRecord(self._name, canonicalId).then(() => _persistMutationMeta(self, -1)).then(() => {
|
|
446
|
+
dispatch(self.dom, 'ln-data-store:deleted', { store: self._name, id: canonicalId, requestId });
|
|
306
447
|
});
|
|
307
448
|
});
|
|
308
449
|
}
|
|
@@ -316,7 +457,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
316
457
|
return Promise.all(ids.map(id => _getRecord(self._name, id))).then(records => {
|
|
317
458
|
const savedIds = records.filter(Boolean).map(r => r.id);
|
|
318
459
|
|
|
319
|
-
return _deleteBulk(self._name, savedIds).then(() => _persistMutationMeta(self)).then(() => {
|
|
460
|
+
return _deleteBulk(self._name, savedIds).then(() => _persistMutationMeta(self, -savedIds.length)).then(() => {
|
|
320
461
|
dispatch(self.dom, 'ln-data-store:deleted', { store: self._name, ids: savedIds, requestId });
|
|
321
462
|
});
|
|
322
463
|
});
|
|
@@ -347,6 +488,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
347
488
|
|
|
348
489
|
if (self.hasCache) {
|
|
349
490
|
self.isLoaded = true;
|
|
491
|
+
self.canServe = true;
|
|
350
492
|
dispatch(self.dom, 'ln-data-store:ready', { store: self._name, count: self.totalCount, source: 'cache' });
|
|
351
493
|
}
|
|
352
494
|
|
|
@@ -368,6 +510,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
368
510
|
}).catch(error => {
|
|
369
511
|
self.isInitialized = true;
|
|
370
512
|
self.isLoaded = false;
|
|
513
|
+
self.canServe = false;
|
|
371
514
|
self.hasCache = false;
|
|
372
515
|
self.isSyncing = false;
|
|
373
516
|
self.initializationError = error;
|
|
@@ -412,7 +555,14 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
412
555
|
return new Promise((resolve, reject) => {
|
|
413
556
|
const tx = db.transaction(storeName, 'readwrite');
|
|
414
557
|
const store = tx.objectStore(storeName);
|
|
415
|
-
ids.forEach(id =>
|
|
558
|
+
ids.forEach(id => {
|
|
559
|
+
store.delete(id);
|
|
560
|
+
if (typeof id === 'string' && id.trim() !== '' && !isNaN(Number(id))) {
|
|
561
|
+
store.delete(Number(id));
|
|
562
|
+
} else if (typeof id === 'number') {
|
|
563
|
+
store.delete(String(id));
|
|
564
|
+
}
|
|
565
|
+
});
|
|
416
566
|
tx.oncomplete = () => resolve();
|
|
417
567
|
tx.onerror = () => reject(tx.error);
|
|
418
568
|
});
|
|
@@ -434,104 +584,182 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
434
584
|
}));
|
|
435
585
|
}
|
|
436
586
|
|
|
437
|
-
// ─── Query Engine (In-Memory over Cache) ───────────────
|
|
438
|
-
|
|
439
587
|
const _collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
|
440
588
|
|
|
441
|
-
function
|
|
442
|
-
if (!
|
|
443
|
-
|
|
444
|
-
const desc = direction === 'desc';
|
|
445
|
-
|
|
446
|
-
return [...records].sort((a, b) => {
|
|
447
|
-
const va = a[field];
|
|
448
|
-
const vb = b[field];
|
|
449
|
-
|
|
450
|
-
if (va == null && vb == null) return 0;
|
|
451
|
-
if (va == null) return desc ? 1 : -1;
|
|
452
|
-
if (vb == null) return desc ? -1 : 1;
|
|
453
|
-
|
|
454
|
-
const result = (typeof va === 'string' && typeof vb === 'string')
|
|
455
|
-
? _collator.compare(va, vb)
|
|
456
|
-
: (va < vb ? -1 : va > vb ? 1 : 0);
|
|
457
|
-
|
|
458
|
-
return desc ? -result : result;
|
|
459
|
-
});
|
|
589
|
+
function _filterKeys(filters) {
|
|
590
|
+
if (!filters) return [];
|
|
591
|
+
return Object.keys(filters).filter(k => Array.isArray(filters[k]) && filters[k].length > 0);
|
|
460
592
|
}
|
|
461
593
|
|
|
462
|
-
function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (!keys.length) return records;
|
|
594
|
+
function _matchesFilters(record, keys, filters) {
|
|
595
|
+
return keys.every(field => filters[field].map(String).includes(String(record[field])));
|
|
596
|
+
}
|
|
466
597
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
);
|
|
598
|
+
function _tokenize(query) {
|
|
599
|
+
return String(query || '').toLowerCase().split(/\s+/).filter(Boolean);
|
|
470
600
|
}
|
|
471
601
|
|
|
472
|
-
function
|
|
473
|
-
|
|
474
|
-
const lower = query.toLowerCase();
|
|
475
|
-
return records.filter(record =>
|
|
602
|
+
function _matchesTokens(record, tokens, searchFields) {
|
|
603
|
+
return tokens.every(token =>
|
|
476
604
|
searchFields.some(field => {
|
|
477
605
|
const val = record[field];
|
|
478
|
-
return val != null && String(val).toLowerCase().includes(
|
|
606
|
+
return val != null && String(val).toLowerCase().includes(token);
|
|
479
607
|
})
|
|
480
608
|
);
|
|
481
609
|
}
|
|
482
610
|
|
|
483
611
|
function _aggregate(records, field, fn) {
|
|
484
|
-
|
|
485
|
-
|
|
612
|
+
return aggregateRecords(records, field, fn);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function _decorate(self, records) {
|
|
616
|
+
return decorateRecords(records, self.presenters && self.presenters.computed);
|
|
617
|
+
}
|
|
486
618
|
|
|
487
|
-
|
|
488
|
-
const sum = numbers.reduce((a, b) => a + b, 0);
|
|
619
|
+
// ─── Public CRUD & Sync APIs ───────────────────────────
|
|
489
620
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
621
|
+
// ─── Query vs Paging ───────────────────────────────────
|
|
622
|
+
//
|
|
623
|
+
// Two separate concerns, deliberately kept apart. _queryLocal answers "which
|
|
624
|
+
// records match, in what order" over the records this store actually holds.
|
|
625
|
+
// _positionalPage answers "which record sits at logical row N" using the
|
|
626
|
+
// server's ordering held in _windowIndex. A windowed store needs both; it must
|
|
627
|
+
// not let the second stand in for the first.
|
|
628
|
+
|
|
629
|
+
// Walks in primary-key order collecting matches and stops the moment the
|
|
630
|
+
// requested slice is full, so the cost tracks the viewport instead of the store
|
|
631
|
+
// (31ms vs 117ms for a 200-row slice out of 10k records).
|
|
632
|
+
//
|
|
633
|
+
// Legal only when the cursor's order already IS the output order, otherwise a
|
|
634
|
+
// match past the cut could belong ahead of one inside it — so an active sort
|
|
635
|
+
// disqualifies it. An encrypted store disqualifies it too: decryption is async
|
|
636
|
+
// and an await inside the cursor callback lets the transaction close.
|
|
637
|
+
//
|
|
638
|
+
// When a full pass is unavoidable the getAll path is measurably cheaper than a
|
|
639
|
+
// cursor (117ms vs 232ms over 10k), so that case is not routed here.
|
|
640
|
+
function _canScanEarly(options) {
|
|
641
|
+
return !options.sort && !getCryptoKey();
|
|
493
642
|
}
|
|
494
643
|
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
644
|
+
function _scanLocal(self, options, need) {
|
|
645
|
+
const keys = _filterKeys(options.filters);
|
|
646
|
+
const tokens = options.search ? _tokenize(options.search) : [];
|
|
647
|
+
const searchFields = self._searchFields;
|
|
648
|
+
const useSearch = tokens.length > 0 && searchFields && searchFields.length > 0;
|
|
649
|
+
|
|
650
|
+
return _tx(self._name, 'readonly').then(store => {
|
|
651
|
+
if (!store) return [];
|
|
652
|
+
return new Promise((resolve, reject) => {
|
|
653
|
+
const found = [];
|
|
654
|
+
const request = store.openCursor();
|
|
655
|
+
request.onsuccess = () => {
|
|
656
|
+
const cursor = request.result;
|
|
657
|
+
if (!cursor || found.length >= need) {
|
|
658
|
+
resolve(found);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const record = cursor.value;
|
|
662
|
+
const passes = (!keys.length || _matchesFilters(record, keys, options.filters))
|
|
663
|
+
&& (!useSearch || _matchesTokens(record, tokens, searchFields));
|
|
664
|
+
if (passes) found.push(record);
|
|
665
|
+
cursor.continue();
|
|
666
|
+
};
|
|
667
|
+
request.onerror = () => reject(request.error);
|
|
668
|
+
});
|
|
508
669
|
});
|
|
509
670
|
}
|
|
510
671
|
|
|
511
|
-
|
|
672
|
+
function _queryLocal(self, records, options) {
|
|
673
|
+
return queryRecords(records, options, self._searchFields, _collator);
|
|
674
|
+
}
|
|
512
675
|
|
|
513
|
-
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
const
|
|
676
|
+
function _positionalPage(self, offset, limit) {
|
|
677
|
+
const ids = [];
|
|
678
|
+
for (let i = offset; i < offset + limit; i++) {
|
|
679
|
+
const id = self._windowIndex.getId(i);
|
|
680
|
+
ids.push(id);
|
|
681
|
+
}
|
|
517
682
|
|
|
518
|
-
|
|
519
|
-
if (options.search) records = _search(records, options.search, self._searchFields);
|
|
683
|
+
const uniqueResolvedIds = Array.from(new Set(ids.filter(id => id !== undefined)));
|
|
520
684
|
|
|
521
|
-
|
|
685
|
+
return _getMultipleRecords(self._name, uniqueResolvedIds).then(records => {
|
|
686
|
+
const recordMap = new Map();
|
|
687
|
+
for (let i = 0; i < records.length; i++) {
|
|
688
|
+
const rec = records[i];
|
|
689
|
+
if (rec) {
|
|
690
|
+
recordMap.set(String(rec.id), rec);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const data = [];
|
|
694
|
+
for (let i = 0; i < ids.length; i++) {
|
|
695
|
+
const id = ids[i];
|
|
696
|
+
if (id === undefined) {
|
|
697
|
+
data.push(null);
|
|
698
|
+
} else {
|
|
699
|
+
const rec = recordMap.get(String(id));
|
|
700
|
+
data.push(rec || null);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
data: _decorate(self, data),
|
|
705
|
+
total: self._windowIndex.grandTotal,
|
|
706
|
+
filtered: self._windowIndex.logicalTotal,
|
|
707
|
+
offset: offset,
|
|
708
|
+
queryGen: self._windowIndex.queryGen
|
|
709
|
+
};
|
|
710
|
+
});
|
|
711
|
+
}
|
|
522
712
|
|
|
523
|
-
|
|
713
|
+
_component.prototype.getAll = function (options = {}) {
|
|
714
|
+
const self = this;
|
|
715
|
+
if (self._windowIndex) {
|
|
716
|
+
const offset = options.offset || 0;
|
|
717
|
+
const limit = options.limit || 200;
|
|
718
|
+
self._windowIndex.ensure(offset, offset + limit, options);
|
|
719
|
+
|
|
720
|
+
// The index holds no ordering for this query — it was reset the moment
|
|
721
|
+
// the query changed. Until the server's first page lands the positions
|
|
722
|
+
// are ours to assign, so the query is answered from the records already
|
|
723
|
+
// held and flagged provisional; the server's answer supersedes it at the
|
|
724
|
+
// next generation. Once the index HAS loaded its ordering is authoritative
|
|
725
|
+
// and a page still missing stays a placeholder — splicing local records
|
|
726
|
+
// into server positions would put the wrong record on the row.
|
|
727
|
+
// Told to leave queries to the server: report the window as unresolved and
|
|
728
|
+
// let the view hold the previous generation until the server answers. The
|
|
729
|
+
// positional page below stays available either way — those rows are the
|
|
730
|
+
// server's own answer, only materialised from cache.
|
|
731
|
+
if (!self._windowIndex.hasLoaded && !self.noLocalQuery) {
|
|
732
|
+
const need = offset + limit;
|
|
733
|
+
// Rows only, never totals — see window-cache.ingest. Nothing to show
|
|
734
|
+
// (empty store, or nothing matched locally) is not an answer either:
|
|
735
|
+
// the positional page reports the window as unresolved and the view
|
|
736
|
+
// keeps the previous generation until the server settles it.
|
|
737
|
+
const provisional = records => records.length
|
|
738
|
+
? {
|
|
739
|
+
data: _decorate(self, records),
|
|
740
|
+
offset: offset,
|
|
741
|
+
queryGen: self._windowIndex.queryGen,
|
|
742
|
+
provisional: true
|
|
743
|
+
}
|
|
744
|
+
: _positionalPage(self, offset, limit);
|
|
524
745
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
746
|
+
if (_canScanEarly(options)) {
|
|
747
|
+
return _scanLocal(self, options, need).then(matches =>
|
|
748
|
+
provisional(matches.slice(offset, need)));
|
|
749
|
+
}
|
|
750
|
+
return _getAllRecords(self._name).then(records =>
|
|
751
|
+
provisional(_queryLocal(self, records, options).records));
|
|
529
752
|
}
|
|
530
753
|
|
|
754
|
+
return _positionalPage(self, offset, limit);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return _getAllRecords(self._name).then(records => {
|
|
758
|
+
const r = _queryLocal(self, records, options);
|
|
531
759
|
return {
|
|
532
|
-
data: _decorate(self, records),
|
|
533
|
-
total,
|
|
534
|
-
filtered
|
|
760
|
+
data: _decorate(self, r.records),
|
|
761
|
+
total: r.total,
|
|
762
|
+
filtered: r.filtered
|
|
535
763
|
};
|
|
536
764
|
});
|
|
537
765
|
};
|
|
@@ -541,9 +769,12 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
541
769
|
};
|
|
542
770
|
|
|
543
771
|
_component.prototype.count = function (filters) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
772
|
+
const hasFilters = filters && Object.keys(filters).length > 0;
|
|
773
|
+
if (!hasFilters) {
|
|
774
|
+
if (this.totalCount != null) return Promise.resolve(this.totalCount);
|
|
775
|
+
return _countRecords(this._name);
|
|
776
|
+
}
|
|
777
|
+
return _getAllRecords(this._name).then(records => filterRecords(records, filters).length);
|
|
547
778
|
};
|
|
548
779
|
|
|
549
780
|
_component.prototype.aggregate = function (field, fn) {
|
|
@@ -559,12 +790,25 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
559
790
|
_component.prototype.applySync = function (upsertedRecords, deletedIds, syncedAt, meta) {
|
|
560
791
|
meta = meta || {};
|
|
561
792
|
const self = this;
|
|
793
|
+
if (self._windowIndex && meta.queryGen != null && meta.queryGen !== self._windowIndex.queryGen) {
|
|
794
|
+
return Promise.resolve();
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const hasChanges = upsertedRecords.length > 0 || deletedIds.length > 0;
|
|
562
798
|
|
|
563
799
|
let chain = Promise.resolve();
|
|
564
800
|
if (upsertedRecords.length > 0) chain = chain.then(() => _putBulk(self._name, upsertedRecords));
|
|
565
801
|
if (deletedIds.length > 0) chain = chain.then(() => _deleteBulk(self._name, deletedIds));
|
|
566
802
|
|
|
567
|
-
return chain.then(() =>
|
|
803
|
+
return chain.then(() => {
|
|
804
|
+
if (self._windowIndex && (meta.offset != null || meta.total != null)) {
|
|
805
|
+
const offset = meta.offset != null ? meta.offset : 0;
|
|
806
|
+
const ids = upsertedRecords.map(r => r.id);
|
|
807
|
+
// Residency is the index: what the window pushed out stops being held.
|
|
808
|
+
const evicted = self._windowIndex.ingest(offset, ids, meta.total, meta.filtered, meta.queryGen);
|
|
809
|
+
if (evicted && evicted.length) return _deleteBulk(self._name, evicted);
|
|
810
|
+
}
|
|
811
|
+
}).then(() => _countRecords(self._name)).then(count => {
|
|
568
812
|
self.totalCount = meta.total !== undefined ? meta.total : count;
|
|
569
813
|
self.hasCache = true;
|
|
570
814
|
return _setMeta(self._name, {
|
|
@@ -576,6 +820,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
576
820
|
}).then(() => {
|
|
577
821
|
const isInitialLoad = !self.isLoaded;
|
|
578
822
|
self.isLoaded = true;
|
|
823
|
+
self.canServe = true;
|
|
579
824
|
self.isSyncing = false;
|
|
580
825
|
self.lastSyncedAt = syncedAt;
|
|
581
826
|
|
|
@@ -608,8 +853,10 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
608
853
|
|
|
609
854
|
return chain.then(() => _countRecords(self._name)).then(count => {
|
|
610
855
|
self.totalCount = meta.total !== undefined ? meta.total : count;
|
|
611
|
-
// record_count/has_cache stay untouched — a page fetch is not an
|
|
856
|
+
// record_count/has_cache/isLoaded stay untouched — a page fetch is not an
|
|
612
857
|
// authoritative cache; it would corrupt _isStale/storeInitialized sync gating.
|
|
858
|
+
// It does leave records behind, so the store can serve reads from here on.
|
|
859
|
+
if (upsertedRecords.length > 0) self.canServe = true;
|
|
613
860
|
return _decorate(self, upsertedRecords);
|
|
614
861
|
}).catch(err => {
|
|
615
862
|
console.error('[ln-data-store] applyQuery failed:', err);
|
|
@@ -641,17 +888,27 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
641
888
|
};
|
|
642
889
|
|
|
643
890
|
_component.prototype.destroy = function () {
|
|
891
|
+
if (this._windowIndex) {
|
|
892
|
+
this._windowIndex.clear();
|
|
893
|
+
this._windowIndex = null;
|
|
894
|
+
this.windowed = false;
|
|
895
|
+
}
|
|
644
896
|
if (this._handlers) {
|
|
645
897
|
for (const [event, fn] of Object.entries(this._handlers)) {
|
|
646
898
|
this.dom.removeEventListener(`ln-data-store:request-${event}`, fn);
|
|
647
899
|
}
|
|
648
900
|
this._handlers = null;
|
|
649
901
|
}
|
|
902
|
+
if (this._queryHandlers) {
|
|
903
|
+
for (const [event, fn] of Object.entries(this._queryHandlers)) {
|
|
904
|
+
this.dom.removeEventListener(event, fn);
|
|
905
|
+
}
|
|
906
|
+
this._queryHandlers = null;
|
|
907
|
+
}
|
|
650
908
|
|
|
651
909
|
delete _stores[this._name];
|
|
652
910
|
|
|
653
911
|
delete this.dom[DOM_ATTRIBUTE];
|
|
654
|
-
dispatch(this.dom, 'ln-data-store:destroyed', { store: this._name });
|
|
655
912
|
};
|
|
656
913
|
|
|
657
914
|
// ─── clearAll (global) ─────────────────────────────────
|
|
@@ -669,6 +926,7 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
669
926
|
}).then(() => {
|
|
670
927
|
Object.values(_stores).forEach(inst => {
|
|
671
928
|
inst.isLoaded = false;
|
|
929
|
+
inst.canServe = false;
|
|
672
930
|
inst.isInitialized = false;
|
|
673
931
|
inst.initializationError = null;
|
|
674
932
|
inst.hasCache = false;
|
|
@@ -679,9 +937,53 @@ import { registerComponent, dispatch, setCryptoKey, getCryptoKey, encryptData, d
|
|
|
679
937
|
});
|
|
680
938
|
}
|
|
681
939
|
|
|
940
|
+
function _emitQueryChanged(self) {
|
|
941
|
+
if (self._windowIndex) {
|
|
942
|
+
self._windowIndex.reset();
|
|
943
|
+
}
|
|
944
|
+
dispatch(self.dom, 'ln-data-store:query-changed', {
|
|
945
|
+
store: self._name,
|
|
946
|
+
query: {
|
|
947
|
+
filters: Object.assign({}, self.query.filters),
|
|
948
|
+
search: self.query.search,
|
|
949
|
+
sort: self.query.sort ? Object.assign({}, self.query.sort) : null
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
|
|
682
954
|
// ─── Registration ──────────────────────────────────────
|
|
683
955
|
|
|
684
|
-
|
|
956
|
+
const FROZEN_ATTR = 'data-ln-data-store-frozen';
|
|
957
|
+
|
|
958
|
+
// The IndexedDB schema and the store's registered name are fixed at open
|
|
959
|
+
// time: createIndex is only legal inside a versionchange transaction
|
|
960
|
+
// (see onupgradeneeded, :85-94). A post-init edit is recorded on the host
|
|
961
|
+
// for the dev stylesheet to surface; nothing is applied.
|
|
962
|
+
function _markFrozen(el, attrName) {
|
|
963
|
+
el.setAttribute(FROZEN_ATTR, attrName);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Shrinking the window drops positions; the records behind them stop being
|
|
967
|
+
// held, exactly as on ingest (:757-758), so they leave IndexedDB too.
|
|
968
|
+
function _applyWindowSize(el) {
|
|
969
|
+
const inst = el[DOM_ATTRIBUTE];
|
|
970
|
+
if (!inst._windowIndex) return;
|
|
971
|
+
const evicted = inst._windowIndex.configure({ windowSize: inst._windowSize });
|
|
972
|
+
if (evicted.length) {
|
|
973
|
+
_deleteBulk(inst._name, evicted).catch(err => {
|
|
974
|
+
console.error('[ln-data-store] window shrink eviction failed:', err);
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function _applyWindowPageSize(el) {
|
|
980
|
+
const inst = el[DOM_ATTRIBUTE];
|
|
981
|
+
if (inst._windowIndex) inst._windowIndex.configure({ pageSize: inst._windowPageSize });
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-data-store', {
|
|
985
|
+
attributes: ATTRIBUTES
|
|
986
|
+
});
|
|
685
987
|
|
|
686
988
|
window[DOM_ATTRIBUTE].clearAll = _clearAll;
|
|
687
989
|
window[DOM_ATTRIBUTE].init = window[DOM_ATTRIBUTE];
|