@livenetworks/ashlar 1.6.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +641 -0
- package/README.md +202 -86
- package/{js → components}/COMPONENTS.md +1116 -1108
- package/{js → components}/index.js +81 -58
- package/{js → components}/ln-accordion/README.md +7 -6
- package/components/ln-accordion/ln-accordion-dev.scss +8 -0
- package/components/ln-accordion/ln-accordion.js +1 -0
- package/components/ln-accordion/ln-accordion.schema.json +30 -0
- package/components/ln-accordion/ln-accordion.scss +8 -0
- package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
- package/{js → components}/ln-ajax/README.md +171 -160
- package/components/ln-ajax/ln-ajax-dev.scss +8 -0
- package/components/ln-ajax/ln-ajax.js +1 -0
- package/components/ln-ajax/ln-ajax.schema.json +30 -0
- package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
- package/{js → components}/ln-api-connector/README.md +27 -5
- package/components/ln-api-connector/ln-api-connector.js +1 -0
- package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
- package/components/ln-api-connector/src/connector-core.js +97 -0
- package/components/ln-api-connector/src/ln-api-connector.js +447 -0
- package/{js → components}/ln-api-queue/README.md +12 -9
- package/components/ln-api-queue/ln-api-queue.js +1 -0
- package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
- package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
- package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
- package/{js → components}/ln-autoresize/README.md +129 -127
- package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
- package/components/ln-autoresize/ln-autoresize.js +1 -0
- package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
- package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
- package/{js → components}/ln-autosave/README.md +127 -124
- package/components/ln-autosave/ln-autosave-dev.scss +13 -0
- package/components/ln-autosave/ln-autosave.js +1 -0
- package/components/ln-autosave/ln-autosave.schema.json +48 -0
- package/components/ln-autosave/src/autosave-model.js +34 -0
- package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
- package/components/ln-chart/README.md +145 -0
- package/components/ln-chart/ln-chart-dev.scss +20 -0
- package/components/ln-chart/ln-chart.js +1 -0
- package/components/ln-chart/ln-chart.schema.json +146 -0
- package/components/ln-chart/ln-chart.scss +8 -0
- package/components/ln-chart/src/chart-model.js +154 -0
- package/components/ln-chart/src/ln-chart.js +176 -0
- package/{js → components}/ln-circular-progress/README.md +15 -14
- package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
- package/components/ln-circular-progress/ln-circular-progress.js +1 -0
- package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
- package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
- package/components/ln-confirm/README.md +216 -0
- package/components/ln-confirm/ln-confirm-dev.scss +24 -0
- package/components/ln-confirm/ln-confirm.js +1 -0
- package/components/ln-confirm/ln-confirm.schema.json +66 -0
- package/components/ln-confirm/ln-confirm.scss +24 -0
- package/{js → components}/ln-confirm/src/ln-confirm.js +180 -167
- package/{js → components}/ln-core/README.md +374 -149
- package/components/ln-core/attrs.js +273 -0
- package/components/ln-core/compare.js +40 -0
- package/components/ln-core/crypto.js +265 -0
- package/components/ln-core/date.js +50 -0
- package/components/ln-core/dom.js +115 -0
- package/components/ln-core/events.js +122 -0
- package/components/ln-core/form.js +136 -0
- package/{js → components}/ln-core/hash.js +149 -92
- package/components/ln-core/helpers.js +33 -0
- package/components/ln-core/http.js +48 -0
- package/components/ln-core/index.js +11 -0
- package/components/ln-core/lifecycle.js +403 -0
- package/components/ln-core/ln-core.schema.json +109 -0
- package/components/ln-core/locale.js +55 -0
- package/components/ln-core/matching.js +78 -0
- package/components/ln-core/number.js +125 -0
- package/components/ln-core/progress.js +30 -0
- package/components/ln-core/template.js +206 -0
- package/{js → components}/ln-core/window-cache.js +247 -213
- package/{js → components}/ln-couchdb-connector/README.md +7 -5
- package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
- package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
- package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
- package/{js → components}/ln-data-coordinator/README.md +109 -73
- package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
- package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
- package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
- package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
- package/{js → components}/ln-data-store/README.md +42 -17
- package/components/ln-data-store/ln-data-store-dev.scss +13 -0
- package/components/ln-data-store/ln-data-store.js +1 -0
- package/components/ln-data-store/ln-data-store.schema.json +84 -0
- package/components/ln-data-store/src/data-store-model.js +155 -0
- package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
- package/components/ln-data-store/src/window-index.js +119 -0
- package/{js → components}/ln-date/README.md +17 -7
- package/{js → components}/ln-date/ln-date-dev.scss +14 -14
- package/components/ln-date/ln-date.js +1 -0
- package/components/ln-date/ln-date.schema.json +92 -0
- package/components/ln-date/src/date-model.js +186 -0
- package/components/ln-date/src/ln-date.js +486 -0
- package/components/ln-debug/README.md +132 -0
- package/components/ln-debug/ln-debug.js +1 -0
- package/components/ln-debug/ln-debug.schema.json +28 -0
- package/components/ln-debug/src/console-sink.js +19 -0
- package/components/ln-debug/src/debug-verifier.js +331 -0
- package/components/ln-debug/src/gate.js +95 -0
- package/components/ln-debug/src/generated-attributes.js +299 -0
- package/components/ln-debug/src/ln-debug.js +60 -0
- package/{js → components}/ln-dropdown/README.md +150 -140
- package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
- package/components/ln-dropdown/ln-dropdown.js +1 -0
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
- package/components/ln-dropdown/ln-dropdown.scss +18 -0
- package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
- package/{js → components}/ln-editor/README.md +11 -9
- package/components/ln-editor/ln-editor-dev.scss +18 -0
- package/components/ln-editor/ln-editor.js +1 -0
- package/components/ln-editor/ln-editor.schema.json +58 -0
- package/components/ln-editor/ln-editor.scss +13 -0
- package/{js → components}/ln-editor/src/ln-editor.js +59 -11
- package/{js → components}/ln-external-links/README.md +10 -8
- package/components/ln-external-links/ln-external-links.js +1 -0
- package/components/ln-external-links/ln-external-links.schema.json +18 -0
- package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
- package/{js → components}/ln-fill/README.md +6 -8
- package/components/ln-fill/ln-fill.js +1 -0
- package/components/ln-fill/ln-fill.schema.json +23 -0
- package/{js → components}/ln-fill/src/ln-fill.js +7 -1
- package/components/ln-filter/README.md +131 -0
- package/components/ln-filter/ln-filter-dev.scss +13 -0
- package/components/ln-filter/ln-filter.js +1 -0
- package/components/ln-filter/ln-filter.schema.json +153 -0
- package/components/ln-filter/src/filter-model.js +185 -0
- package/components/ln-filter/src/ln-filter.js +435 -0
- package/{js → components}/ln-form/README.md +260 -264
- package/components/ln-form/ln-form-dev.scss +8 -0
- package/components/ln-form/ln-form.js +1 -0
- package/components/ln-form/ln-form.schema.json +45 -0
- package/{js → components}/ln-form/src/ln-form.js +107 -99
- package/{js → components}/ln-http/README.md +11 -6
- package/components/ln-http/ln-http.js +1 -0
- package/components/ln-http/ln-http.schema.json +6 -0
- package/components/ln-http/src/http-core.js +51 -0
- package/{js → components}/ln-http/src/ln-http.js +28 -33
- package/{js → components}/ln-icon/README.md +16 -9
- package/components/ln-icon/ln-icon.schema.json +6 -0
- package/components/ln-include/README.md +75 -0
- package/components/ln-include/ln-include-dev.scss +13 -0
- package/components/ln-include/ln-include.js +1 -0
- package/components/ln-include/ln-include.schema.json +23 -0
- package/components/ln-include/src/ln-include.js +111 -0
- package/components/ln-key/README.md +130 -0
- package/components/ln-key/ln-key-dev.scss +18 -0
- package/components/ln-key/ln-key.js +1 -0
- package/components/ln-key/ln-key.schema.json +56 -0
- package/components/ln-key/src/key-model.js +146 -0
- package/components/ln-key/src/ln-key.js +201 -0
- package/{js → components}/ln-link/README.md +7 -9
- package/components/ln-link/ln-link-dev.scss +8 -0
- package/components/ln-link/ln-link.js +1 -0
- package/components/ln-link/ln-link.schema.json +22 -0
- package/{js → components}/ln-link/src/ln-link.js +16 -11
- package/components/ln-list/README.md +152 -0
- package/components/ln-list/ln-list-dev.scss +8 -0
- package/components/ln-list/ln-list.js +1 -0
- package/components/ln-list/ln-list.schema.json +166 -0
- package/components/ln-list/ln-list.scss +25 -0
- package/{js → components}/ln-list/src/ln-list.js +1301 -1216
- package/{js → components}/ln-modal/README.md +13 -10
- package/components/ln-modal/ln-modal-dev.scss +18 -0
- package/components/ln-modal/ln-modal.js +1 -0
- package/components/ln-modal/ln-modal.schema.json +54 -0
- package/components/ln-modal/ln-modal.scss +51 -0
- package/{js → components}/ln-modal/src/ln-modal.js +164 -137
- package/{js → components}/ln-nav/README.md +11 -11
- package/components/ln-nav/ln-nav.js +1 -0
- package/components/ln-nav/ln-nav.schema.json +25 -0
- package/{js → components}/ln-nav/src/ln-nav.js +25 -14
- package/components/ln-number/README.md +205 -0
- package/{js → components}/ln-number/ln-number-dev.scss +13 -13
- package/components/ln-number/ln-number.js +1 -0
- package/components/ln-number/ln-number.schema.json +63 -0
- package/components/ln-number/src/ln-number.js +400 -0
- package/components/ln-number/src/number-model.js +31 -0
- package/components/ln-obfuscator/README.md +60 -0
- package/components/ln-obfuscator/ln-obfuscator.js +1 -0
- package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
- package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
- package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
- package/{js → components}/ln-options/README.md +3 -1
- package/components/ln-options/ln-options-dev.scss +13 -0
- package/components/ln-options/ln-options.js +1 -0
- package/components/ln-options/ln-options.schema.json +41 -0
- package/{js → components}/ln-options/src/ln-options.js +13 -5
- package/components/ln-persist/README.md +151 -0
- package/components/ln-persist/ln-persist.js +1 -0
- package/components/ln-persist/ln-persist.schema.json +28 -0
- package/components/ln-persist/src/ln-persist.js +83 -0
- package/components/ln-picklist/README.md +230 -0
- package/components/ln-picklist/ln-picklist-dev.scss +22 -0
- package/components/ln-picklist/ln-picklist.js +1 -0
- package/components/ln-picklist/ln-picklist.schema.json +49 -0
- package/components/ln-picklist/src/ln-picklist.js +228 -0
- package/{js → components}/ln-popover/README.md +164 -162
- package/components/ln-popover/ln-popover-dev.scss +13 -0
- package/components/ln-popover/ln-popover.js +1 -0
- package/components/ln-popover/ln-popover.schema.json +82 -0
- package/components/ln-popover/ln-popover.scss +18 -0
- package/{js → components}/ln-popover/src/ln-popover.js +42 -36
- package/{js → components}/ln-progress/README.md +410 -413
- package/components/ln-progress/ln-progress-dev.scss +8 -0
- package/components/ln-progress/ln-progress.js +1 -0
- package/components/ln-progress/ln-progress.schema.json +40 -0
- package/components/ln-progress/src/ln-progress.js +91 -0
- package/components/ln-progress/src/progress-model.js +20 -0
- package/{js → components}/ln-router/README.md +443 -432
- package/components/ln-router/ln-router-dev.scss +13 -0
- package/components/ln-router/ln-router.js +1 -0
- package/components/ln-router/ln-router.schema.json +68 -0
- package/{js → components}/ln-router/src/ln-router.js +585 -539
- package/components/ln-router/src/router-model.js +50 -0
- package/components/ln-scroll/README.md +45 -0
- package/components/ln-scroll/ln-scroll.js +1 -0
- package/components/ln-scroll/ln-scroll.schema.json +85 -0
- package/components/ln-scroll/src/ln-scroll.js +150 -0
- package/components/ln-search/README.md +188 -0
- package/components/ln-search/ln-search-dev.scss +13 -0
- package/components/ln-search/ln-search.js +1 -0
- package/components/ln-search/ln-search.schema.json +103 -0
- package/components/ln-search/src/ln-search.js +330 -0
- package/{js → components}/ln-slug/README.md +6 -3
- package/components/ln-slug/ln-slug-dev.scss +18 -0
- package/components/ln-slug/ln-slug.js +1 -0
- package/components/ln-slug/ln-slug.schema.json +23 -0
- package/{js → components}/ln-slug/src/ln-slug.js +20 -13
- package/components/ln-slug/src/slug-model.js +21 -0
- package/{js → components}/ln-sort/README.md +178 -170
- package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
- package/components/ln-sort/ln-sort.js +1 -0
- package/components/ln-sort/ln-sort.schema.json +104 -0
- package/components/ln-sort/src/ln-sort.js +321 -0
- package/components/ln-sort/src/sort-model.js +117 -0
- package/{js → components}/ln-sortable/README.md +7 -11
- package/components/ln-sortable/ln-sortable-dev.scss +13 -0
- package/components/ln-sortable/ln-sortable.js +1 -0
- package/components/ln-sortable/ln-sortable.schema.json +36 -0
- package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
- package/{js → components}/ln-stat/README.md +3 -1
- package/components/ln-stat/ln-stat-dev.scss +8 -0
- package/components/ln-stat/ln-stat.js +1 -0
- package/components/ln-stat/ln-stat.schema.json +32 -0
- package/{js → components}/ln-stat/src/ln-stat.js +14 -18
- package/components/ln-stat/src/stat-model.js +28 -0
- package/{js → components}/ln-table/README.md +37 -24
- package/{js → components}/ln-table/ln-table-dev.scss +17 -17
- package/components/ln-table/ln-table.js +1 -0
- package/components/ln-table/ln-table.schema.json +211 -0
- package/components/ln-table/ln-table.scss +15 -0
- package/{js → components}/ln-table/src/ln-table.js +1365 -1493
- package/components/ln-table/src/table-model.js +98 -0
- package/components/ln-table-coordinator/README.md +61 -0
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
- package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
- package/{js → components}/ln-tabs/README.md +32 -14
- package/components/ln-tabs/ln-tabs-dev.scss +24 -0
- package/components/ln-tabs/ln-tabs.js +1 -0
- package/components/ln-tabs/ln-tabs.schema.json +75 -0
- package/components/ln-tabs/ln-tabs.scss +8 -0
- package/components/ln-tabs/src/ln-tabs.js +207 -0
- package/components/ln-tabs/src/tabs-model.js +82 -0
- package/{js → components}/ln-time/README.md +96 -95
- package/components/ln-time/ln-time-dev.scss +13 -0
- package/components/ln-time/ln-time.js +1 -0
- package/components/ln-time/ln-time.schema.json +38 -0
- package/{js → components}/ln-time/src/ln-time.js +206 -223
- package/components/ln-time/src/time-model.js +64 -0
- package/{js → components}/ln-toast/README.md +23 -5
- package/components/ln-toast/ln-toast-dev.scss +8 -0
- package/components/ln-toast/ln-toast.js +1 -0
- package/components/ln-toast/ln-toast.schema.json +65 -0
- package/components/ln-toast/ln-toast.scss +59 -0
- package/{js → components}/ln-toast/src/ln-toast.js +233 -232
- package/{js → components}/ln-toggle/README.md +198 -196
- package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
- package/components/ln-toggle/ln-toggle.js +1 -0
- package/components/ln-toggle/ln-toggle.schema.json +52 -0
- package/components/ln-toggle/ln-toggle.scss +37 -0
- package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
- package/{js → components}/ln-tooltip/README.md +6 -6
- package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
- package/components/ln-tooltip/ln-tooltip.js +1 -0
- package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
- package/components/ln-tooltip/ln-tooltip.scss +9 -0
- package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
- package/{js → components}/ln-translations/README.md +3 -1
- package/components/ln-translations/ln-translations-dev.scss +13 -0
- package/components/ln-translations/ln-translations.js +1 -0
- package/components/ln-translations/ln-translations.schema.json +120 -0
- package/{js → components}/ln-translations/src/ln-translations.js +25 -10
- package/components/ln-ui-coordinator/README.md +101 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
- package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
- package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
- package/components/ln-upload/README.md +133 -0
- package/components/ln-upload/ln-upload-dev.scss +18 -0
- package/components/ln-upload/ln-upload.js +1 -0
- package/components/ln-upload/ln-upload.schema.json +193 -0
- package/components/ln-upload/ln-upload.scss +20 -0
- package/components/ln-upload/src/ln-upload.js +652 -0
- package/components/ln-upload/src/upload-model.js +78 -0
- package/{js → components}/ln-validate/README.md +182 -180
- package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
- package/components/ln-validate/ln-validate.js +1 -0
- package/components/ln-validate/ln-validate.schema.json +50 -0
- package/components/ln-validate/ln-validate.scss +7 -0
- package/{js → components}/ln-validate/src/ln-validate.js +23 -45
- package/components/ln-validate/src/validate-model.js +64 -0
- package/components/ln-websocket-connector/README.md +166 -0
- package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
- package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
- package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
- package/ln-ashlar.html-data.json +1796 -0
- package/package.json +84 -61
- package/{scss → theme}/base/_global.scss +1 -2
- package/{scss → theme}/base/_reset.scss +1 -1
- package/{scss → theme}/base/_typography.scss +2 -0
- package/theme/brand.css +73 -0
- package/{scss → theme}/components/_app-shell.scss +8 -0
- package/theme/components/_button.scss +24 -0
- package/theme/components/_chart.scss +5 -0
- package/theme/components/_confirm.scss +6 -0
- package/{scss → theme}/components/_date.scss +30 -22
- package/{scss → theme}/components/_dropdown.scss +6 -1
- package/theme/components/_form.scss +140 -0
- package/{scss → theme}/components/_layout.scss +6 -0
- package/{scss → theme}/components/_ln-table.scss +1 -6
- package/theme/components/_picklist.scss +3 -0
- package/{scss → theme}/components/_sort.scss +10 -10
- package/theme/components/_status-badge.scss +6 -0
- package/{scss → theme}/components/_table.scss +16 -0
- package/{scss → theme}/components/_tabs.scss +0 -4
- package/theme/components/_toast.scss +48 -0
- package/{scss → theme}/components/_toggle.scss +1 -1
- package/theme/components/_upload.scss +45 -0
- package/{scss → theme}/config/_breakpoints.scss +1 -1
- package/theme/config/_contrast.scss +128 -0
- package/{scss → theme}/config/_density.scss +191 -205
- package/theme/config/_palette.scss +274 -0
- package/theme/config/_theme.scss +293 -0
- package/{scss → theme}/config/_tokens.scss +285 -441
- package/{scss → theme}/config/mixins/_accordion.scss +1 -1
- package/{scss → theme}/config/mixins/_alert.scss +1 -0
- package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
- package/{scss → theme}/config/mixins/_borders.scss +2 -0
- package/theme/config/mixins/_btn.scss +175 -0
- package/{scss → theme}/config/mixins/_card.scss +55 -46
- package/theme/config/mixins/_chart.scss +82 -0
- package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
- package/{scss → theme}/config/mixins/_colors.scss +8 -14
- package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
- package/theme/config/mixins/_dropdown.scss +169 -0
- package/{scss → theme}/config/mixins/_editor.scss +0 -7
- package/{scss → theme}/config/mixins/_filter.scss +47 -47
- package/{scss → theme}/config/mixins/_footer.scss +1 -1
- package/{scss → theme}/config/mixins/_form.scss +49 -18
- package/{scss → theme}/config/mixins/_index.scss +62 -61
- package/{scss → theme}/config/mixins/_layout.scss +6 -2
- package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
- package/{scss → theme}/config/mixins/_loader.scss +1 -1
- package/{scss → theme}/config/mixins/_modal.scss +145 -149
- package/{scss → theme}/config/mixins/_motion.scss +13 -0
- package/{scss → theme}/config/mixins/_nav.scss +7 -7
- package/theme/config/mixins/_picklist.scss +66 -0
- package/theme/config/mixins/_popover.scss +96 -0
- package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
- package/{scss → theme}/config/mixins/_sort.scss +43 -42
- package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
- package/{scss → theme}/config/mixins/_table.scss +25 -14
- package/{scss → theme}/config/mixins/_tabs.scss +1 -1
- package/{scss → theme}/config/mixins/_toast.scss +174 -179
- package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
- package/{scss → theme}/config/mixins/_typography.scss +16 -5
- package/{scss → theme}/config/mixins/_upload.scss +8 -6
- package/theme/ln-ashlar-core.scss +24 -0
- package/theme/ln-ashlar-dev.scss +66 -0
- package/theme/ln-ashlar-landing.scss +56 -0
- package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
- package/theme/ln-ashlar.scss +8 -0
- package/theme/presentation/_bento.scss +151 -0
- package/theme/presentation/_cta.scss +111 -0
- package/theme/presentation/_effects.scss +216 -0
- package/theme/presentation/_features.scss +108 -0
- package/theme/presentation/_footer.scss +129 -0
- package/theme/presentation/_hero.scss +158 -0
- package/theme/presentation/_layout.scss +170 -0
- package/theme/presentation/_navbar.scss +140 -0
- package/theme/presentation/_pricing.scss +262 -0
- package/theme/presentation/_stats.scss +87 -0
- package/theme/presentation/_testimonials.scss +124 -0
- package/theme/presentation/_tokens.scss +39 -0
- package/theme/presentation/_typography.scss +163 -0
- package/{scss → theme}/utilities/_animations.scss +38 -0
- package/{scss → theme}/utilities/_utilities.scss +6 -0
- package/web-types.json +2312 -0
- package/js/ln-accordion/ln-accordion.js +0 -1
- package/js/ln-ajax/ln-ajax.js +0 -1
- package/js/ln-api-connector/ln-api-connector.js +0 -1
- package/js/ln-api-connector/src/ln-api-connector.js +0 -368
- package/js/ln-api-queue/ln-api-queue.js +0 -1
- package/js/ln-autoresize/ln-autoresize.js +0 -1
- package/js/ln-autosave/ln-autosave.js +0 -1
- package/js/ln-circular-progress/ln-circular-progress.js +0 -1
- package/js/ln-confirm/README.md +0 -162
- package/js/ln-confirm/_ln-confirm.scss +0 -24
- package/js/ln-confirm/ln-confirm.js +0 -1
- package/js/ln-core/crypto.js +0 -83
- package/js/ln-core/helpers.js +0 -788
- package/js/ln-core/index.js +0 -7
- package/js/ln-core/persist.js +0 -97
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
- package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
- package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
- package/js/ln-data-store/ln-data-store.js +0 -1
- package/js/ln-date/ln-date.js +0 -1
- package/js/ln-date/src/ln-date.js +0 -603
- package/js/ln-debug/README.md +0 -58
- package/js/ln-debug/ln-debug.js +0 -1
- package/js/ln-debug/src/ln-debug.js +0 -19
- package/js/ln-dropdown/ln-dropdown.js +0 -1
- package/js/ln-dropdown/ln-dropdown.scss +0 -15
- package/js/ln-editor/ln-editor.js +0 -1
- package/js/ln-editor/ln-editor.scss +0 -43
- package/js/ln-external-links/ln-external-links.js +0 -1
- package/js/ln-fill/ln-fill.js +0 -1
- package/js/ln-filter/README.md +0 -189
- package/js/ln-filter/ln-filter-dev.scss +0 -8
- package/js/ln-filter/ln-filter.js +0 -1
- package/js/ln-filter/src/ln-filter.js +0 -419
- package/js/ln-form/ln-form.js +0 -1
- package/js/ln-http/ln-http.js +0 -1
- package/js/ln-link/ln-link.js +0 -1
- package/js/ln-list/README.md +0 -129
- package/js/ln-list/ln-list.js +0 -1
- package/js/ln-list/ln-list.scss +0 -28
- package/js/ln-modal/ln-modal-dev.scss +0 -13
- package/js/ln-modal/ln-modal.js +0 -1
- package/js/ln-modal/ln-modal.scss +0 -35
- package/js/ln-modal-coordinator/README.md +0 -99
- package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
- package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
- package/js/ln-nav/ln-nav.js +0 -1
- package/js/ln-number/README.md +0 -149
- package/js/ln-number/ln-number.js +0 -1
- package/js/ln-number/src/ln-number.js +0 -443
- package/js/ln-options/ln-options.js +0 -1
- package/js/ln-popover/ln-popover.js +0 -1
- package/js/ln-progress/ln-progress.js +0 -1
- package/js/ln-progress/src/ln-progress.js +0 -98
- package/js/ln-router/ln-router.js +0 -1
- package/js/ln-search/README.md +0 -158
- package/js/ln-search/ln-search-dev.scss +0 -8
- package/js/ln-search/ln-search.js +0 -1
- package/js/ln-search/src/ln-search.js +0 -117
- package/js/ln-slug/ln-slug.js +0 -1
- package/js/ln-sort/ln-sort.js +0 -1
- package/js/ln-sort/src/ln-sort.js +0 -178
- package/js/ln-sortable/ln-sortable.js +0 -1
- package/js/ln-stat/ln-stat.js +0 -1
- package/js/ln-table/ln-table.js +0 -1
- package/js/ln-table/ln-table.scss +0 -11
- package/js/ln-table-coordinator/README.md +0 -73
- package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
- package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
- package/js/ln-tabs/ln-tabs-dev.scss +0 -18
- package/js/ln-tabs/ln-tabs.js +0 -1
- package/js/ln-tabs/src/ln-tabs.js +0 -188
- package/js/ln-time/ln-time.js +0 -1
- package/js/ln-toast/ln-toast.js +0 -1
- package/js/ln-toggle/ln-toggle.js +0 -1
- package/js/ln-tooltip/ln-tooltip.js +0 -1
- package/js/ln-tooltip/ln-tooltip.scss +0 -9
- package/js/ln-translations/ln-translations.js +0 -1
- package/js/ln-upload/README.md +0 -255
- package/js/ln-upload/ln-upload.js +0 -1
- package/js/ln-upload/ln-upload.scss +0 -20
- package/js/ln-upload/src/ln-upload.js +0 -423
- package/js/ln-validate/ln-validate.js +0 -1
- package/scss/components/_button.scss +0 -20
- package/scss/components/_confirm.scss +0 -5
- package/scss/components/_form.scss +0 -107
- package/scss/components/_status-badge.scss +0 -4
- package/scss/components/_toast.scss +0 -68
- package/scss/components/_upload.scss +0 -51
- package/scss/config/_theme.scss +0 -216
- package/scss/config/mixins/_btn.scss +0 -179
- package/scss/config/mixins/_confirm.scss +0 -31
- package/scss/config/mixins/_dropdown.scss +0 -141
- package/scss/config/mixins/_popover.scss +0 -64
- package/scss/ln-ashlar-dev.scss +0 -39
- /package/{js → components}/ln-core/positioning.js +0 -0
- /package/{js → components}/ln-core/reactive.js +0 -0
- /package/{js → components}/ln-filter/ln-filter.scss +0 -0
- /package/{js → components}/ln-icon/ln-icon.js +0 -0
- /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
- /package/{js → components}/ln-search/ln-search.scss +0 -0
- /package/{js → components}/ln-toast/template.html +0 -0
- /package/{scss → theme}/components/_accordion.scss +0 -0
- /package/{scss → theme}/components/_ajax.scss +0 -0
- /package/{scss → theme}/components/_alert.scss +0 -0
- /package/{scss → theme}/components/_avatar.scss +0 -0
- /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
- /package/{scss → theme}/components/_card.scss +0 -0
- /package/{scss → theme}/components/_chip.scss +0 -0
- /package/{scss → theme}/components/_circular-progress.scss +0 -0
- /package/{scss → theme}/components/_editor.scss +0 -0
- /package/{scss → theme}/components/_empty-state.scss +0 -0
- /package/{scss → theme}/components/_filter.scss +0 -0
- /package/{scss → theme}/components/_link.scss +0 -0
- /package/{scss → theme}/components/_loader.scss +0 -0
- /package/{scss → theme}/components/_modal.scss +0 -0
- /package/{scss → theme}/components/_nav.scss +0 -0
- /package/{scss → theme}/components/_page-header.scss +0 -0
- /package/{scss → theme}/components/_popover.scss +0 -0
- /package/{scss → theme}/components/_progress.scss +0 -0
- /package/{scss → theme}/components/_prose.scss +0 -0
- /package/{scss → theme}/components/_scrollbar.scss +0 -0
- /package/{scss → theme}/components/_sections.scss +0 -0
- /package/{scss → theme}/components/_sidebar.scss +0 -0
- /package/{scss → theme}/components/_stat-card.scss +0 -0
- /package/{scss → theme}/components/_stepper.scss +0 -0
- /package/{scss → theme}/components/_timeline.scss +0 -0
- /package/{scss → theme}/components/_tooltip.scss +0 -0
- /package/{scss → theme}/components/_translations.scss +0 -0
- /package/{scss → theme}/config/_icons.scss +0 -0
- /package/{scss → theme}/config/_mixins.scss +0 -0
- /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
- /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
- /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
- /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
- /package/{scss → theme}/config/mixins/_chip.scss +0 -0
- /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
- /package/{scss → theme}/config/mixins/_display.scss +0 -0
- /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
- /package/{scss → theme}/config/mixins/_focus.scss +0 -0
- /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
- /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
- /package/{scss → theme}/config/mixins/_link.scss +0 -0
- /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
- /package/{scss → theme}/config/mixins/_position.scss +0 -0
- /package/{scss → theme}/config/mixins/_progress.scss +0 -0
- /package/{scss → theme}/config/mixins/_prose.scss +0 -0
- /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
- /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
- /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
- /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
- /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
- /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
- /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
- /package/{scss → theme}/config/mixins/_translations.scss +0 -0
- /package/{scss → theme}/tabler-icons.txt +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# ln-persist
|
|
2
|
+
|
|
3
|
+
> Applied as a declarative decorator attribute (`data-ln-persist`) to an element owned by a persist-enabled component.
|
|
4
|
+
> On initialization, it reads stored state from `localStorage.getItem(storageKey)` and applies it via `el.setAttribute(attr, value)`.
|
|
5
|
+
> A `MutationObserver` watches the component's state attribute: whenever it changes, it synchronizes the new value back to
|
|
6
|
+
> `localStorage.setItem(storageKey, value)` with zero imperative JS API calls.
|
|
7
|
+
|
|
8
|
+
`data-ln-persist` only has an effect on an element owned by a component that
|
|
9
|
+
declares `options.persist` — currently `ln-toggle`, `ln-tabs`, `ln-sort`,
|
|
10
|
+
`ln-search`, `ln-filter`. On any other element the attribute is a silent
|
|
11
|
+
no-op.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 🧭 Philosophy
|
|
16
|
+
|
|
17
|
+
Persistence used to be five components hand-calling `persistGet`/`persistSet`
|
|
18
|
+
from `ln-core/persist.js` inside their own constructors and attribute-sync
|
|
19
|
+
handlers — coupling every persistence-aware component to that module's exact
|
|
20
|
+
function signatures and JSON-value shape. `ln-persist` inverts this: each
|
|
21
|
+
owning component declares a `persist: { attr, hashActive }` block when it
|
|
22
|
+
calls `registerComponent(...)`, and `ln-persist` does the restoring and
|
|
23
|
+
saving from the outside, through the same shared `findElements` funnel every
|
|
24
|
+
component instance is already constructed through. No owning component
|
|
25
|
+
imports anything from `ln-persist`, and `ln-persist` never imports any
|
|
26
|
+
owning component.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🛠️ Declarative API Contract
|
|
31
|
+
|
|
32
|
+
### HTML Attributes
|
|
33
|
+
|
|
34
|
+
| Attribute | Elements | Description |
|
|
35
|
+
| :--- | :--- | :--- |
|
|
36
|
+
| `data-ln-persist` | Any element owned by a persist-declaring component | Opt-in. Bare = use `el.id` as the storage key. `data-ln-persist="key"` = explicit key override. |
|
|
37
|
+
| `data-ln-persist-scope` | Same as `data-ln-persist` | Opt-in, independent attribute. `"page"` scopes the stored key to the current URL pathname. Absent = global (same key on every page). |
|
|
38
|
+
|
|
39
|
+
There is no Programmatic JS API — `persistGet`/`persistSet`/`persistRemove`/
|
|
40
|
+
`persistClear` do not exist. The attribute is the sole contract.
|
|
41
|
+
|
|
42
|
+
### Storage key format
|
|
43
|
+
|
|
44
|
+
Global by default:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
ln:{id}:{attr}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Page-scoped, via `data-ln-persist-scope="page"`:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
ln:{id}:{pagePath}:{attr}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `id` — the `data-ln-persist="key"` value if non-empty, else `el.id`. Missing
|
|
57
|
+
both → `console.warn('[ln-persist] Element requires id or data-ln-persist="key"', el)`, persistence skipped.
|
|
58
|
+
- `attr` — the owning component's declared `persist.attr` (e.g.
|
|
59
|
+
`data-ln-toggle`, `data-ln-tabs-active`, `data-ln-sort-state`,
|
|
60
|
+
`data-ln-search`, `data-ln-filter-values`).
|
|
61
|
+
- `pagePath` — `location.pathname`, lowercase, trailing slash stripped, or
|
|
62
|
+
`/` for root.
|
|
63
|
+
|
|
64
|
+
This orphans every key written under the previous
|
|
65
|
+
`ln:{component}:{pathname}:{id}` format — harmless leftover localStorage,
|
|
66
|
+
never read again.
|
|
67
|
+
|
|
68
|
+
Persisted values are raw attribute strings — no `JSON.stringify`/`parse`.
|
|
69
|
+
The restore/save unit is always "one DOM attribute's string value," not the
|
|
70
|
+
old composite objects (`{field,column,direction,targetId}` for sort,
|
|
71
|
+
`{key,values}` for filter).
|
|
72
|
+
|
|
73
|
+
### Hash wins
|
|
74
|
+
|
|
75
|
+
When the owning component's `persist.hashActive(el)` returns `true`,
|
|
76
|
+
`ln-persist` skips both restore and save for that element entirely — the URL
|
|
77
|
+
hash is authoritative, never double-written to both hash and `localStorage`.
|
|
78
|
+
This is a behaviour change for `ln-search`/`ln-filter`, which previously
|
|
79
|
+
wrote both; `ln-tabs` was already fully hash-gated on save, `ln-sort` only on
|
|
80
|
+
restore.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## ⚠️ Common Pitfalls
|
|
85
|
+
|
|
86
|
+
- **Persisting without a stable key.** `data-ln-persist` with no value falls
|
|
87
|
+
back to `el.id`; an element with neither silently skips persistence
|
|
88
|
+
(`console.warn`s once).
|
|
89
|
+
- **Expecting `data-ln-persist-scope="page"` alone to persist anything.**
|
|
90
|
+
It only changes the key shape of an element that already has
|
|
91
|
+
`data-ln-persist` — it's not a standalone opt-in.
|
|
92
|
+
- **Expecting a composite persisted value.** Every owner persists exactly one
|
|
93
|
+
attribute's string value now, not an object.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🔧 Internals
|
|
98
|
+
|
|
99
|
+
Source: `components/ln-persist/src/ln-persist.js`. No `DOM_SELECTOR`,
|
|
100
|
+
`DOM_ATTRIBUTE`, or instance — registry-driven, sink-filling module.
|
|
101
|
+
Structurally closest existing precedent: `components/ln-debug/src/gate.js`
|
|
102
|
+
(nullable sink, single module-level idempotency guard, no instance).
|
|
103
|
+
|
|
104
|
+
### The sink
|
|
105
|
+
|
|
106
|
+
`ln-core` exposes a nullable `setPersistSink(sink)`, mirroring
|
|
107
|
+
`setDebugSink` exactly. `findElements` — the single funnel every component
|
|
108
|
+
instance in the library is constructed through (boot sweep, each
|
|
109
|
+
component's own `childList` observer, and the shared observer's legacy
|
|
110
|
+
upgrade path) — calls `window.lnCore._persistSink(el, selector)` for any
|
|
111
|
+
element carrying `data-ln-persist`, immediately before constructing that
|
|
112
|
+
element's owning component instance. `ln-persist` fills the slot with
|
|
113
|
+
`_persistSink`, which looks up the matching entry in `registry.persist`
|
|
114
|
+
(populated by `registerComponent`'s `persist:` option) by `selector`, and —
|
|
115
|
+
unless `persist.hashActive(el)` is true — writes the stored value onto `el`
|
|
116
|
+
with `setAttribute`, synchronously, before the owner's constructor runs.
|
|
117
|
+
|
|
118
|
+
### Ordering — no boot gate
|
|
119
|
+
|
|
120
|
+
`_registerAttrEntry(...)` always runs before that same component's own
|
|
121
|
+
`boot()` — see `ln-core`'s `registerComponent`. So by the time any
|
|
122
|
+
owner's `findElements` call reaches the sink, that owner's own `persist:`
|
|
123
|
+
declaration is already in `registry.persist`, regardless of what order the
|
|
124
|
+
other four owners import in. `ln-persist` only needs to precede the *first*
|
|
125
|
+
owner's import so `setPersistSink(...)` has already run — a plain
|
|
126
|
+
import-order invariant (`components/index.js` imports `ln-persist` before
|
|
127
|
+
`ln-tabs`, the earliest of the five owners), not a gate on anything else's
|
|
128
|
+
boot timing.
|
|
129
|
+
|
|
130
|
+
### Path 3 — elements inserted after initial boot
|
|
131
|
+
|
|
132
|
+
A node inserted later by `ln-include`, `ln-router`, or an `ln-table` render
|
|
133
|
+
hits the same owning component's `childList` observer, which calls the same
|
|
134
|
+
`findElements`, which runs the same sink check. Restores correctly for
|
|
135
|
+
free — no separate mechanism, no gap.
|
|
136
|
+
|
|
137
|
+
### Save-path wiring
|
|
138
|
+
|
|
139
|
+
Wired lazily, the first time the sink is invoked for a given `persist.attr`
|
|
140
|
+
— not at `ln-persist`'s own module evaluation, since no owner has
|
|
141
|
+
registered yet at that point. `observeAttributes([persist.attr], ...)` is
|
|
142
|
+
called once per `persist.attr`, guarded by a module-level `_wiredAttrs` Set.
|
|
143
|
+
The save handler re-derives the key, checks `hashActive`, and writes or
|
|
144
|
+
removes the `localStorage` entry on every subsequent mutation of that
|
|
145
|
+
attribute.
|
|
146
|
+
|
|
147
|
+
### Destroy
|
|
148
|
+
|
|
149
|
+
None — no per-element instance to tear down. This module installs a
|
|
150
|
+
document-level sink for the life of the page, same category as `ln-debug`'s
|
|
151
|
+
gate.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){"use strict";function D(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function C(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let o=null;for(let s=1;s<t.length;s++)if(t[s]&&t[s].nodeType===1){o=t[s];break}if(!C(o))return}e.apply(console,t)}}function L(e){window.lnCore=window.lnCore||{},window.lnCore._persistSink=e}function I(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function b(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){b(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const m={};function F(e,t){m[e]=t}function E(e){return m[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=F,window.lnCore.getDataMapper=E);function R(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,b(function(){new MutationObserver(function(){I(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const y={};function T(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];y[n]=t}function W(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return y[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=T,window.lnCore.getLocaleFallback=W,window.lnCore.ensureLocaleObserver=R);const h={};function B(e,t){h[e]||(h[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=h[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function A(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let r=0;r<n.length;r++){const a=n[r],l=a.getAttribute("data-ln-field");t[l]!=null&&(a.textContent=t[l])}const o=e.querySelectorAll("[data-ln-attr]");for(let r=0;r<o.length;r++){const a=o[r],l=a.getAttribute("data-ln-attr").split(",");for(let u=0;u<l.length;u++){const d=l[u].trim().split(":");if(d.length!==2)continue;const c=d[0].trim(),f=d[1].trim();t[f]!=null&&a.setAttribute(c,t[f])}}const s=e.querySelectorAll("[data-ln-show]");for(let r=0;r<s.length;r++){const a=s[r],l=a.getAttribute("data-ln-show");l in t&&a.classList.toggle("hidden",!t[l])}const i=e.querySelectorAll("[data-ln-class]");for(let r=0;r<i.length;r++){const a=i[r],l=a.getAttribute("data-ln-class").split(",");for(let u=0;u<l.length;u++){const d=l[u].trim().split(":");if(d.length!==2)continue;const c=d[0].trim(),f=d[1].trim();f in t&&a.classList.toggle(c,!!t[f])}}return e}function M(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<n.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[o],t??null),n[o].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)A(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function _(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const i=n.currentNode;i.textContent.indexOf("{{")!==-1&&(i.textContent=i.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(r,a){return t[a]!==void 0?t[a]:""}))}const o=function(i,r){return t[r]!==void 0?t[r]:""},s=Array.from(e.querySelectorAll("*"));e.nodeType===1&&s.push(e);for(let i=0;i<s.length;i++){const r=s[i],a=r.attributes;for(let l=0;l<a.length;l++){const u=a[l];u.value.indexOf("{{")!==-1&&r.setAttribute(u.name,u.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function q(e,t,n,o,s,i){const r={};for(let l=0;l<e.children.length;l++){const u=e.children[l],d=u.getAttribute("data-ln-render-key");d&&(r[d]=u)}const a=document.createDocumentFragment();for(let l=0;l<t.length;l++){const u=t[l],d=String(o(u));let c=r[d];if(c)s(c,u,l);else{const f=B(n,i);if(!f||(_(f,u),c=f.firstElementChild,!c))continue;c.setAttribute("data-ln-render-key",d),s(c,u,l)}a.appendChild(c)}e.textContent="",e.appendChild(a)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=_,window.lnCore.fill=A,window.lnCore.lnFill=M,window.lnCore.renderList=q);function V(e,t,n,o,s){if(!e||t===null)return!0;const i=o||"ln-component",r=e.type;if(r==="trigger"||r==="marker"||r==="string"||r==="list"||!r||t===""&&e.fallback!==void 0)return!0;const a=l=>{s?console.warn(l,s):console.warn(l)};if(r==="boolean"){const l=t.trim().toLowerCase();return l!==""&&l!=="true"&&l!=="false"&&l!=="1"&&l!=="0"?(a(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(r==="enum"){const l=e.values||[];return l.includes(t)?!0:(a(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${l.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(r==="integer"){if(!/^-?\d+$/.test(t))return a(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const l=parseInt(t,10);return e.min!==void 0&&l<e.min?(a(`[${i}] Value ${l} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&l>e.max?(a(`[${i}] Value ${l} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(r==="float")return isNaN(Number(t))?(a(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(r==="json")try{return JSON.parse(t),!0}catch(l){return a(`[${i}] Invalid JSON for attribute "${n}": ${l.message}. Fallback: ${e.fallback}.`),!1}return!0}function j(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function N(e,t,n,o){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",r=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&r.push(e);for(const a of r)if(!a[n]){window.lnCore._persistSink&&a.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(a,t);try{a[n]=new o(a)}catch(l){console.error("["+n+"] init failed",a,l)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function v(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function K(e){const t=v(),n=e.observed||[];for(let o=0;o<n.length;o++){const s=n[o];t.byAttr.has(s)||t.byAttr.set(s,[]),t.byAttr.get(s).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const s=t.byDeclaredAttr.get(o);s.some(i=>i.componentTag===e.componentTag)||s.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=j(e);if(o===null)t.reactiveWildcard.push(e);else for(const s of o)t.byReactive.has(s)||t.byReactive.set(s,[]),t.byReactive.get(s).push(e)}e.persist&&t.persist.push(e)}function k(e,t,n,o){for(let s=0;s<e.length;s++){const i=e[s];if(!t[i.attribute])continue;const r=i.effects&&i.effects[n];r?r(t,n,o):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,o)}}function H(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=v(),s=o.byAttr.get(n);if(D()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&C(t)){const i=o.byDeclaredAttr.get(n),r=t.getAttribute(n);for(let a=0;a<i.length;a++)V(i[a].spec,r,n,i[a].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||s)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=o.byReactive.get(n);i&&k(i,t,n,e.oldValue),o.reactiveWildcard.length&&k(o.reactiveWildcard,t,n,e.oldValue)}if(s)for(let i=0;i<s.length;i++){const r=s[i];if(r.handler){r.handler(t,n,e.oldValue);continue}r.onAttributeChange&&t[r.attribute]?r.onAttributeChange(t,n):(N(t,r.selector,r.attribute,r.ComponentFn),r.onInit&&r.onInit(t))}}function U(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,b(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{H(t[n])}catch(o){console.error("[ln-core] mutation handler failed",t[n].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function G(e,t){K({observed:e,handler:t}),U()}function S(e){return(e||"").replace(/^#/,"")}function p(e){const t=e===void 0?location.hash:e,n={},o=S(t);if(!o)return n;const s=o.split("&");for(let i=0;i<s.length;i++){const r=s[i];if(!r)continue;const a=r.indexOf(":"),l=a>-1?r.slice(0,a):r,u=a>-1?r.slice(a+1):"";if(l)try{n[l]=decodeURIComponent(u)}catch{n[l]=u}}return n}function J(e){if(!e)return null;const t=p();return e in t?t[e]:null}function P(e,t){if(!e)return;const n=p();t==null?delete n[e]:n[e]=String(t);const s=Object.keys(n).map(function(i){const r=n[i];return r===""?i:i+":"+encodeURIComponent(r)}).join("&");S(location.hash)!==s&&(location.hash=s)}function Q(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function X(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function z(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function Y(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function Z(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function ee(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),o=e.slice(t+1),s=o?o.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:s}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=p,window.lnCore.hashGet=J,window.lnCore.hashSet=P,window.lnCore.hashLinkClick=Q,window.lnCore.resolveHashNamespace=X,window.lnCore.hashSortEncode=z,window.lnCore.hashSortDecode=Y,window.lnCore.hashFilterEncode=Z,window.lnCore.hashFilterDecode=ee),(function(){if(window.lnCore&&window.lnCore._persistBound)return;window.lnCore=window.lnCore||{},window.lnCore._persistBound=!0;function e(){return location.pathname.replace(/\/+$/,"").toLowerCase()||"/"}function t(r,a){const l=r.getAttribute("data-ln-persist"),u=l!==null&&l!==""?l:r.id;return u?r.getAttribute("data-ln-persist-scope")==="page"?"ln:"+u+":"+e()+":"+a:"ln:"+u+":"+a:(console.warn('[ln-persist] Element requires id or data-ln-persist="key"',r),null)}let n=null;function o(){if(n!==null)return n;try{if(typeof localStorage>"u")return n=!1;const r="__ln_persist_test__";return localStorage.setItem(r,r),localStorage.removeItem(r),n=!0}catch{return n=!1}}const s=new Set;function i(r,a){const l=window.lnCore&&window.lnCore._attrRegistry,u=l&&l.persist||[];let d=null;for(let w=0;w<u.length;w++)if(u[w].selector===a){d=u[w];break}if(!d)return;const c=d.persist;if(s.has(c.attr)||(s.add(c.attr),G([c.attr],function(w,x){if(!w.hasAttribute("data-ln-persist")||c.hashActive&&c.hashActive(w))return;const g=t(w,x);if(!g||!o())return;const O=w.getAttribute(x);try{O===null?localStorage.removeItem(g):localStorage.setItem(g,O)}catch{}})),c.hashActive&&c.hashActive(r))return;const f=t(r,c.attr);if(!f||!o())return;const $=localStorage.getItem(f);$!==null&&r.setAttribute(c.attr,$)}L(i)})()})();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
|
|
3
|
+
"component": "ln-persist",
|
|
4
|
+
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
+
"attributes": {
|
|
6
|
+
"data-ln-persist": {
|
|
7
|
+
"direction": "author",
|
|
8
|
+
"sources": [
|
|
9
|
+
"src/ln-persist.js"
|
|
10
|
+
],
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Storage key identifier for persisting component state in localStorage"
|
|
13
|
+
},
|
|
14
|
+
"data-ln-persist-scope": {
|
|
15
|
+
"direction": "author",
|
|
16
|
+
"sources": [
|
|
17
|
+
"src/ln-persist.js"
|
|
18
|
+
],
|
|
19
|
+
"type": "enum",
|
|
20
|
+
"values": [
|
|
21
|
+
"global",
|
|
22
|
+
"page"
|
|
23
|
+
],
|
|
24
|
+
"fallback": "global",
|
|
25
|
+
"description": "Persistence scope namespace (global or per-page path)"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { setPersistSink, observeAttributes } from '../../ln-core/index.js';
|
|
2
|
+
|
|
3
|
+
(function () {
|
|
4
|
+
if (window.lnCore && window.lnCore._persistBound) return;
|
|
5
|
+
window.lnCore = window.lnCore || {};
|
|
6
|
+
window.lnCore._persistBound = true;
|
|
7
|
+
|
|
8
|
+
// ─── Attribute Contract (SSOT) ──────────────────────────
|
|
9
|
+
const ATTRIBUTES = {
|
|
10
|
+
'data-ln-persist': { type: 'string', description: 'Storage key identifier for persisting component state in localStorage' },
|
|
11
|
+
'data-ln-persist-scope': { type: 'enum', values: ['global', 'page'], fallback: 'global', description: 'Persistence scope namespace (global or per-page path)' }
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function _pageKey() {
|
|
15
|
+
const path = location.pathname.replace(/\/+$/, '').toLowerCase();
|
|
16
|
+
return path || '/';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// el-first key: ln:{id}:{attr}, or ln:{id}:{pathname}:{attr} when
|
|
20
|
+
// data-ln-persist-scope="page" is present.
|
|
21
|
+
function _resolveKey(el, attr) {
|
|
22
|
+
const explicit = el.getAttribute('data-ln-persist');
|
|
23
|
+
const id = (explicit !== null && explicit !== '') ? explicit : el.id;
|
|
24
|
+
if (!id) {
|
|
25
|
+
console.warn('[ln-persist] Element requires id or data-ln-persist="key"', el);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const scoped = el.getAttribute('data-ln-persist-scope') === 'page';
|
|
29
|
+
return scoped
|
|
30
|
+
? ('ln:' + id + ':' + _pageKey() + ':' + attr)
|
|
31
|
+
: ('ln:' + id + ':' + attr);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let _storageOk = null;
|
|
35
|
+
function _checkStorage() {
|
|
36
|
+
if (_storageOk !== null) return _storageOk;
|
|
37
|
+
try {
|
|
38
|
+
if (typeof localStorage === 'undefined') return (_storageOk = false);
|
|
39
|
+
const t = '__ln_persist_test__';
|
|
40
|
+
localStorage.setItem(t, t);
|
|
41
|
+
localStorage.removeItem(t);
|
|
42
|
+
return (_storageOk = true);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return (_storageOk = false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const _wiredAttrs = new Set();
|
|
49
|
+
|
|
50
|
+
function _persistSink(el, selector) {
|
|
51
|
+
const registry = window.lnCore && window.lnCore._attrRegistry;
|
|
52
|
+
const entries = (registry && registry.persist) || [];
|
|
53
|
+
let entry = null;
|
|
54
|
+
for (let i = 0; i < entries.length; i++) {
|
|
55
|
+
if (entries[i].selector === selector) { entry = entries[i]; break; }
|
|
56
|
+
}
|
|
57
|
+
if (!entry) return;
|
|
58
|
+
const persist = entry.persist;
|
|
59
|
+
|
|
60
|
+
if (!_wiredAttrs.has(persist.attr)) {
|
|
61
|
+
_wiredAttrs.add(persist.attr);
|
|
62
|
+
observeAttributes([persist.attr], function (changedEl, name) {
|
|
63
|
+
if (!changedEl.hasAttribute('data-ln-persist')) return;
|
|
64
|
+
if (persist.hashActive && persist.hashActive(changedEl)) return;
|
|
65
|
+
const key = _resolveKey(changedEl, name);
|
|
66
|
+
if (!key || !_checkStorage()) return;
|
|
67
|
+
const value = changedEl.getAttribute(name);
|
|
68
|
+
try {
|
|
69
|
+
if (value === null) localStorage.removeItem(key);
|
|
70
|
+
else localStorage.setItem(key, value);
|
|
71
|
+
} catch (e) { /* quota / private mode — silent */ }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (persist.hashActive && persist.hashActive(el)) return;
|
|
76
|
+
const key = _resolveKey(el, persist.attr);
|
|
77
|
+
if (!key || !_checkStorage()) return;
|
|
78
|
+
const saved = localStorage.getItem(key);
|
|
79
|
+
if (saved !== null) el.setAttribute(persist.attr, saved);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setPersistSink(_persistSink);
|
|
83
|
+
})();
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# ln-picklist
|
|
2
|
+
|
|
3
|
+
> Applied to a container (`data-ln-picklist`) wrapping `<ul data-ln-picklist-list="available">` and `<ul data-ln-picklist-list="selected">`.
|
|
4
|
+
> It listens for native `change` events on child `<input type="checkbox">`: when `checked` is true, it moves the parent `<li>` via
|
|
5
|
+
> `appendChild` into the `selected` list; when unchecked, it returns it to `available`. Native checkboxes drive both DOM placement
|
|
6
|
+
> and form submission (`name`/`value`) with zero hidden inputs or state mirrors.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🧭 Philosophy & Architecture
|
|
11
|
+
|
|
12
|
+
One parent element (the root) carries the component. It listens for `change`;
|
|
13
|
+
checking an item moves it to the other list, unchecking returns it. That is the
|
|
14
|
+
entire mechanism — there is no second act.
|
|
15
|
+
|
|
16
|
+
Why it is worth having at all: `<select multiple>` is hostile: ctrl-clicking,
|
|
17
|
+
no room to see what you picked, and a scroll position that eats your selection.
|
|
18
|
+
A picklist puts the pool on the left and the choices on the right, both fully
|
|
19
|
+
visible.
|
|
20
|
+
|
|
21
|
+
The component itself is deliberately tiny. **The checkbox is the state.** Its
|
|
22
|
+
`checked` property and the list the `<li>` currently sits in are the same fact,
|
|
23
|
+
and `name` / `value` carry the selection to the server natively — unchecked
|
|
24
|
+
boxes are simply not submitted. So there are no hidden inputs, no state
|
|
25
|
+
attribute and no JS mirror to keep in sync.
|
|
26
|
+
|
|
27
|
+
Everything else composes:
|
|
28
|
+
|
|
29
|
+
| Want | Use |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Filtering a long list | [`ln-search`](../ln-search/README.md) on each `<ul>` |
|
|
32
|
+
| Counts, empty states, "select all" | Your page or a project coordinator |
|
|
33
|
+
| Drag-and-drop ordering | [`ln-sortable`](../ln-sortable/README.md) |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 📦 Minimal Blueprint
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<section data-ln-picklist>
|
|
41
|
+
<ul data-ln-picklist-list="available">
|
|
42
|
+
<li>
|
|
43
|
+
<label>
|
|
44
|
+
<input type="checkbox" name="countries[]" value="de">
|
|
45
|
+
Germany
|
|
46
|
+
</label>
|
|
47
|
+
</li>
|
|
48
|
+
<li>
|
|
49
|
+
<label>
|
|
50
|
+
<input type="checkbox" name="countries[]" value="fr">
|
|
51
|
+
France
|
|
52
|
+
</label>
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
|
|
56
|
+
<ul data-ln-picklist-list="selected"></ul>
|
|
57
|
+
</section>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
On initialization, `ln-picklist` automatically scans all items and transfers any checked
|
|
61
|
+
checkbox (`<input type="checkbox" checked>`) into the `selected` list. All items can be
|
|
62
|
+
authored in the `available` pool from backend templates, leaving `selected` initially
|
|
63
|
+
empty. On form reset, both lists are automatically re-synchronized with `defaultChecked`.
|
|
64
|
+
|
|
65
|
+
### With search on both lists
|
|
66
|
+
|
|
67
|
+
`ln-picklist` has no search of its own. Point an `ln-search` control at each
|
|
68
|
+
list's `id` and it hides non-matching `<li>` children for free:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<section data-ln-picklist>
|
|
72
|
+
<section>
|
|
73
|
+
<h3 id="pool-h">Available</h3>
|
|
74
|
+
|
|
75
|
+
<search aria-label="Search available">
|
|
76
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
77
|
+
<input type="search" data-ln-search-for="pool" placeholder="Search…">
|
|
78
|
+
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
79
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
80
|
+
</button>
|
|
81
|
+
</search>
|
|
82
|
+
|
|
83
|
+
<ul data-ln-picklist-list="available" id="pool" data-ln-search="" aria-labelledby="pool-h">
|
|
84
|
+
…
|
|
85
|
+
</ul>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<section>
|
|
89
|
+
<h3 id="picked-h">Selected</h3>
|
|
90
|
+
<search aria-label="Search selected">
|
|
91
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
92
|
+
<input type="search" data-ln-search-for="picked" placeholder="Search…">
|
|
93
|
+
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
94
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
95
|
+
</button>
|
|
96
|
+
</search>
|
|
97
|
+
|
|
98
|
+
<ul data-ln-picklist-list="selected" id="picked" data-ln-search="" aria-labelledby="picked-h">
|
|
99
|
+
…
|
|
100
|
+
</ul>
|
|
101
|
+
</section>
|
|
102
|
+
</section>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The `change` event of that search input bubbles to the picklist root; the
|
|
106
|
+
component ignores it, because it only acts on checkboxes that live inside one
|
|
107
|
+
of its two lists.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 🛠️ Declarative API Contract
|
|
112
|
+
|
|
113
|
+
### HTML Attributes
|
|
114
|
+
|
|
115
|
+
| Attribute | Element | Values | Description |
|
|
116
|
+
|---|---|---|---|
|
|
117
|
+
| `data-ln-picklist` | Root | `""` \| `"disabled"` | Initializes the component. `"disabled"` blocks moving. |
|
|
118
|
+
| `data-ln-picklist-max` | Root | integer (e.g. `"5"`) | Maximum selection limit. Blocks further additions to `selected` and dispatches `ln-picklist:max-reached`. |
|
|
119
|
+
| `data-ln-picklist-list` | `<ul>` / `<ol>` | `available` \| `selected` | Marks which list is which. Both are required. |
|
|
120
|
+
|
|
121
|
+
There is no attribute on the item. It is found from the checkbox that changed.
|
|
122
|
+
|
|
123
|
+
### JS API
|
|
124
|
+
|
|
125
|
+
Accessed via `element.lnPicklist`:
|
|
126
|
+
|
|
127
|
+
| Helper | Signature | Description |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `enable` | `()` | Allows moving (sets `data-ln-picklist=""`). |
|
|
130
|
+
| `disable` | `()` | Blocks moving (sets `data-ln-picklist="disabled"`). |
|
|
131
|
+
| `sync` | `()` | Synchronizes items between available/selected according to checkbox state. |
|
|
132
|
+
| `destroy` | `()` | Detaches listeners and cleans up the instance property. |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## ⚡ DOM Events
|
|
137
|
+
|
|
138
|
+
All events bubble from the root.
|
|
139
|
+
|
|
140
|
+
### `ln-picklist:max-reached`
|
|
141
|
+
|
|
142
|
+
Fires when an item move to `selected` is blocked because the `data-ln-picklist-max` limit has been reached. The checkbox is automatically reverted.
|
|
143
|
+
|
|
144
|
+
`detail`: `{ max, item, checkbox, count }`
|
|
145
|
+
|
|
146
|
+
### `ln-picklist:before-move`
|
|
147
|
+
|
|
148
|
+
Cancelable. Fires before the item moves. Calling `preventDefault()` aborts the
|
|
149
|
+
move **and reverts the checkbox**, so the DOM never contradicts itself.
|
|
150
|
+
|
|
151
|
+
`detail`: `{ item, from, to, checkbox }`
|
|
152
|
+
|
|
153
|
+
### `ln-picklist:move`
|
|
154
|
+
|
|
155
|
+
Fires after the item has been re-parented.
|
|
156
|
+
|
|
157
|
+
`detail`: `{ item, from, to, checkbox }`
|
|
158
|
+
|
|
159
|
+
### `ln-picklist:enabled` / `ln-picklist:disabled`
|
|
160
|
+
|
|
161
|
+
Fire when the root attribute crosses the `disabled` boundary.
|
|
162
|
+
|
|
163
|
+
`detail`: `{ target }`
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## ⚠️ Common Pitfalls
|
|
168
|
+
|
|
169
|
+
> [!CAUTION]
|
|
170
|
+
> 1. **Rendering a checked item on the left (or an unchecked item on the
|
|
171
|
+
> right).** The component treats `checked` and list membership as one fact.
|
|
172
|
+
> Mismatched markup stays mismatched until the user touches that checkbox.
|
|
173
|
+
> 2. **Omitting one of the two lists.** The component logs a warning and does
|
|
174
|
+
> not attach its listener, rather than failing on the first click. With
|
|
175
|
+
> `data-ln-debug` on `<body>` the diagnostic is drawn in place.
|
|
176
|
+
> 3. **Expecting a name on the item.** The submitted value is the checkbox's
|
|
177
|
+
> own `name`/`value`. Give every checkbox the same array-style `name`
|
|
178
|
+
> (`countries[]`) and a distinct `value`.
|
|
179
|
+
> 4. **Expecting the left list to keep its order.** An unchecked item is
|
|
180
|
+
> appended to the end of the available list, not returned to its original
|
|
181
|
+
> position.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 🔧 Internals
|
|
186
|
+
|
|
187
|
+
### Target filtering
|
|
188
|
+
|
|
189
|
+
`change` bubbles, so anything composed inside the root reaches the listener.
|
|
190
|
+
It acts only when `e.target` resolves to a checkbox whose `<li>` sits directly
|
|
191
|
+
in one of the two known lists.
|
|
192
|
+
|
|
193
|
+
### Disabled
|
|
194
|
+
|
|
195
|
+
The browser toggles the checkbox before any listener runs, so when disabled the
|
|
196
|
+
component *reverts* the toggle rather than merely returning — otherwise the box
|
|
197
|
+
would read checked while the item stayed put.
|
|
198
|
+
|
|
199
|
+
### Focus
|
|
200
|
+
|
|
201
|
+
Re-parenting a node that holds focus blurs it to `<body>` in Chromium, so focus
|
|
202
|
+
is restored to the checkbox after the move — but only when it genuinely had
|
|
203
|
+
focus, so a synthetic `change` from a script cannot steal it.
|
|
204
|
+
|
|
205
|
+
### Attribute-driven enable/disable
|
|
206
|
+
|
|
207
|
+
`data-ln-picklist` is observed by the shared attribute observer; flipping it to
|
|
208
|
+
`"disabled"` and back emits the paired events. No re-initialization.
|
|
209
|
+
|
|
210
|
+
### Form reset
|
|
211
|
+
|
|
212
|
+
Resetting a form restores the controls **silently** — the spec fires no
|
|
213
|
+
`change` and no `input` for them, only `reset` on the form. So the change
|
|
214
|
+
handler never hears about it, and without help the items would sit in whichever
|
|
215
|
+
list the user left them while their boxes flipped back.
|
|
216
|
+
|
|
217
|
+
The component snapshots the authored placement at boot and re-appends each item
|
|
218
|
+
to the list it came from. The work is deferred with `setTimeout(…, 0)`, because
|
|
219
|
+
`reset` fires *before* the controls are restored, and skipped if another
|
|
220
|
+
listener cancelled the reset. Items the page has since removed are not
|
|
221
|
+
resurrected.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 🎨 Styling
|
|
226
|
+
|
|
227
|
+
`@mixin picklist` lays the two lists out side by side and applies
|
|
228
|
+
`check-list-outline` to each — checkbox pills with a **visible, focusable**
|
|
229
|
+
input. `check-list` is deliberately not used: `@mixin pill` hides the input with
|
|
230
|
+
`display: none`, which would drop the control out of the tab order.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@use '../../theme/config/mixins' as *;
|
|
2
|
+
|
|
3
|
+
[data-ln-debug] {
|
|
4
|
+
// 1. Structure Check: a picklist is meaningless without both lists
|
|
5
|
+
[data-ln-picklist]:not(:has([data-ln-picklist-list="available"])) {
|
|
6
|
+
@include dev-inline-error("data-ln-picklist is missing [data-ln-picklist-list='available']");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-ln-picklist]:not(:has([data-ln-picklist-list="selected"])) {
|
|
10
|
+
@include dev-inline-error("data-ln-picklist is missing [data-ln-picklist-list='selected']");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// 2. Orphan Check: a list must live inside a picklist root
|
|
14
|
+
[data-ln-picklist-list]:not([data-ln-picklist] *) {
|
|
15
|
+
@include dev-inline-error("[data-ln-picklist-list] is orphaned (not inside a [data-ln-picklist] root)");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 3. Value Check: the list marker only accepts these two sides
|
|
19
|
+
[data-ln-picklist-list]:not([data-ln-picklist-list="available"], [data-ln-picklist-list="selected"]) {
|
|
20
|
+
@include dev-inline-error("[data-ln-picklist-list] must be \"available\" or \"selected\"");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){"use strict";function L(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function y(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let i=null;for(let r=1;r<t.length;r++)if(t[r]&&t[r].nodeType===1){i=t[r];break}if(!y(i))return}e.apply(console,t)}}function C(e,t,n){const i=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,i),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:i}))}function N(e,t,n){const i=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,i);const r=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:i});return e.dispatchEvent(r),r}function m(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){m(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const T={};function j(e,t){T[e]=t}function V(e){return T[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=j,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,m(function(){new MutationObserver(function(){C(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const $={};function U(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];$[n]=t}function Q(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return $[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=U,window.lnCore.getLocaleFallback=Q,window.lnCore.ensureLocaleObserver=H);const _={};function K(e,t){_[e]||(_[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=_[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function R(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let l=0;l<n.length;l++){const a=n[l],s=a.getAttribute("data-ln-field");t[s]!=null&&(a.textContent=t[s])}const i=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<i.length;l++){const a=i[l],s=a.getAttribute("data-ln-attr").split(",");for(let c=0;c<s.length;c++){const d=s[c].trim().split(":");if(d.length!==2)continue;const u=d[0].trim(),f=d[1].trim();t[f]!=null&&a.setAttribute(u,t[f])}}const r=e.querySelectorAll("[data-ln-show]");for(let l=0;l<r.length;l++){const a=r[l],s=a.getAttribute("data-ln-show");s in t&&a.classList.toggle("hidden",!t[s])}const o=e.querySelectorAll("[data-ln-class]");for(let l=0;l<o.length;l++){const a=o[l],s=a.getAttribute("data-ln-class").split(",");for(let c=0;c<s.length;c++){const d=s[c].trim().split(":");if(d.length!==2)continue;const u=d[0].trim(),f=d[1].trim();f in t&&a.classList.toggle(u,!!t[f])}}return e}function P(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let i=0;i<n.length;i++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[i],t??null),n[i].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)R(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function I(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const o=n.currentNode;o.textContent.indexOf("{{")!==-1&&(o.textContent=o.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(l,a){return t[a]!==void 0?t[a]:""}))}const i=function(o,l){return t[l]!==void 0?t[l]:""},r=Array.from(e.querySelectorAll("*"));e.nodeType===1&&r.push(e);for(let o=0;o<r.length;o++){const l=r[o],a=l.attributes;for(let s=0;s<a.length;s++){const c=a[s];c.value.indexOf("{{")!==-1&&l.setAttribute(c.name,c.value.replace(/\{\{\s*(\w+)\s*\}\}/g,i))}}return e}function G(e,t,n,i,r,o){const l={};for(let s=0;s<e.children.length;s++){const c=e.children[s],d=c.getAttribute("data-ln-render-key");d&&(l[d]=c)}const a=document.createDocumentFragment();for(let s=0;s<t.length;s++){const c=t[s],d=String(i(c));let u=l[d];if(u)r(u,c,s);else{const f=K(n,o);if(!f||(I(f,c),u=f.firstElementChild,!u))continue;u.setAttribute("data-ln-render-key",d),r(u,c,s)}a.appendChild(u)}e.textContent="",e.appendChild(a)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=I,window.lnCore.fill=R,window.lnCore.lnFill=P,window.lnCore.renderList=G);function D(e,t,n,i,r){if(!e||t===null)return!0;const o=i||"ln-component",l=e.type;if(l==="trigger"||l==="marker"||l==="string"||l==="list"||!l||t===""&&e.fallback!==void 0)return!0;const a=s=>{r?console.warn(s,r):console.warn(s)};if(l==="boolean"){const s=t.trim().toLowerCase();return s!==""&&s!=="true"&&s!=="false"&&s!=="1"&&s!=="0"?(a(`[${o}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(l==="enum"){const s=e.values||[];return s.includes(t)?!0:(a(`[${o}] Invalid value "${t}" for attribute "${n}". Allowed: ${s.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(l==="integer"){if(!/^-?\d+$/.test(t))return a(`[${o}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const s=parseInt(t,10);return e.min!==void 0&&s<e.min?(a(`[${o}] Value ${s} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&s>e.max?(a(`[${o}] Value ${s} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(l==="float")return isNaN(Number(t))?(a(`[${o}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(l==="json")try{return JSON.parse(t),!0}catch(s){return a(`[${o}] Invalid JSON for attribute "${n}": ${s.message}. Fallback: ${e.fallback}.`),!1}return!0}function J(e){const t={};for(const n in e){const i=e[n];i&&i.effect&&(t[n]=i.effect)}return Object.keys(t).length?t:null}function X(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function A(e,t,n,i){if(e.nodeType!==1)return;const o=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",l=Array.from(e.querySelectorAll(o));e.matches&&e.matches(o)&&l.push(e);for(const a of l)if(!a[n]){window.lnCore._persistSink&&a.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(a,t);try{a[n]=new i(a)}catch(s){console.error("["+n+"] init failed",a,s)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function z(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function Y(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function q(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function Z(e){const t=q(),n=e.observed||[];for(let i=0;i<n.length;i++){const r=n[i];t.byAttr.has(r)||t.byAttr.set(r,[]),t.byAttr.get(r).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const i in e.attributes){t.byDeclaredAttr.has(i)||t.byDeclaredAttr.set(i,[]);const r=t.byDeclaredAttr.get(i);r.some(o=>o.componentTag===e.componentTag)||r.push({spec:e.attributes[i],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const i=X(e);if(i===null)t.reactiveWildcard.push(e);else for(const r of i)t.byReactive.has(r)||t.byReactive.set(r,[]),t.byReactive.get(r).push(e)}e.persist&&t.persist.push(e)}function F(e,t,n,i){for(let r=0;r<e.length;r++){const o=e[r];if(!t[o.attribute])continue;const l=o.effects&&o.effects[n];l?l(t,n,i):o.onAttrChange&&(!o.declared||o.declared.has(n))&&o.onAttrChange(t,n,i)}}function ee(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const i=q(),r=i.byAttr.get(n);if(L()&&i.byDeclaredAttr&&i.byDeclaredAttr.has(n)&&y(t)){const o=i.byDeclaredAttr.get(n),l=t.getAttribute(n);for(let a=0;a<o.length;a++)D(o[a].spec,l,n,o[a].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||r)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const o=i.byReactive.get(n);o&&F(o,t,n,e.oldValue),i.reactiveWildcard.length&&F(i.reactiveWildcard,t,n,e.oldValue)}if(r)for(let o=0;o<r.length;o++){const l=r[o];if(l.handler){l.handler(t,n,e.oldValue);continue}l.onAttributeChange&&t[l.attribute]?l.onAttributeChange(t,n):(A(t,l.selector,l.attribute,l.ComponentFn),l.onInit&&l.onInit(t))}}function te(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{ee(t[n])}catch(i){console.error("[ln-core] mutation handler failed",t[n].target,i)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function M(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function ne(e){const t=M();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const i=t[n];if(i.onSubtreeChange){const r=i.query,o=e.target.nodeType===1?e.target.matches(r)?e.target:e.target.closest(r):e.target.parentElement?e.target.parentElement.closest(r):null;o&&i.onSubtreeChange(o,e)}}for(let n=0;n<e.addedNodes.length;n++){const i=e.addedNodes[n];if(i.nodeType===1)for(let r=0;r<t.length;r++){const o=t[r];A(i,o.selector,o.attribute,o.ComponentFn),o.onInit&&o.onInit(i)}}for(let n=0;n<e.removedNodes.length;n++){const i=e.removedNodes[n];if(i.nodeType===1)for(let r=0;r<t.length;r++){const o=t[r],l=o.query,a=Array.from(i.querySelectorAll(l));i.matches&&i.matches(l)&&a.push(i);for(let s=0;s<a.length;s++){const c=a[s];if(!document.contains(c)){const d=c[o.attribute];if(d&&typeof d.destroy=="function")try{d.destroy()}catch(u){console.error("["+o.attribute+"] destroy failed",c,u)}delete c[o.attribute]}}}}}}function ie(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const i=t[n];if(i.type==="childList")try{ne(i)}catch(r){console.error("[ln-core] lifecycle handler failed",i.target,r)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function oe(e,t,n,i,r={}){const o=r.extraAttributes||[],l=r.onAttributeChange||null,a=r.onSubtreeChange||null,s=r.onInit||null,c=r.onAttrChange||null,d=r.effects||null,u=r.attributes||null,f=u?J(u):d,h=u?new Set(Object.keys(u)):null,w=r.persist||null;function p(x){const b=x||document.body;if(A(b,e,t,n),u&&L())for(const g in u){const we=u[g],S=Array.from(b.querySelectorAll("["+g+"]"));b.matches&&b.matches("["+g+"]")&&S.push(b);for(let E=0;E<S.length;E++){const O=S[E];y(O)&&D(we,O.getAttribute(g),g,i,O)}}s&&s(b)}const k=[];if(e.indexOf("[")!==-1){const x=/\[([\w-]+)/g;let b;for(;(b=x.exec(e))!==null;)k.push(b[1])}else k.push(e);Z({selector:e,attribute:t,componentTag:i,attributes:u,ComponentFn:n,onInit:s,observed:k.concat(o),onAttributeChange:l,onAttrChange:c,effects:f,declared:h,persist:w}),te();const he=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";M().push({selector:e,attribute:t,ComponentFn:n,onInit:s,onSubtreeChange:a,query:he}),ie(),window[t]=p;function W(){z()>0?Y(function(){p(document.body)}):p(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W):W(),p}function B(e){return(e||"").replace(/^#/,"")}function v(e){const t=e===void 0?location.hash:e,n={},i=B(t);if(!i)return n;const r=i.split("&");for(let o=0;o<r.length;o++){const l=r[o];if(!l)continue;const a=l.indexOf(":"),s=a>-1?l.slice(0,a):l,c=a>-1?l.slice(a+1):"";if(s)try{n[s]=decodeURIComponent(c)}catch{n[s]=c}}return n}function re(e){if(!e)return null;const t=v();return e in t?t[e]:null}function le(e,t){if(!e)return;const n=v();t==null?delete n[e]:n[e]=String(t);const r=Object.keys(n).map(function(o){const l=n[o];return l===""?o:o+":"+encodeURIComponent(l)}).join("&");B(location.hash)!==r&&(location.hash=r)}function se(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function ce(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const i=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return i?t?i+"-"+t:i:t||null}function ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function de(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function ue(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function fe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),i=e.slice(t+1),r=i?i.split(",").map(function(o){try{return decodeURIComponent(o)}catch{return o}}).filter(Boolean):[];return{key:n,values:r}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=v,window.lnCore.hashGet=re,window.lnCore.hashSet=le,window.lnCore.hashLinkClick=se,window.lnCore.resolveHashNamespace=ce,window.lnCore.hashSortEncode=ae,window.lnCore.hashSortDecode=de,window.lnCore.hashFilterEncode=ue,window.lnCore.hashFilterDecode=fe),(function(){const e="data-ln-picklist",t="lnPicklist",n="data-ln-picklist-list",i="data-ln-picklist-max";if(window[t]!==void 0)return;const r={"data-ln-picklist":{type:"enum",values:["enabled","disabled"],fallback:"enabled",effect:a,description:"Controls enabled/disabled state of the dual-list picklist"},"data-ln-picklist-max":{type:"integer",fallback:1/0,min:1,effect:s,description:"Maximum selectable items in the selected list"},"data-ln-picklist-list":{type:"enum",values:["available","selected"],description:"Role marker for available or selected picklist columns"}};function o(c){if(this.dom=c,this.isEnabled=c.getAttribute(e)!=="disabled",this.max=l(c),this.available=c.querySelector("["+n+'="available"]'),this.selected=c.querySelector("["+n+'="selected"]'),!this.available||!this.selected)return console.warn("[ln-picklist] requires both ["+n+'="available"] and ['+n+'="selected"]',c),this;this._onChange=this._onChange.bind(this),c.addEventListener("change",this._onChange),this._initial=[];for(const d of[this.available,this.selected])for(const u of d.children)this._initial.push(u);return this.sync(),this._form=c.closest("form"),this._form&&(this._onFormReset=this._onFormReset.bind(this),this._form.addEventListener("reset",this._onFormReset)),this}o.prototype.destroy=function(){this.dom[t]&&(this._destroyed=!0,this._onChange&&this.dom.removeEventListener("change",this._onChange),this._form&&this._form.removeEventListener("reset",this._onFormReset),delete this.dom[t])},o.prototype.enable=function(){this.dom.setAttribute(e,"")},o.prototype.disable=function(){this.dom.setAttribute(e,"disabled")},o.prototype.sync=function(){if(!this.available||!this.selected)return;const c=Array.from(this.available.children).concat(Array.from(this.selected.children));for(let u=0;u<c.length;u++)this._initial.includes(c[u])||this._initial.push(c[u]);let d=0;for(let u=0;u<this._initial.length;u++){const f=this._initial[u];if(!f.isConnected)continue;const h=f.querySelector('input[type="checkbox"]');if(!h)continue;let w;h.checked?this.max===null||d<this.max?(w=this.selected,d++):(h.checked=!1,w=this.available):w=this.available,w.appendChild(f)}},o.prototype._onFormReset=function(c){const d=this;setTimeout(function(){d._destroyed||c.defaultPrevented||d.sync()},0)},o.prototype._onChange=function(c){const d=c.target.closest('input[type="checkbox"]');if(!d)return;const u=d.closest("li");if(!u)return;const f=u.parentElement;if(f!==this.available&&f!==this.selected)return;if(!this.isEnabled){d.checked=!d.checked;return}const h=d.checked?this.selected:this.available;if(f===h)return;if(h===this.selected&&this.max!==null&&this.selected.children.length>=this.max){d.checked=!d.checked,C(this.dom,"ln-picklist:max-reached",{max:this.max,item:u,checkbox:d,count:this.selected.children.length});return}const w={item:u,from:f,to:h,checkbox:d};if(N(this.dom,"ln-picklist:before-move",w).defaultPrevented){d.checked=!d.checked;return}const p=document.activeElement===d;h.appendChild(u),p&&d.focus(),C(this.dom,"ln-picklist:move",w)};function l(c){const d=c.getAttribute(i);if(d===null||d==="")return null;const u=parseInt(d,10);return isNaN(u)||u<0?null:u}function a(c){const d=c[t];if(!d)return;const u=c.getAttribute(e)!=="disabled";u!==d.isEnabled&&(d.isEnabled=u,C(c,u?"ln-picklist:enabled":"ln-picklist:disabled",{target:c}))}function s(c){const d=c[t];d&&(d.max=l(c))}oe(e,t,o,"ln-picklist",{attributes:r})})()})();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
|
|
3
|
+
"component": "ln-picklist",
|
|
4
|
+
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
+
"attributes": {
|
|
6
|
+
"data-ln-debug": {
|
|
7
|
+
"direction": null,
|
|
8
|
+
"sources": [
|
|
9
|
+
"ln-picklist-dev.scss"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"data-ln-picklist": {
|
|
13
|
+
"direction": "both",
|
|
14
|
+
"sources": [
|
|
15
|
+
"ln-picklist-dev.scss",
|
|
16
|
+
"src/ln-picklist.js"
|
|
17
|
+
],
|
|
18
|
+
"type": "enum",
|
|
19
|
+
"values": [
|
|
20
|
+
"enabled",
|
|
21
|
+
"disabled"
|
|
22
|
+
],
|
|
23
|
+
"fallback": "enabled",
|
|
24
|
+
"description": "Controls enabled/disabled state of the dual-list picklist"
|
|
25
|
+
},
|
|
26
|
+
"data-ln-picklist-list": {
|
|
27
|
+
"direction": "author",
|
|
28
|
+
"sources": [
|
|
29
|
+
"ln-picklist-dev.scss",
|
|
30
|
+
"src/ln-picklist.js"
|
|
31
|
+
],
|
|
32
|
+
"type": "enum",
|
|
33
|
+
"values": [
|
|
34
|
+
"available",
|
|
35
|
+
"selected"
|
|
36
|
+
],
|
|
37
|
+
"description": "Role marker for available or selected picklist columns"
|
|
38
|
+
},
|
|
39
|
+
"data-ln-picklist-max": {
|
|
40
|
+
"direction": "author",
|
|
41
|
+
"sources": [
|
|
42
|
+
"src/ln-picklist.js"
|
|
43
|
+
],
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"min": 1,
|
|
46
|
+
"description": "Maximum selectable items in the selected list"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|