@livenetworks/ashlar 1.6.1 → 2.0.1
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 +190 -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/{js → components}/ln-confirm/README.md +69 -18
- 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 -171
- 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/_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
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// ─── Typed Live Attribute Readers ──────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Scanner constraint: attribute names passed into these readers, and into a
|
|
4
|
+
// `defineAttrs` spec, must always be inline string literals or a `const`
|
|
5
|
+
// bound to a literal — never concatenated or templated.
|
|
6
|
+
// `scripts/sync-ln-schemas.mjs:22-23` is a raw regex over file text; a
|
|
7
|
+
// constructed name is invisible to it and the CI docs gate breaks silently
|
|
8
|
+
// (this already happened once — commit `3495690`).
|
|
9
|
+
|
|
10
|
+
export function attrStr(el, name, fallback) {
|
|
11
|
+
const raw = el.getAttribute(name);
|
|
12
|
+
return raw === null ? fallback : raw;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// `0` stays `0` — unlike the `parseInt(...) || 1000` idiom this replaces.
|
|
16
|
+
export function attrInt(el, name, fallback) {
|
|
17
|
+
const parsed = parseInt(el.getAttribute(name), 10);
|
|
18
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function attrBool(el, name, fallback = false) {
|
|
22
|
+
const raw = el.getAttribute(name);
|
|
23
|
+
if (raw === null) return !!fallback;
|
|
24
|
+
const val = raw.trim().toLowerCase();
|
|
25
|
+
if (val === 'false' || val === '0') return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Allocates a new array per read — do not call inside a per-row loop.
|
|
30
|
+
export function attrList(el, name) {
|
|
31
|
+
return (el.getAttribute(name) || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const _enumReaderCache = new Map();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Cached reader factory for enum attributes.
|
|
38
|
+
* @param {string[]} values - allowed values
|
|
39
|
+
* @param {*} fallback - default value
|
|
40
|
+
* @returns {function(Element, string): *}
|
|
41
|
+
*/
|
|
42
|
+
export function attrEnum(values, fallback) {
|
|
43
|
+
const key = (values ? values.join('|') : '') + '::' + fallback;
|
|
44
|
+
if (_enumReaderCache.has(key)) return _enumReaderCache.get(key);
|
|
45
|
+
const valSet = new Set(values || []);
|
|
46
|
+
const reader = function (el, name) {
|
|
47
|
+
const raw = el.getAttribute(name);
|
|
48
|
+
return (raw !== null && valSet.has(raw)) ? raw : fallback;
|
|
49
|
+
};
|
|
50
|
+
_enumReaderCache.set(key, reader);
|
|
51
|
+
return reader;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function attrFloat(el, name, fallback) {
|
|
55
|
+
const parsed = parseFloat(el.getAttribute(name));
|
|
56
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function attrJson(el, name, fallback) {
|
|
60
|
+
const raw = el.getAttribute(name);
|
|
61
|
+
if (!raw) return fallback;
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(raw);
|
|
64
|
+
} catch (_) {
|
|
65
|
+
return fallback;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validates a DOM attribute value against its declared schema entry.
|
|
71
|
+
* Logs a console.warn (with [componentTag] prefix) if the value is invalid.
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} entry - { type, values, min, max, fallback, ... }
|
|
74
|
+
* @param {string|null} value - raw DOM attribute value (null if absent)
|
|
75
|
+
* @param {string} attrName - attribute name (e.g. 'data-ln-modal')
|
|
76
|
+
* @param {string} componentTag - component tag (e.g. 'ln-modal')
|
|
77
|
+
* @param {Element} [el] - optional DOM element carrying the attribute
|
|
78
|
+
* @returns {boolean} true if valid, false if invalid
|
|
79
|
+
*/
|
|
80
|
+
export function validateAttrValue(entry, value, attrName, componentTag, el) {
|
|
81
|
+
if (!entry || value === null) return true;
|
|
82
|
+
const tag = componentTag || 'ln-component';
|
|
83
|
+
const type = entry.type;
|
|
84
|
+
|
|
85
|
+
// valueless / unconstrained types
|
|
86
|
+
if (type === 'trigger' || type === 'marker' || type === 'string' || type === 'list' || !type) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Bare attributes (presence-only in HTML) with a declared fallback resolve to fallback in readers
|
|
91
|
+
if (value === '' && entry.fallback !== undefined) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const logWarn = (msg) => {
|
|
96
|
+
if (el) console.warn(msg, el);
|
|
97
|
+
else console.warn(msg);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (type === 'boolean') {
|
|
101
|
+
const lower = value.trim().toLowerCase();
|
|
102
|
+
if (lower !== '' && lower !== 'true' && lower !== 'false' && lower !== '1' && lower !== '0') {
|
|
103
|
+
logWarn(`[${tag}] Invalid value "${value}" for boolean attribute "${attrName}". Allowed: "true", "false", or presence-only.`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (type === 'enum') {
|
|
110
|
+
const allowed = entry.values || [];
|
|
111
|
+
if (!allowed.includes(value)) {
|
|
112
|
+
logWarn(`[${tag}] Invalid value "${value}" for attribute "${attrName}". Allowed: ${allowed.join(', ')}. Fallback: "${entry.fallback}".`);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (type === 'integer') {
|
|
119
|
+
if (!/^-?\d+$/.test(value)) {
|
|
120
|
+
logWarn(`[${tag}] Invalid integer "${value}" for attribute "${attrName}". Fallback: ${entry.fallback}.`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const num = parseInt(value, 10);
|
|
124
|
+
if (entry.min !== undefined && num < entry.min) {
|
|
125
|
+
logWarn(`[${tag}] Value ${num} for attribute "${attrName}" is less than min (${entry.min}).`);
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (entry.max !== undefined && num > entry.max) {
|
|
129
|
+
logWarn(`[${tag}] Value ${num} for attribute "${attrName}" is greater than max (${entry.max}).`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (type === 'float') {
|
|
136
|
+
if (isNaN(Number(value))) {
|
|
137
|
+
logWarn(`[${tag}] Invalid float "${value}" for attribute "${attrName}". Fallback: ${entry.fallback}.`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (type === 'json') {
|
|
144
|
+
try {
|
|
145
|
+
JSON.parse(value);
|
|
146
|
+
return true;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
logWarn(`[${tag}] Invalid JSON for attribute "${attrName}": ${err.message}. Fallback: ${entry.fallback}.`);
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Define live getter-only properties on `instance`, each reading `dom` on
|
|
158
|
+
* every access. Nothing is copied into instance state, so there is no state
|
|
159
|
+
* to invalidate and no sync step.
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} instance
|
|
162
|
+
* @param {Element} dom
|
|
163
|
+
* @param {Object} spec - { propName: [reader, attributeName, fallback] }
|
|
164
|
+
* @returns {Object} instance
|
|
165
|
+
*/
|
|
166
|
+
export function defineAttrs(instance, dom, spec) {
|
|
167
|
+
for (const propName in spec) {
|
|
168
|
+
const [reader, attributeName, fallback] = spec[propName];
|
|
169
|
+
Object.defineProperty(instance, propName, {
|
|
170
|
+
// Getter only, no setter — assignment throws in strict mode (ES
|
|
171
|
+
// modules are strict). Deliberate: it forbids a drifting copy.
|
|
172
|
+
get: function () {
|
|
173
|
+
return reader(dom, attributeName, fallback);
|
|
174
|
+
},
|
|
175
|
+
enumerable: true,
|
|
176
|
+
configurable: true
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return instance;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* One table per component is the single source of truth for its HOST
|
|
184
|
+
* attributes. Derivations below read it; nothing else declares an
|
|
185
|
+
* attribute name.
|
|
186
|
+
*
|
|
187
|
+
* Table entry shape:
|
|
188
|
+
* 'data-ln-x': { prop, read, type, values, fallback, min, max, effect, description } — any subset
|
|
189
|
+
*
|
|
190
|
+
* The scanner constraint at the top of this file applies to table KEYS:
|
|
191
|
+
* literal strings only. A computed key is invisible to
|
|
192
|
+
* scripts/sync-ln-schemas.mjs.
|
|
193
|
+
*/
|
|
194
|
+
export function attrSpec(table) {
|
|
195
|
+
const spec = {};
|
|
196
|
+
for (const name in table) {
|
|
197
|
+
const entry = table[name];
|
|
198
|
+
if (!entry || !entry.prop) continue;
|
|
199
|
+
let reader = entry.read;
|
|
200
|
+
if (!reader && entry.type) {
|
|
201
|
+
switch (entry.type) {
|
|
202
|
+
case 'enum':
|
|
203
|
+
reader = attrEnum(entry.values, entry.fallback);
|
|
204
|
+
break;
|
|
205
|
+
case 'integer':
|
|
206
|
+
reader = attrInt;
|
|
207
|
+
break;
|
|
208
|
+
case 'float':
|
|
209
|
+
reader = attrFloat;
|
|
210
|
+
break;
|
|
211
|
+
case 'boolean':
|
|
212
|
+
reader = attrBool;
|
|
213
|
+
break;
|
|
214
|
+
case 'list':
|
|
215
|
+
reader = attrList;
|
|
216
|
+
break;
|
|
217
|
+
case 'json':
|
|
218
|
+
reader = attrJson;
|
|
219
|
+
break;
|
|
220
|
+
case 'string':
|
|
221
|
+
default:
|
|
222
|
+
reader = attrStr;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!reader) reader = attrStr;
|
|
227
|
+
spec[entry.prop] = [reader, name, entry.fallback];
|
|
228
|
+
}
|
|
229
|
+
return spec;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function attrEffects(table) {
|
|
233
|
+
const effects = {};
|
|
234
|
+
for (const name in table) {
|
|
235
|
+
const entry = table[name];
|
|
236
|
+
if (!entry) continue;
|
|
237
|
+
if (entry.effect) effects[name] = entry.effect;
|
|
238
|
+
}
|
|
239
|
+
// null, not {} — `_registerAttrEntry` tests `entry.onAttrChange || entry.effects`,
|
|
240
|
+
// and an empty object is truthy. A table of pure getters would otherwise be
|
|
241
|
+
// pushed into `registry.reactive` and iterated on every data-ln-* mutation
|
|
242
|
+
// on the page while having nothing to run.
|
|
243
|
+
return Object.keys(effects).length ? effects : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The attribute names a registry entry should be indexed under, so the shared
|
|
248
|
+
* observer can reach it by name instead of walking every reactive entry.
|
|
249
|
+
*
|
|
250
|
+
* Note this reads `entry.effects` — the DERIVED table produced by
|
|
251
|
+
* `attrEffects` above — not the raw component table. An entry is indexed
|
|
252
|
+
* under every name it declares, which for a component like `ln-list` means
|
|
253
|
+
* the suffixed names (`data-ln-list-window`) and not only the selector.
|
|
254
|
+
*
|
|
255
|
+
* Internal to ln-core, and deliberately NOT re-exported from index.js: unlike
|
|
256
|
+
* `attrSpec` / `attrEffects`, which take the attribute table a component author
|
|
257
|
+
* writes, this takes a registry entry — a shape no author ever constructs.
|
|
258
|
+
* Publishing it would make the entry shape semver-load-bearing for no caller.
|
|
259
|
+
*
|
|
260
|
+
* @param {Object} entry - a registry entry: { effects, onAttrChange, declared }
|
|
261
|
+
* @returns {Set<string>|null} names to index under, or null for a wildcard —
|
|
262
|
+
* `onAttrChange` without `declared` asks for EVERY data-ln-* mutation and
|
|
263
|
+
* by definition cannot be keyed by name.
|
|
264
|
+
*/
|
|
265
|
+
export function reactiveNames(entry) {
|
|
266
|
+
if (entry.onAttrChange && !entry.declared) return null;
|
|
267
|
+
|
|
268
|
+
const names = new Set();
|
|
269
|
+
if (entry.effects) for (const n in entry.effects) names.add(n);
|
|
270
|
+
if (entry.onAttrChange && entry.declared) for (const n of entry.declared) names.add(n);
|
|
271
|
+
return names;
|
|
272
|
+
}
|
|
273
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the comparison type for a column/field from its current value set.
|
|
3
|
+
* Type is locked ONCE per sort operation (not per pair) — comparing types
|
|
4
|
+
* per-pair breaks comparator transitivity and produces wrong ordering.
|
|
5
|
+
* All non-empty values must be finite numbers for 'number'; otherwise 'string'.
|
|
6
|
+
* @param {Array<unknown>} values
|
|
7
|
+
* @returns {'number' | 'string'}
|
|
8
|
+
*/
|
|
9
|
+
export function detectValueType(values) {
|
|
10
|
+
let hasValue = false;
|
|
11
|
+
for (let i = 0; i < values.length; i++) {
|
|
12
|
+
const v = values[i];
|
|
13
|
+
if (v === '' || v == null) continue;
|
|
14
|
+
hasValue = true;
|
|
15
|
+
if (!Number.isFinite(Number(v))) return 'string';
|
|
16
|
+
}
|
|
17
|
+
return hasValue ? 'number' : 'string';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Compare two raw values given a pre-locked type (see detectValueType).
|
|
22
|
+
* Pass an Intl.Collator instance for locale-aware string comparison —
|
|
23
|
+
* built once by the caller outside the sort loop, never per pair.
|
|
24
|
+
* @param {unknown} a
|
|
25
|
+
* @param {unknown} b
|
|
26
|
+
* @param {'number' | 'string'} type
|
|
27
|
+
* @param {Intl.Collator|null} [collator]
|
|
28
|
+
* @returns {number}
|
|
29
|
+
*/
|
|
30
|
+
export function compareValues(a, b, type, collator) {
|
|
31
|
+
if (type === 'number') {
|
|
32
|
+
const na = parseFloat(a);
|
|
33
|
+
const nb = parseFloat(b);
|
|
34
|
+
return (isNaN(na) ? 0 : na) - (isNaN(nb) ? 0 : nb);
|
|
35
|
+
}
|
|
36
|
+
const strA = a != null ? String(a) : '';
|
|
37
|
+
const strB = b != null ? String(b) : '';
|
|
38
|
+
if (collator) return collator.compare(strA, strB);
|
|
39
|
+
return strA < strB ? -1 : strA > strB ? 1 : 0;
|
|
40
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Web Crypto API Reusable Cryptographic Helpers
|
|
2
|
+
let _cryptoKey = null;
|
|
3
|
+
|
|
4
|
+
const DEFAULT_SALT = 'ln-ashlar:storage-salt:v1';
|
|
5
|
+
const CHUNK_SIZE = 0x8000; // 32KB chunks for stack-safe Base64 conversion
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Encodes a Uint8Array to a Base64 string in chunks to prevent call-stack overflow on large payloads.
|
|
9
|
+
* @param {Uint8Array} bytes
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
export function uint8ToBase64(bytes) {
|
|
13
|
+
let binary = '';
|
|
14
|
+
const len = bytes.byteLength;
|
|
15
|
+
for (let i = 0; i < len; i += CHUNK_SIZE) {
|
|
16
|
+
binary += String.fromCharCode.apply(
|
|
17
|
+
null,
|
|
18
|
+
bytes.subarray(i, Math.min(i + CHUNK_SIZE, len))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return btoa(binary);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Decodes a Base64 string to a Uint8Array.
|
|
26
|
+
* @param {string} base64
|
|
27
|
+
* @returns {Uint8Array}
|
|
28
|
+
*/
|
|
29
|
+
export function base64ToUint8(base64) {
|
|
30
|
+
const binary = atob(base64);
|
|
31
|
+
const len = binary.length;
|
|
32
|
+
const bytes = new Uint8Array(len);
|
|
33
|
+
for (let i = 0; i < len; i++) {
|
|
34
|
+
bytes[i] = binary.charCodeAt(i);
|
|
35
|
+
}
|
|
36
|
+
return bytes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves key and options from overloaded arguments.
|
|
41
|
+
* Ensures an options object like { silent: true } is never passed to WebCrypto as a CryptoKey.
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
function _resolveKeyAndOptions(keyOrOptions, maybeOptions) {
|
|
45
|
+
let key = _cryptoKey;
|
|
46
|
+
let options = {};
|
|
47
|
+
|
|
48
|
+
if (typeof CryptoKey !== 'undefined' && keyOrOptions instanceof CryptoKey) {
|
|
49
|
+
key = keyOrOptions;
|
|
50
|
+
if (maybeOptions && typeof maybeOptions === 'object') {
|
|
51
|
+
options = maybeOptions;
|
|
52
|
+
}
|
|
53
|
+
} else if (keyOrOptions && typeof keyOrOptions === 'object') {
|
|
54
|
+
options = keyOrOptions;
|
|
55
|
+
if (typeof CryptoKey !== 'undefined' && options.key instanceof CryptoKey) {
|
|
56
|
+
key = options.key;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { key, options };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Derives a 256-bit AES-GCM CryptoKey using PBKDF2 or SHA-256.
|
|
65
|
+
* @param {string} secretString
|
|
66
|
+
* @param {Object} [options]
|
|
67
|
+
* @param {'pbkdf2'|'sha256'} [options.method='pbkdf2']
|
|
68
|
+
* @param {string|Uint8Array} [options.salt]
|
|
69
|
+
* @param {number} [options.iterations=100000]
|
|
70
|
+
* @returns {Promise<CryptoKey>}
|
|
71
|
+
*/
|
|
72
|
+
export async function deriveCryptoKey(secretString, options = {}) {
|
|
73
|
+
if (!secretString) {
|
|
74
|
+
throw new Error('[ln-crypto] Key derivation failed: Secret string is required');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const method = options.method || 'pbkdf2';
|
|
78
|
+
const enc = new TextEncoder();
|
|
79
|
+
|
|
80
|
+
if (method === 'sha256') {
|
|
81
|
+
const hash = await crypto.subtle.digest('SHA-256', enc.encode(secretString));
|
|
82
|
+
return crypto.subtle.importKey(
|
|
83
|
+
'raw',
|
|
84
|
+
hash,
|
|
85
|
+
{ name: 'AES-GCM' },
|
|
86
|
+
false,
|
|
87
|
+
['encrypt', 'decrypt']
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// PBKDF2 mode
|
|
92
|
+
const saltStr = options.salt || DEFAULT_SALT;
|
|
93
|
+
const salt = typeof saltStr === 'string' ? enc.encode(saltStr) : saltStr;
|
|
94
|
+
const iterations = options.iterations || 100000;
|
|
95
|
+
|
|
96
|
+
const baseKey = await crypto.subtle.importKey(
|
|
97
|
+
'raw',
|
|
98
|
+
enc.encode(secretString),
|
|
99
|
+
'PBKDF2',
|
|
100
|
+
false,
|
|
101
|
+
['deriveKey']
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return crypto.subtle.deriveKey(
|
|
105
|
+
{
|
|
106
|
+
name: 'PBKDF2',
|
|
107
|
+
salt,
|
|
108
|
+
iterations,
|
|
109
|
+
hash: 'SHA-256'
|
|
110
|
+
},
|
|
111
|
+
baseKey,
|
|
112
|
+
{ name: 'AES-GCM', length: 256 },
|
|
113
|
+
false,
|
|
114
|
+
['encrypt', 'decrypt']
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Sets the active cryptographic key in module memory.
|
|
120
|
+
* Default method is 'sha256' for backward-compatibility with existing at-rest storage caches.
|
|
121
|
+
* Pass { method: 'pbkdf2' } to use PBKDF2.
|
|
122
|
+
* Passing empty/falsy clears the key.
|
|
123
|
+
* @param {string|null} secretString
|
|
124
|
+
* @param {Object} [options]
|
|
125
|
+
* @returns {Promise<void>}
|
|
126
|
+
*/
|
|
127
|
+
export async function setCryptoKey(secretString, options = {}) {
|
|
128
|
+
if (!secretString) {
|
|
129
|
+
_cryptoKey = null;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// Default to sha256 when called as setCryptoKey('secret') for backward compatibility
|
|
135
|
+
const method = options.method || 'sha256';
|
|
136
|
+
_cryptoKey = await deriveCryptoKey(secretString, { ...options, method });
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error('[ln-core/crypto] Key derivation failed:', err);
|
|
139
|
+
_cryptoKey = null;
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Returns the currently active CryptoKey in module memory.
|
|
146
|
+
* @returns {CryptoKey|null}
|
|
147
|
+
*/
|
|
148
|
+
export function getCryptoKey() {
|
|
149
|
+
return _cryptoKey;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clears the active cryptographic key in module memory.
|
|
154
|
+
*/
|
|
155
|
+
export function clearCryptoKey() {
|
|
156
|
+
_cryptoKey = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Checks if an active cryptographic key is set.
|
|
161
|
+
* @returns {boolean}
|
|
162
|
+
*/
|
|
163
|
+
export function hasCryptoKey() {
|
|
164
|
+
return _cryptoKey !== null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Encrypts arbitrary data using AES-GCM (256-bit).
|
|
169
|
+
* Fail-closed: Throws an Error if no active key is provided or if encryption fails.
|
|
170
|
+
* @param {any} plainData
|
|
171
|
+
* @param {CryptoKey|Object} [keyOrOptions]
|
|
172
|
+
* @param {Object} [maybeOptions]
|
|
173
|
+
* @returns {Promise<Object>} Version 1 envelope: { v: 1, alg: 'AES-GCM', encrypted: true, iv, data }
|
|
174
|
+
*/
|
|
175
|
+
export async function encryptData(plainData, keyOrOptions, maybeOptions) {
|
|
176
|
+
const { key } = _resolveKeyAndOptions(keyOrOptions, maybeOptions);
|
|
177
|
+
|
|
178
|
+
if (plainData === undefined || plainData === null) {
|
|
179
|
+
return plainData;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!key) {
|
|
183
|
+
throw new Error('[ln-crypto] Encryption failed: No active cryptographic key provided');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const enc = new TextEncoder();
|
|
188
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
189
|
+
|
|
190
|
+
const serialized = typeof plainData === 'string' ? plainData : JSON.stringify(plainData);
|
|
191
|
+
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
192
|
+
{ name: 'AES-GCM', iv },
|
|
193
|
+
key,
|
|
194
|
+
enc.encode(serialized)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
v: 1,
|
|
199
|
+
alg: 'AES-GCM',
|
|
200
|
+
encrypted: true,
|
|
201
|
+
iv: uint8ToBase64(iv),
|
|
202
|
+
data: uint8ToBase64(new Uint8Array(encryptedBuffer))
|
|
203
|
+
};
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error('[ln-core/crypto] Encryption failed:', err);
|
|
206
|
+
throw new Error('[ln-crypto] Encryption failed: ' + (err && err.message ? err.message : String(err)));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Decrypts an encrypted envelope using AES-GCM (256-bit).
|
|
212
|
+
* Fail-closed: Throws an Error if key is missing or decryption fails, unless { silent: true } is passed.
|
|
213
|
+
* Backwards compatible: Decrypts both v1 envelopes and legacy unversioned { encrypted: true, iv, data } payloads.
|
|
214
|
+
* @param {Object} encryptedObject
|
|
215
|
+
* @param {CryptoKey|Object} [keyOrOptions]
|
|
216
|
+
* @param {Object} [maybeOptions]
|
|
217
|
+
* @returns {Promise<any>}
|
|
218
|
+
*/
|
|
219
|
+
export async function decryptData(encryptedObject, keyOrOptions, maybeOptions) {
|
|
220
|
+
const { key, options } = _resolveKeyAndOptions(keyOrOptions, maybeOptions);
|
|
221
|
+
const silent = options.silent === true;
|
|
222
|
+
|
|
223
|
+
if (!encryptedObject || !encryptedObject.encrypted) {
|
|
224
|
+
return encryptedObject;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!key) {
|
|
228
|
+
if (silent) {
|
|
229
|
+
return { ...encryptedObject, decryptionError: true };
|
|
230
|
+
}
|
|
231
|
+
throw new Error('[ln-crypto] Decryption failed: No active cryptographic key provided');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!encryptedObject.iv || !encryptedObject.data) {
|
|
235
|
+
if (silent) {
|
|
236
|
+
return { ...encryptedObject, decryptionError: true };
|
|
237
|
+
}
|
|
238
|
+
throw new Error('[ln-crypto] Decryption failed: Malformed envelope (missing iv or data)');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const dec = new TextDecoder();
|
|
243
|
+
const iv = base64ToUint8(encryptedObject.iv);
|
|
244
|
+
const encryptedBuffer = base64ToUint8(encryptedObject.data);
|
|
245
|
+
|
|
246
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
247
|
+
{ name: 'AES-GCM', iv },
|
|
248
|
+
key,
|
|
249
|
+
encryptedBuffer
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const decoded = dec.decode(decryptedBuffer);
|
|
253
|
+
try {
|
|
254
|
+
return JSON.parse(decoded);
|
|
255
|
+
} catch (_) {
|
|
256
|
+
return decoded;
|
|
257
|
+
}
|
|
258
|
+
} catch (err) {
|
|
259
|
+
if (silent) {
|
|
260
|
+
return { ...encryptedObject, decryptionError: true };
|
|
261
|
+
}
|
|
262
|
+
console.error('[ln-core/crypto] Decryption failed. Key may be incorrect or payload tampered:', err);
|
|
263
|
+
throw new Error('[ln-crypto] Decryption failed. Key may be incorrect or payload tampered: ' + (err && err.message ? err.message : String(err)));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses raw input (Unix seconds, milliseconds, or ISO string) into a Date object.
|
|
3
|
+
* @param {unknown} raw
|
|
4
|
+
* @returns {Date|null}
|
|
5
|
+
*/
|
|
6
|
+
export function parseDateInput(raw) {
|
|
7
|
+
if (raw === null || raw === undefined || raw === '') return null;
|
|
8
|
+
|
|
9
|
+
if (raw instanceof Date) {
|
|
10
|
+
return isNaN(raw.getTime()) ? null : raw;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const num = Number(raw);
|
|
14
|
+
if (!isNaN(num) && num > 0) {
|
|
15
|
+
// If timestamp is in seconds (< 10^11), convert to ms
|
|
16
|
+
const ms = num < 1e11 ? num * 1000 : num;
|
|
17
|
+
const d = new Date(ms);
|
|
18
|
+
return isNaN(d.getTime()) ? null : d;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof raw === 'string') {
|
|
22
|
+
const str = raw.trim();
|
|
23
|
+
if (!str) return null;
|
|
24
|
+
// Date-only ISO parses as UTC midnight, which reads back as the previous day west of UTC.
|
|
25
|
+
const ymd = /^(\d{4})-(\d{2})-(\d{2})$/.exec(str);
|
|
26
|
+
if (ymd) {
|
|
27
|
+
const year = +ymd[1], month = +ymd[2], day = +ymd[3];
|
|
28
|
+
const local = new Date(year, month - 1, day);
|
|
29
|
+
if (local.getFullYear() !== year || local.getMonth() !== month - 1 || local.getDate() !== day) return null;
|
|
30
|
+
return local;
|
|
31
|
+
}
|
|
32
|
+
const d = new Date(str);
|
|
33
|
+
return isNaN(d.getTime()) ? null : d;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Formats a Date object to an ISO date string (YYYY-MM-DD).
|
|
41
|
+
* @param {Date} date
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
export function formatDateToISO(date) {
|
|
45
|
+
if (!date || !(date instanceof Date) || isNaN(date.getTime())) return '';
|
|
46
|
+
const y = date.getFullYear();
|
|
47
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
48
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
49
|
+
return y + '-' + m + '-' + d;
|
|
50
|
+
}
|