@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
package/js/ln-core/helpers.js
DELETED
|
@@ -1,788 +0,0 @@
|
|
|
1
|
-
// ─── Global Console Warning Interceptor (Production Mode) ──
|
|
2
|
-
if (typeof window !== 'undefined') {
|
|
3
|
-
const originalWarn = console.warn;
|
|
4
|
-
console.warn = function (...args) {
|
|
5
|
-
const isLibraryWarning = typeof args[0] === 'string' &&
|
|
6
|
-
(args[0].startsWith('[ln-') || args[0].startsWith('[lnCore'));
|
|
7
|
-
|
|
8
|
-
if (isLibraryWarning) {
|
|
9
|
-
const isDebug =
|
|
10
|
-
document.documentElement.hasAttribute('data-ln-debug') ||
|
|
11
|
-
(document.body && document.body.hasAttribute('data-ln-debug'));
|
|
12
|
-
|
|
13
|
-
if (!isDebug) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
originalWarn.apply(console, args);
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ─── Template Cache ────────────────────────────────────────
|
|
22
|
-
const _tmplCache = {};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Clone a <template data-ln-template="name"> element.
|
|
26
|
-
* Cached after first lookup.
|
|
27
|
-
*/
|
|
28
|
-
export function cloneTemplate(name, componentTag) {
|
|
29
|
-
if (!_tmplCache[name]) {
|
|
30
|
-
_tmplCache[name] = document.querySelector('[data-ln-template="' + name + '"]');
|
|
31
|
-
}
|
|
32
|
-
const tmpl = _tmplCache[name];
|
|
33
|
-
if (!tmpl) {
|
|
34
|
-
console.warn('[' + (componentTag || 'ln-core') + '] Template "' + name + '" not found');
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
return tmpl.content.cloneNode(true);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ─── Event Dispatch ────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
export function dispatch(element, eventName, detail) {
|
|
43
|
-
element.dispatchEvent(new CustomEvent(eventName, {
|
|
44
|
-
bubbles: true,
|
|
45
|
-
detail: detail || {}
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function dispatchCancelable(element, eventName, detail) {
|
|
50
|
-
const event = new CustomEvent(eventName, {
|
|
51
|
-
bubbles: true,
|
|
52
|
-
cancelable: true,
|
|
53
|
-
detail: detail || {}
|
|
54
|
-
});
|
|
55
|
-
element.dispatchEvent(event);
|
|
56
|
-
return event;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Re-filter / re-sort / re-render a data-driven list-like component, then
|
|
61
|
-
* notify the coordinator that fresh data is requested.
|
|
62
|
-
*
|
|
63
|
-
* Shared by ln-list and ln-table. The passed `component` MUST expose this
|
|
64
|
-
* instance contract:
|
|
65
|
-
* - _applyFilterAndSort() method
|
|
66
|
-
* - _render() method
|
|
67
|
-
* - _updateFooter() method
|
|
68
|
-
* - _vStart, _vEnd number (reset to -1 to force a full re-render)
|
|
69
|
-
* - dom the component root element (dispatch target)
|
|
70
|
-
* - name the component's name (becomes detail[keyName])
|
|
71
|
-
* - currentSort, currentFilters, currentSearch
|
|
72
|
-
*
|
|
73
|
-
* @param {Object} component component instance satisfying the contract above
|
|
74
|
-
* @param {string} eventName e.g. 'ln-table:request-data' / 'ln-list:request-data'
|
|
75
|
-
* @param {string} keyName payload key for the name, e.g. 'table' / 'list'
|
|
76
|
-
*/
|
|
77
|
-
export function requestData(component, eventName, keyName) {
|
|
78
|
-
component._applyFilterAndSort();
|
|
79
|
-
component._vStart = -1;
|
|
80
|
-
component._vEnd = -1;
|
|
81
|
-
component._render();
|
|
82
|
-
component._updateFooter();
|
|
83
|
-
|
|
84
|
-
const detail = {
|
|
85
|
-
sort: component.currentSort,
|
|
86
|
-
filters: component.currentFilters,
|
|
87
|
-
search: component.currentSearch
|
|
88
|
-
};
|
|
89
|
-
detail[keyName] = component.name;
|
|
90
|
-
dispatch(component.dom, eventName, detail);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ─── Declarative DOM Binding ───────────────────────────────
|
|
94
|
-
|
|
95
|
-
export function fill(root, data) {
|
|
96
|
-
if (!root || !data) return root;
|
|
97
|
-
|
|
98
|
-
// data-ln-field="prop" → textContent
|
|
99
|
-
const fields = root.querySelectorAll('[data-ln-field]');
|
|
100
|
-
for (let i = 0; i < fields.length; i++) {
|
|
101
|
-
const el = fields[i];
|
|
102
|
-
const prop = el.getAttribute('data-ln-field');
|
|
103
|
-
if (data[prop] != null) {
|
|
104
|
-
el.textContent = data[prop];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// data-ln-attr="attr:prop, attr:prop" → setAttribute
|
|
109
|
-
const attrs = root.querySelectorAll('[data-ln-attr]');
|
|
110
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
111
|
-
const el = attrs[i];
|
|
112
|
-
const pairs = el.getAttribute('data-ln-attr').split(',');
|
|
113
|
-
for (let j = 0; j < pairs.length; j++) {
|
|
114
|
-
const parts = pairs[j].trim().split(':');
|
|
115
|
-
if (parts.length !== 2) continue;
|
|
116
|
-
const attr = parts[0].trim();
|
|
117
|
-
const prop = parts[1].trim();
|
|
118
|
-
if (data[prop] != null) {
|
|
119
|
-
el.setAttribute(attr, data[prop]);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// data-ln-show="prop" → classList.toggle('hidden', !value)
|
|
125
|
-
const shows = root.querySelectorAll('[data-ln-show]');
|
|
126
|
-
for (let i = 0; i < shows.length; i++) {
|
|
127
|
-
const el = shows[i];
|
|
128
|
-
const prop = el.getAttribute('data-ln-show');
|
|
129
|
-
if (prop in data) {
|
|
130
|
-
el.classList.toggle('hidden', !data[prop]);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// data-ln-class="cls:prop, cls:prop" → classList.toggle(cls, !!value)
|
|
135
|
-
const classes = root.querySelectorAll('[data-ln-class]');
|
|
136
|
-
for (let i = 0; i < classes.length; i++) {
|
|
137
|
-
const el = classes[i];
|
|
138
|
-
const pairs = el.getAttribute('data-ln-class').split(',');
|
|
139
|
-
for (let j = 0; j < pairs.length; j++) {
|
|
140
|
-
const parts = pairs[j].trim().split(':');
|
|
141
|
-
if (parts.length !== 2) continue;
|
|
142
|
-
const cls = parts[0].trim();
|
|
143
|
-
const prop = parts[1].trim();
|
|
144
|
-
if (prop in data) {
|
|
145
|
-
el.classList.toggle(cls, !!data[prop]);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return root;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ─── Event-driven fill fan-out ─────────────────────────────
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Dispatch `ln-fill` at every [data-ln-form] and [data-ln-fillable]
|
|
157
|
-
* descendant of `container`. Fillables self-handle; null → clear/reset.
|
|
158
|
-
*/
|
|
159
|
-
export function lnFill(container, record) {
|
|
160
|
-
// Dispatch at container itself when it is a form or fillable.
|
|
161
|
-
// Enables lnFill(formEl, record) to reach ln-form's listener, which
|
|
162
|
-
// guards e.target === self.dom. Modal callers (lnFill(modalEl, …)
|
|
163
|
-
// where modalEl is [data-ln-modal]) do not match, so they are unaffected.
|
|
164
|
-
if (container.matches && container.matches('[data-ln-form], [data-ln-fillable]')) {
|
|
165
|
-
container.dispatchEvent(new CustomEvent('ln-fill', { detail: record ?? null, bubbles: true }));
|
|
166
|
-
}
|
|
167
|
-
const targets = container.querySelectorAll('[data-ln-form], [data-ln-fillable]');
|
|
168
|
-
for (let i = 0; i < targets.length; i++) {
|
|
169
|
-
targets[i].dispatchEvent(new CustomEvent('ln-fill', { detail: record ?? null, bubbles: true }));
|
|
170
|
-
}
|
|
171
|
-
return container;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Delegated display-fillable handler — installed once per page (idempotent
|
|
175
|
-
// across standalone component bundles that each inline ln-core helpers).
|
|
176
|
-
// Forms are NOT [data-ln-fillable]; ln-form's own listener handles them.
|
|
177
|
-
if (typeof window !== 'undefined') {
|
|
178
|
-
window.lnCore = window.lnCore || {};
|
|
179
|
-
if (!window.lnCore._fillBound) {
|
|
180
|
-
window.lnCore._fillBound = true;
|
|
181
|
-
document.addEventListener('ln-fill', function (e) {
|
|
182
|
-
if (!e.target.matches || !e.target.matches('[data-ln-fillable]')) return;
|
|
183
|
-
if (e.detail) {
|
|
184
|
-
fill(e.target, e.detail);
|
|
185
|
-
} else {
|
|
186
|
-
const fields = e.target.querySelectorAll('[data-ln-field]');
|
|
187
|
-
for (let i = 0; i < fields.length; i++) {
|
|
188
|
-
fields[i].textContent = '';
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ─── Template Text-Node Placeholders ──────────────────────
|
|
196
|
-
|
|
197
|
-
export function fillTemplate(clone, data) {
|
|
198
|
-
if (!clone || !data) return clone;
|
|
199
|
-
|
|
200
|
-
// ── Pass 1: text nodes ──────────────────────────────────
|
|
201
|
-
const walker = document.createTreeWalker(clone, NodeFilter.SHOW_TEXT);
|
|
202
|
-
while (walker.nextNode()) {
|
|
203
|
-
const node = walker.currentNode;
|
|
204
|
-
if (node.textContent.indexOf('{{') !== -1) {
|
|
205
|
-
node.textContent = node.textContent.replace(
|
|
206
|
-
/\{\{\s*(\w+)\s*\}\}/g,
|
|
207
|
-
function (_, key) { return data[key] !== undefined ? data[key] : ''; }
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// ── Pass 2: element attributes ──────────────────────────
|
|
213
|
-
// Uses setAttribute — never innerHTML — so injection risk is the same
|
|
214
|
-
// as data-ln-attr (the existing declarative attribute-fill mechanism).
|
|
215
|
-
// Complements data-ln-table-cell-attr (field→attr stamping); both coexist.
|
|
216
|
-
const _replaceTokens = function (_, key) { return data[key] !== undefined ? data[key] : ''; };
|
|
217
|
-
const elements = Array.from(clone.querySelectorAll('*'));
|
|
218
|
-
// Include the root itself when clone is an Element (e.g. a <tr> passed
|
|
219
|
-
// directly by ln-table._fillRow) — querySelectorAll does not include root.
|
|
220
|
-
if (clone.nodeType === 1) elements.push(clone);
|
|
221
|
-
for (let i = 0; i < elements.length; i++) {
|
|
222
|
-
const el = elements[i];
|
|
223
|
-
const attrs = el.attributes;
|
|
224
|
-
for (let j = 0; j < attrs.length; j++) {
|
|
225
|
-
const attr = attrs[j];
|
|
226
|
-
if (attr.value.indexOf('{{') !== -1) {
|
|
227
|
-
el.setAttribute(attr.name, attr.value.replace(/\{\{\s*(\w+)\s*\}\}/g, _replaceTokens));
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return clone;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ─── Keyed List Rendering ──────────────────────────────────
|
|
236
|
-
|
|
237
|
-
export function renderList(container, items, templateName, keyFn, fillFn, componentTag) {
|
|
238
|
-
// Index existing children by data-ln-key
|
|
239
|
-
const existingByKey = {};
|
|
240
|
-
for (let i = 0; i < container.children.length; i++) {
|
|
241
|
-
const child = container.children[i];
|
|
242
|
-
const key = child.getAttribute('data-ln-key');
|
|
243
|
-
if (key) existingByKey[key] = child;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Build ordered fragment — reuse or clone
|
|
247
|
-
const frag = document.createDocumentFragment();
|
|
248
|
-
|
|
249
|
-
for (let i = 0; i < items.length; i++) {
|
|
250
|
-
const item = items[i];
|
|
251
|
-
const key = String(keyFn(item));
|
|
252
|
-
let el = existingByKey[key];
|
|
253
|
-
|
|
254
|
-
if (el) {
|
|
255
|
-
fillFn(el, item, i);
|
|
256
|
-
} else {
|
|
257
|
-
const clone = cloneTemplate(templateName, componentTag);
|
|
258
|
-
if (!clone) continue;
|
|
259
|
-
fillTemplate(clone, item);
|
|
260
|
-
el = clone.firstElementChild;
|
|
261
|
-
if (!el) continue;
|
|
262
|
-
el.setAttribute('data-ln-key', key);
|
|
263
|
-
fillFn(el, item, i);
|
|
264
|
-
}
|
|
265
|
-
frag.appendChild(el);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Atomic update: orphans still in container, reused nodes already moved to frag
|
|
269
|
-
container.textContent = '';
|
|
270
|
-
container.appendChild(frag);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ─── Guard Body ────────────────────────────────────────
|
|
274
|
-
|
|
275
|
-
export function guardBody(setupFn, componentTag) {
|
|
276
|
-
if (!document.body) {
|
|
277
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
278
|
-
guardBody(setupFn, componentTag);
|
|
279
|
-
});
|
|
280
|
-
console.warn('[' + componentTag + '] Script loaded before <body> — add "defer" to your <script> tag');
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
setupFn();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export function cloneTemplateScoped(root, name, componentTag) {
|
|
287
|
-
if (root) {
|
|
288
|
-
const local = root.querySelector('[data-ln-template="' + name + '"]');
|
|
289
|
-
if (local) return local.content.cloneNode(true);
|
|
290
|
-
}
|
|
291
|
-
return cloneTemplate(name, componentTag);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// ─── Dictionary (i18n) ────────────────────────────────────
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Build a plain object from hidden dictionary elements.
|
|
298
|
-
* Reads all [selector] elements once, extracts key→textContent,
|
|
299
|
-
* removes them from DOM. Returns dictionary object.
|
|
300
|
-
*
|
|
301
|
-
* HTML: <ul hidden>
|
|
302
|
-
* <li data-ln-upload-dict="remove">Remove</li>
|
|
303
|
-
* </ul>
|
|
304
|
-
* JS: const dict = buildDict(container, 'data-ln-upload-dict');
|
|
305
|
-
* dict.remove → 'Remove'
|
|
306
|
-
*/
|
|
307
|
-
export function buildDict(root, selector) {
|
|
308
|
-
const dict = {};
|
|
309
|
-
const els = root.querySelectorAll('[' + selector + ']');
|
|
310
|
-
for (let i = 0; i < els.length; i++) {
|
|
311
|
-
dict[els[i].getAttribute(selector)] = els[i].textContent;
|
|
312
|
-
els[i].remove();
|
|
313
|
-
}
|
|
314
|
-
return dict;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ─── Find Elements ─────────────────────────────────────────
|
|
318
|
-
|
|
319
|
-
export function findElements(root, selector, attribute, ComponentClass) {
|
|
320
|
-
if (root.nodeType !== 1) return;
|
|
321
|
-
|
|
322
|
-
// Support both simple attribute names and full CSS selectors
|
|
323
|
-
const isComplex = selector.indexOf('[') !== -1 || selector.indexOf('.') !== -1 || selector.indexOf('#') !== -1;
|
|
324
|
-
const query = isComplex ? selector : '[' + selector + ']';
|
|
325
|
-
|
|
326
|
-
const items = Array.from(root.querySelectorAll(query));
|
|
327
|
-
if (root.matches && root.matches(query)) {
|
|
328
|
-
items.push(root);
|
|
329
|
-
}
|
|
330
|
-
for (const el of items) {
|
|
331
|
-
if (!el[attribute]) {
|
|
332
|
-
el[attribute] = new ComponentClass(el);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// ─── Visibility Check ─────────────────────────────────────
|
|
338
|
-
|
|
339
|
-
export function isVisible(el) {
|
|
340
|
-
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// ─── Form Serialization ───────────────────────────────────
|
|
344
|
-
|
|
345
|
-
// Effective HTTP method for a form: hidden _method input (non-empty) wins,
|
|
346
|
-
// else the form's own `method` attribute. Shared by ln-form's submit gate
|
|
347
|
-
// and ln-data-coordinator's native-submit claim so both read the identical
|
|
348
|
-
// literal DOM state — no drift between the two independent checks.
|
|
349
|
-
export function resolveFormMethod(form) {
|
|
350
|
-
const methodInput = form.querySelector('input[name="_method"]');
|
|
351
|
-
const raw = (methodInput && methodInput.value !== '') ? methodInput.value : form.method;
|
|
352
|
-
return (raw || '').toUpperCase();
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function serializeForm(form, opts) {
|
|
356
|
-
const typed = !!(opts && opts.typed);
|
|
357
|
-
const exclude = opts && opts.exclude;
|
|
358
|
-
const data = {};
|
|
359
|
-
const elements = form.elements;
|
|
360
|
-
|
|
361
|
-
// Pre-count checkbox names in typed mode to distinguish single-checkbox
|
|
362
|
-
// (→ boolean) from same-name checkbox groups (→ array of checked values).
|
|
363
|
-
const checkboxCounts = {};
|
|
364
|
-
if (typed) {
|
|
365
|
-
for (let i = 0; i < elements.length; i++) {
|
|
366
|
-
const el = elements[i];
|
|
367
|
-
if (el.name && el.type === 'checkbox' && !el.disabled) {
|
|
368
|
-
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
for (let i = 0; i < elements.length; i++) {
|
|
374
|
-
const el = elements[i];
|
|
375
|
-
if (!el.name || el.disabled || el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
376
|
-
if (exclude && el.matches && el.matches(exclude)) continue;
|
|
377
|
-
|
|
378
|
-
if (el.type === 'checkbox') {
|
|
379
|
-
if (typed && checkboxCounts[el.name] === 1) {
|
|
380
|
-
data[el.name] = el.checked; // single checkbox → boolean
|
|
381
|
-
} else {
|
|
382
|
-
if (!data[el.name]) data[el.name] = [];
|
|
383
|
-
if (el.checked) data[el.name].push(el.value); // group → array (unchanged)
|
|
384
|
-
}
|
|
385
|
-
} else if (el.type === 'radio') {
|
|
386
|
-
if (el.checked) data[el.name] = el.value;
|
|
387
|
-
} else if (el.type === 'select-multiple') {
|
|
388
|
-
data[el.name] = [];
|
|
389
|
-
for (let j = 0; j < el.options.length; j++) {
|
|
390
|
-
if (el.options[j].selected) data[el.name].push(el.options[j].value);
|
|
391
|
-
}
|
|
392
|
-
} else if (typed && el.type === 'hidden') {
|
|
393
|
-
data[el.name] = el.value; // hidden → always string (never coerce)
|
|
394
|
-
} else if (typed && (el.type === 'number' || el.type === 'range')) {
|
|
395
|
-
const n = Number(el.value);
|
|
396
|
-
data[el.name] = (el.value === '' || isNaN(n)) ? null : n; // number/range → Number | null
|
|
397
|
-
} else {
|
|
398
|
-
data[el.name] = el.value;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return data;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Coerce a value to boolean for single-checkbox fill.
|
|
406
|
-
// Flat data-* values arrive as strings; "false", "0", "", "off", "no"
|
|
407
|
-
// (case-insensitive, trimmed) are treated as unchecked.
|
|
408
|
-
function _coerceBool(value) {
|
|
409
|
-
if (typeof value !== 'string') return !!value;
|
|
410
|
-
const v = value.trim().toLowerCase();
|
|
411
|
-
return v !== 'false' && v !== '0' && v !== '' && v !== 'off' && v !== 'no';
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export function populateForm(form, data) {
|
|
415
|
-
const elements = form.elements;
|
|
416
|
-
const filled = [];
|
|
417
|
-
|
|
418
|
-
// Pre-count checkbox names to distinguish single-checkbox (→ bool)
|
|
419
|
-
// from same-name checkbox groups (→ comma-separated list membership).
|
|
420
|
-
// NOTE: unlike serializeForm, populateForm fills disabled elements too,
|
|
421
|
-
// so the count must include disabled members (no !el.disabled guard) —
|
|
422
|
-
// otherwise a group with a disabled box reverts to single-checkbox coercion.
|
|
423
|
-
const checkboxCounts = {};
|
|
424
|
-
for (let i = 0; i < elements.length; i++) {
|
|
425
|
-
const el = elements[i];
|
|
426
|
-
if (el.name && el.type === 'checkbox') {
|
|
427
|
-
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
for (let i = 0; i < elements.length; i++) {
|
|
432
|
-
const el = elements[i];
|
|
433
|
-
if (el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
434
|
-
|
|
435
|
-
// Decoupled-key fill: data-ln-fill-as wins over name for record matching.
|
|
436
|
-
// Write (fill) path only — serializeForm stays name-based.
|
|
437
|
-
const matchKey = el.getAttribute('data-ln-fill-as') || el.name;
|
|
438
|
-
if (!matchKey || !(matchKey in data)) continue;
|
|
439
|
-
|
|
440
|
-
const value = data[matchKey];
|
|
441
|
-
|
|
442
|
-
if (el.type === 'checkbox') {
|
|
443
|
-
if (Array.isArray(value)) {
|
|
444
|
-
// Programmatic array path — membership check.
|
|
445
|
-
el.checked = value.indexOf(el.value) !== -1;
|
|
446
|
-
} else if (checkboxCounts[el.name] > 1) {
|
|
447
|
-
// Same-name checkbox group + scalar: treat as comma-separated list.
|
|
448
|
-
// "admin,editor" → ['admin','editor'] → membership check.
|
|
449
|
-
const list = String(value).split(',').map(function (s) { return s.trim(); });
|
|
450
|
-
el.checked = list.indexOf(el.value) !== -1;
|
|
451
|
-
} else {
|
|
452
|
-
// Single checkbox — boolean coercion.
|
|
453
|
-
// "false"/"0"/"off"/"no"/"" → unchecked; anything else → checked.
|
|
454
|
-
el.checked = _coerceBool(value);
|
|
455
|
-
}
|
|
456
|
-
filled.push(el);
|
|
457
|
-
} else if (el.type === 'radio') {
|
|
458
|
-
el.checked = el.value === String(value);
|
|
459
|
-
filled.push(el);
|
|
460
|
-
} else if (el.type === 'select-multiple') {
|
|
461
|
-
if (Array.isArray(value)) {
|
|
462
|
-
for (let j = 0; j < el.options.length; j++) {
|
|
463
|
-
el.options[j].selected = value.indexOf(el.options[j].value) !== -1;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
filled.push(el);
|
|
467
|
-
} else {
|
|
468
|
-
el.value = value;
|
|
469
|
-
filled.push(el);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return filled;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// ─── Locale Detection ─────────────────────────────────────
|
|
477
|
-
|
|
478
|
-
const _standardTerritories = {
|
|
479
|
-
mk: 'mk-MK',
|
|
480
|
-
de: 'de-DE',
|
|
481
|
-
fr: 'fr-FR',
|
|
482
|
-
es: 'es-ES',
|
|
483
|
-
it: 'it-IT',
|
|
484
|
-
nl: 'nl-NL',
|
|
485
|
-
pt: 'pt-PT',
|
|
486
|
-
ru: 'ru-RU',
|
|
487
|
-
bg: 'bg-BG',
|
|
488
|
-
hr: 'hr-HR',
|
|
489
|
-
sr: 'sr-RS',
|
|
490
|
-
sq: 'sq-AL',
|
|
491
|
-
el: 'el-GR',
|
|
492
|
-
en: 'en-US'
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
export function getLocale(el) {
|
|
496
|
-
const langEl = el ? el.closest('[lang]') : null;
|
|
497
|
-
const raw = (langEl ? (langEl.getAttribute('lang') || langEl.lang) : null)
|
|
498
|
-
|| (document.documentElement ? (document.documentElement.getAttribute('lang') || document.documentElement.lang) : null)
|
|
499
|
-
|| navigator.language;
|
|
500
|
-
if (!raw) return 'en-US';
|
|
501
|
-
const lower = raw.trim().toLowerCase();
|
|
502
|
-
if (lower.indexOf('-') === -1 && _standardTerritories[lower]) {
|
|
503
|
-
return _standardTerritories[lower];
|
|
504
|
-
}
|
|
505
|
-
return raw;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// ─── Raw Value Read ───────────────────────────────────────
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Read the raw machine value behind a formatted cell/item display.
|
|
512
|
-
* Returns the `data-ln-value` attribute if present, else trimmed textContent.
|
|
513
|
-
* Single read path for value-based sort/filter across components.
|
|
514
|
-
*/
|
|
515
|
-
export function readValue(el) {
|
|
516
|
-
return el.hasAttribute('data-ln-value')
|
|
517
|
-
? el.getAttribute('data-ln-value')
|
|
518
|
-
: el.textContent.trim();
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// ─── Value Comparison Primitives ───────────────────────────
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Detect the comparison type for a column/field from its current value set.
|
|
525
|
-
* Type is locked ONCE per sort operation (not per pair) — comparing types
|
|
526
|
-
* per-pair breaks comparator transitivity and produces wrong ordering.
|
|
527
|
-
* All non-empty values must be finite numbers for 'number'; otherwise 'string'.
|
|
528
|
-
*/
|
|
529
|
-
export function detectValueType(values) {
|
|
530
|
-
let hasValue = false;
|
|
531
|
-
for (let i = 0; i < values.length; i++) {
|
|
532
|
-
const v = values[i];
|
|
533
|
-
if (v === '' || v == null) continue;
|
|
534
|
-
hasValue = true;
|
|
535
|
-
if (!Number.isFinite(Number(v))) return 'string';
|
|
536
|
-
}
|
|
537
|
-
return hasValue ? 'number' : 'string';
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Compare two raw values given a pre-locked type (see detectValueType).
|
|
542
|
-
* Pass an Intl.Collator instance for locale-aware string comparison —
|
|
543
|
-
* built once by the caller outside the sort loop, never per pair.
|
|
544
|
-
*/
|
|
545
|
-
export function compareValues(a, b, type, collator) {
|
|
546
|
-
if (type === 'number') {
|
|
547
|
-
const na = parseFloat(a);
|
|
548
|
-
const nb = parseFloat(b);
|
|
549
|
-
return (isNaN(na) ? 0 : na) - (isNaN(nb) ? 0 : nb);
|
|
550
|
-
}
|
|
551
|
-
const strA = a != null ? String(a) : '';
|
|
552
|
-
const strB = b != null ? String(b) : '';
|
|
553
|
-
if (collator) return collator.compare(strA, strB);
|
|
554
|
-
return strA < strB ? -1 : strA > strB ? 1 : 0;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// ─── Value Property Interception ───────────────────────────
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Intercept the programmatic `value` getter/setter on an input element.
|
|
561
|
-
* Useful for custom components that need to sync/format programmatic changes.
|
|
562
|
-
* @param {HTMLInputElement} dom - The visible input element.
|
|
563
|
-
* @param {Object} descriptor - The original property descriptor of HTMLInputElement.prototype.value.
|
|
564
|
-
* @param {Object} callbacks - Getter and setter callbacks.
|
|
565
|
-
*/
|
|
566
|
-
export function interceptValueProperty(dom, descriptor, { get, set }) {
|
|
567
|
-
Object.defineProperty(dom, 'value', {
|
|
568
|
-
get: function () {
|
|
569
|
-
if (get) {
|
|
570
|
-
return get.call(this);
|
|
571
|
-
}
|
|
572
|
-
return descriptor.get.call(this);
|
|
573
|
-
},
|
|
574
|
-
set: function (val) {
|
|
575
|
-
if (set) {
|
|
576
|
-
set.call(this, val, (originalVal) => descriptor.set.call(this, originalVal));
|
|
577
|
-
} else {
|
|
578
|
-
descriptor.set.call(this, val);
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
configurable: true
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// ─── Component Registration ───────────────────────────────
|
|
586
|
-
|
|
587
|
-
export function registerComponent(selector, attribute, ComponentFn, componentTag, options = {}) {
|
|
588
|
-
const extraAttributes = options.extraAttributes || [];
|
|
589
|
-
const onAttributeChange = options.onAttributeChange || null;
|
|
590
|
-
const onInit = options.onInit || null;
|
|
591
|
-
|
|
592
|
-
function constructor(domRoot) {
|
|
593
|
-
const root = domRoot || document.body;
|
|
594
|
-
findElements(root, selector, attribute, ComponentFn);
|
|
595
|
-
if (onInit) onInit(root);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
guardBody(function () {
|
|
599
|
-
const observer = new MutationObserver(function (mutations) {
|
|
600
|
-
for (let i = 0; i < mutations.length; i++) {
|
|
601
|
-
const mutation = mutations[i];
|
|
602
|
-
if (mutation.type === 'childList') {
|
|
603
|
-
for (let j = 0; j < mutation.addedNodes.length; j++) {
|
|
604
|
-
const node = mutation.addedNodes[j];
|
|
605
|
-
if (node.nodeType === 1) {
|
|
606
|
-
findElements(node, selector, attribute, ComponentFn);
|
|
607
|
-
if (onInit) onInit(node);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
for (let j = 0; j < mutation.removedNodes.length; j++) {
|
|
611
|
-
const node = mutation.removedNodes[j];
|
|
612
|
-
if (node.nodeType === 1) {
|
|
613
|
-
const isComplex = selector.indexOf('[') !== -1 || selector.indexOf('.') !== -1 || selector.indexOf('#') !== -1;
|
|
614
|
-
const query = isComplex ? selector : '[' + selector + ']';
|
|
615
|
-
const items = Array.from(node.querySelectorAll(query));
|
|
616
|
-
if (node.matches && node.matches(query)) {
|
|
617
|
-
items.push(node);
|
|
618
|
-
}
|
|
619
|
-
for (let k = 0; k < items.length; k++) {
|
|
620
|
-
const item = items[k];
|
|
621
|
-
if (!document.contains(item)) {
|
|
622
|
-
const inst = item[attribute];
|
|
623
|
-
if (inst && typeof inst.destroy === 'function') {
|
|
624
|
-
inst.destroy();
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
} else if (mutation.type === 'attributes') {
|
|
631
|
-
if (onAttributeChange && mutation.target[attribute]) {
|
|
632
|
-
onAttributeChange(mutation.target, mutation.attributeName);
|
|
633
|
-
} else {
|
|
634
|
-
findElements(mutation.target, selector, attribute, ComponentFn);
|
|
635
|
-
if (onInit) onInit(mutation.target);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
// Extract attribute names from selector for attributeFilter
|
|
642
|
-
let observedAttributes = [];
|
|
643
|
-
if (selector.indexOf('[') !== -1) {
|
|
644
|
-
const re = /\[([\w-]+)/g;
|
|
645
|
-
let match;
|
|
646
|
-
while ((match = re.exec(selector)) !== null) {
|
|
647
|
-
observedAttributes.push(match[1]);
|
|
648
|
-
}
|
|
649
|
-
} else {
|
|
650
|
-
observedAttributes.push(selector);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
observer.observe(document.body, {
|
|
654
|
-
childList: true,
|
|
655
|
-
subtree: true,
|
|
656
|
-
attributes: true,
|
|
657
|
-
attributeFilter: observedAttributes.concat(extraAttributes)
|
|
658
|
-
});
|
|
659
|
-
}, componentTag || (selector.indexOf('[') === -1 ? selector.replace('data-', '') : 'component'));
|
|
660
|
-
|
|
661
|
-
window[attribute] = constructor;
|
|
662
|
-
|
|
663
|
-
if (document.readyState === 'loading') {
|
|
664
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
665
|
-
constructor(document.body);
|
|
666
|
-
});
|
|
667
|
-
} else {
|
|
668
|
-
constructor(document.body);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
return constructor;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// ─── HTTP / URL Helpers ────────────────────────────────────
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Check if a click event on a link should be intercepted by an in-app navigator.
|
|
678
|
-
* Intercept only left clicks, same-origin, not target="_blank", no download,
|
|
679
|
-
* not mailto/tel, and not pure hash/empty.
|
|
680
|
-
*
|
|
681
|
-
* @param {MouseEvent} event
|
|
682
|
-
* @param {HTMLAnchorElement} anchor
|
|
683
|
-
* @returns {boolean}
|
|
684
|
-
*/
|
|
685
|
-
export function shouldInterceptLink(event, anchor) {
|
|
686
|
-
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
687
|
-
return false;
|
|
688
|
-
}
|
|
689
|
-
if (!anchor) return false;
|
|
690
|
-
const href = anchor.getAttribute('href');
|
|
691
|
-
if (!href) return false;
|
|
692
|
-
if (anchor.getAttribute('target') === '_blank') return false;
|
|
693
|
-
if (anchor.hasAttribute('download')) return false;
|
|
694
|
-
if (href.startsWith('mailto:') || href.startsWith('tel:')) return false;
|
|
695
|
-
if (href === '#' || href.startsWith('#')) return false;
|
|
696
|
-
if (anchor.hostname && anchor.hostname !== window.location.hostname) return false;
|
|
697
|
-
return true;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Build a URL from segments, stripping duplicate slashes.
|
|
702
|
-
*/
|
|
703
|
-
export function buildUrl(...segments) {
|
|
704
|
-
return segments
|
|
705
|
-
.filter(x => x !== undefined && x !== null && x !== '')
|
|
706
|
-
.map((part, index) => {
|
|
707
|
-
if (index === 0) return part.replace(/\/+$/, '');
|
|
708
|
-
return part.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
709
|
-
})
|
|
710
|
-
.filter(Boolean)
|
|
711
|
-
.join('/');
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Compile standard JSON request headers.
|
|
716
|
-
*/
|
|
717
|
-
export function getHeaders(customHeaders, auth) {
|
|
718
|
-
return Object.assign({
|
|
719
|
-
'Content-Type': 'application/json',
|
|
720
|
-
'Accept': 'application/json'
|
|
721
|
-
}, customHeaders, auth ? { 'Authorization': auth } : null);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
/**
|
|
725
|
-
* Safely parse headers from JSON string.
|
|
726
|
-
*/
|
|
727
|
-
export function parseHeaders(str, componentName = 'ln-core') {
|
|
728
|
-
try { return str ? JSON.parse(str) : {}; }
|
|
729
|
-
catch (e) { return console.error(`[${componentName}] Invalid headers JSON:`, e), {}; }
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// ─── Domain Data Mapper Registry ───────────────────────────
|
|
733
|
-
const _dataMappers = {};
|
|
734
|
-
|
|
735
|
-
export function registerDataMapper(name, mapper) {
|
|
736
|
-
_dataMappers[name] = mapper;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
export function getDataMapper(name) {
|
|
740
|
-
return _dataMappers[name] || { ingress: r => r, egress: r => r };
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// ─── Locale Fallback Registry ───────────────────────────────
|
|
744
|
-
const _localeFallbacks = {};
|
|
745
|
-
|
|
746
|
-
export function registerLocaleFallback(langPrefix, dictionary) {
|
|
747
|
-
if (!langPrefix || typeof dictionary !== 'object') return;
|
|
748
|
-
const prefix = langPrefix.toLowerCase().split('-')[0];
|
|
749
|
-
_localeFallbacks[prefix] = dictionary;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
export function getLocaleFallback(lang) {
|
|
753
|
-
if (!lang) return null;
|
|
754
|
-
const prefix = lang.toLowerCase().split('-')[0];
|
|
755
|
-
return _localeFallbacks[prefix] || null;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// Default Macedonian locale fallback registration
|
|
759
|
-
registerLocaleFallback('mk', {
|
|
760
|
-
monthsLong: [
|
|
761
|
-
'јануари', 'февруари', 'март', 'април', 'мај', 'јуни',
|
|
762
|
-
'јули', 'август', 'септември', 'октомври', 'ноември', 'декември'
|
|
763
|
-
],
|
|
764
|
-
monthsShort: [
|
|
765
|
-
'јан', 'фев', 'мар', 'апр', 'мај', 'јун',
|
|
766
|
-
'јул', 'авг', 'септ', 'окт', 'ноем', 'дек'
|
|
767
|
-
],
|
|
768
|
-
daysLong: [
|
|
769
|
-
'недела', 'понеделник', 'вторник', 'среда', 'четврток', 'петок', 'сабота'
|
|
770
|
-
],
|
|
771
|
-
daysShort: [
|
|
772
|
-
'нед', 'пон', 'вт', 'ср', 'чет', 'пет', 'саб'
|
|
773
|
-
]
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
if (typeof window !== 'undefined') {
|
|
777
|
-
window.lnCore = window.lnCore || {};
|
|
778
|
-
window.lnCore.registerDataMapper = registerDataMapper;
|
|
779
|
-
window.lnCore.getDataMapper = getDataMapper;
|
|
780
|
-
window.lnCore.registerLocaleFallback = registerLocaleFallback;
|
|
781
|
-
window.lnCore.getLocaleFallback = getLocaleFallback;
|
|
782
|
-
window.lnCore.fillTemplate = fillTemplate;
|
|
783
|
-
window.lnCore.fill = fill;
|
|
784
|
-
window.lnCore.lnFill = lnFill;
|
|
785
|
-
window.lnCore.renderList = renderList;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
|