@livenetworks/ashlar 2.0.6 → 2.0.8
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 -641
- package/components/COMPONENTS.md +1116 -1116
- package/components/index.js +81 -81
- package/components/ln-accordion/ln-accordion-dev.scss +8 -8
- package/components/ln-accordion/ln-accordion.js +1 -1
- package/components/ln-accordion/ln-accordion.schema.json +30 -30
- package/components/ln-accordion/src/ln-accordion.js +1 -0
- package/components/ln-ajax/ln-ajax-dev.scss +8 -8
- package/components/ln-ajax/ln-ajax.js +1 -1
- package/components/ln-ajax/ln-ajax.schema.json +30 -30
- package/components/ln-api-connector/ln-api-connector.js +1 -1
- package/components/ln-api-connector/ln-api-connector.schema.json +96 -96
- package/components/ln-api-connector/src/ln-api-connector.js +1 -0
- package/components/ln-api-queue/ln-api-queue.js +1 -1
- package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
- package/components/ln-api-queue/src/ln-api-queue.js +3 -13
- package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
- package/components/ln-autoresize/ln-autoresize.js +1 -1
- package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
- package/components/ln-autosave/ln-autosave-dev.scss +13 -13
- package/components/ln-autosave/ln-autosave.js +1 -1
- package/components/ln-autosave/ln-autosave.schema.json +48 -48
- package/components/ln-autosave/src/autosave-model.js +34 -34
- package/components/ln-chart/ln-chart.js +1 -1
- package/components/ln-chart/ln-chart.schema.json +146 -146
- package/components/ln-chart/src/chart-model.js +154 -154
- package/components/ln-circular-progress/ln-circular-progress.js +1 -1
- package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
- package/components/ln-confirm/ln-confirm-dev.scss +24 -24
- package/components/ln-confirm/ln-confirm.js +1 -1
- package/components/ln-confirm/ln-confirm.schema.json +66 -66
- package/components/ln-confirm/src/ln-confirm.js +178 -178
- package/components/ln-core/crypto.js +286 -265
- package/components/ln-core/dom.js +115 -115
- package/components/ln-core/form.js +136 -136
- package/components/ln-core/helpers.js +33 -33
- package/components/ln-core/http.js +48 -48
- package/components/ln-core/index.js +1 -1
- package/components/ln-core/ln-core.schema.json +109 -109
- package/components/ln-core/locale.js +55 -55
- package/components/ln-core/matching.js +78 -78
- package/components/ln-core/number.js +125 -125
- package/components/ln-core/template.js +206 -206
- package/components/ln-couchdb-connector/README.md +4 -0
- package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
- package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
- package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +19 -1
- package/components/ln-data-coordinator/README.md +9 -38
- package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
- package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
- package/components/ln-data-coordinator/src/ln-data-coordinator.js +428 -1299
- package/components/ln-data-store/README.md +3 -2
- package/components/ln-data-store/ln-data-store-dev.scss +13 -13
- package/components/ln-data-store/ln-data-store.js +1 -1
- package/components/ln-data-store/ln-data-store.schema.json +84 -84
- package/components/ln-data-store/src/data-store-model.js +184 -155
- package/components/ln-data-store/src/ln-data-store.js +58 -11
- package/components/ln-date/README.md +4 -0
- package/components/ln-date/ln-date.js +1 -1
- package/components/ln-date/ln-date.schema.json +92 -92
- package/components/ln-date/src/date-model.js +186 -186
- package/components/ln-date/src/ln-date.js +3 -3
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/ln-debug.schema.json +28 -28
- package/components/ln-debug/src/console-sink.js +19 -19
- package/components/ln-debug/src/gate.js +95 -95
- package/components/ln-debug/src/generated-attributes.js +2 -2
- package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
- package/components/ln-dropdown/ln-dropdown.js +1 -1
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
- package/components/ln-editor/ln-editor.js +1 -1
- package/components/ln-editor/ln-editor.schema.json +58 -58
- package/components/ln-external-links/ln-external-links.js +1 -1
- package/components/ln-external-links/ln-external-links.schema.json +18 -18
- package/components/ln-fill/ln-fill.js +1 -1
- package/components/ln-fill/ln-fill.schema.json +23 -23
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/ln-filter.schema.json +153 -153
- package/components/ln-filter/src/filter-model.js +185 -185
- package/components/ln-form/ln-form-dev.scss +8 -8
- package/components/ln-form/ln-form.js +1 -1
- package/components/ln-form/ln-form.schema.json +45 -45
- package/components/ln-http/ln-http.js +1 -1
- package/components/ln-http/ln-http.schema.json +6 -6
- package/components/ln-icon/ln-icon.schema.json +6 -6
- package/components/ln-include/ln-include.js +1 -1
- package/components/ln-include/ln-include.schema.json +23 -23
- package/components/ln-key/ln-key-dev.scss +18 -18
- package/components/ln-key/ln-key.js +1 -1
- package/components/ln-key/ln-key.schema.json +56 -56
- package/components/ln-key/src/ln-key.js +10 -10
- package/components/ln-link/ln-link-dev.scss +8 -8
- package/components/ln-link/ln-link.js +1 -1
- package/components/ln-link/ln-link.schema.json +22 -22
- package/components/ln-list/README.md +152 -152
- package/components/ln-list/ln-list-dev.scss +8 -8
- package/components/ln-list/ln-list.js +1 -1
- package/components/ln-list/ln-list.schema.json +166 -166
- package/components/ln-list/src/ln-list.js +4 -4
- package/components/ln-modal/README.md +4 -6
- package/components/ln-modal/ln-modal.js +1 -1
- package/components/ln-modal/ln-modal.schema.json +65 -65
- package/components/ln-modal/src/ln-modal.js +28 -8
- package/components/ln-nav/ln-nav.js +1 -1
- package/components/ln-nav/ln-nav.schema.json +25 -25
- package/components/ln-number/ln-number.js +1 -1
- package/components/ln-number/ln-number.schema.json +63 -63
- package/components/ln-number/src/number-model.js +31 -31
- package/components/ln-obfuscator/README.md +60 -60
- package/components/ln-obfuscator/ln-obfuscator.js +1 -1
- package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
- package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
- package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
- package/components/ln-options/ln-options.js +1 -1
- package/components/ln-options/ln-options.schema.json +41 -41
- package/components/ln-options/src/ln-options.js +1 -0
- package/components/ln-persist/README.md +151 -151
- package/components/ln-persist/ln-persist.js +1 -1
- package/components/ln-persist/ln-persist.schema.json +28 -28
- package/components/ln-persist/src/ln-persist.js +83 -83
- package/components/ln-picklist/README.md +230 -230
- package/components/ln-picklist/ln-picklist-dev.scss +22 -22
- package/components/ln-picklist/ln-picklist.js +1 -1
- package/components/ln-picklist/ln-picklist.schema.json +49 -49
- package/components/ln-picklist/src/ln-picklist.js +228 -228
- package/components/ln-popover/README.md +4 -0
- package/components/ln-popover/ln-popover-dev.scss +13 -13
- package/components/ln-popover/ln-popover.js +1 -1
- package/components/ln-popover/ln-popover.schema.json +82 -82
- package/components/ln-popover/src/ln-popover.js +9 -0
- package/components/ln-progress/ln-progress-dev.scss +8 -8
- package/components/ln-progress/ln-progress.js +1 -1
- package/components/ln-progress/ln-progress.schema.json +40 -40
- package/components/ln-router/ln-router.js +1 -1
- package/components/ln-router/ln-router.schema.json +76 -76
- package/components/ln-scroll/README.md +45 -45
- package/components/ln-scroll/ln-scroll.js +1 -1
- package/components/ln-scroll/ln-scroll.schema.json +85 -85
- package/components/ln-scroll/src/ln-scroll.js +150 -150
- package/components/ln-search/README.md +188 -188
- package/components/ln-search/ln-search.js +1 -1
- package/components/ln-search/ln-search.schema.json +103 -103
- package/components/ln-slug/ln-slug.js +1 -1
- package/components/ln-slug/ln-slug.schema.json +23 -23
- package/components/ln-sort/ln-sort.js +1 -1
- package/components/ln-sort/ln-sort.schema.json +104 -104
- package/components/ln-sort/src/ln-sort.js +321 -321
- package/components/ln-sort/src/sort-model.js +117 -117
- package/components/ln-sortable/ln-sortable-dev.scss +13 -13
- package/components/ln-sortable/ln-sortable.js +1 -1
- package/components/ln-sortable/ln-sortable.schema.json +36 -36
- package/components/ln-stat/ln-stat-dev.scss +8 -8
- package/components/ln-stat/ln-stat.js +1 -1
- package/components/ln-stat/ln-stat.schema.json +32 -32
- package/components/ln-stat/src/stat-model.js +28 -28
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +211 -211
- package/components/ln-table/src/ln-table.js +2 -0
- package/components/ln-table/src/table-model.js +98 -98
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
- package/components/ln-table-coordinator/src/ln-table-coordinator.js +3 -3
- package/components/ln-tabs/README.md +2 -2
- package/components/ln-tabs/ln-tabs.js +1 -1
- package/components/ln-tabs/ln-tabs.schema.json +75 -75
- package/components/ln-tabs/src/ln-tabs.js +208 -207
- package/components/ln-tabs/src/tabs-model.js +82 -82
- package/components/ln-time/ln-time-dev.scss +13 -13
- package/components/ln-time/ln-time.js +1 -1
- package/components/ln-time/ln-time.schema.json +38 -38
- package/components/ln-toast/ln-toast.js +1 -1
- package/components/ln-toast/ln-toast.schema.json +65 -65
- package/components/ln-toggle/ln-toggle.js +1 -1
- package/components/ln-toggle/ln-toggle.schema.json +52 -52
- package/components/ln-tooltip/ln-tooltip.js +1 -1
- package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
- package/components/ln-translations/ln-translations.js +1 -1
- package/components/ln-translations/ln-translations.schema.json +120 -120
- package/components/ln-ui-coordinator/README.md +62 -62
- package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
- package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
- package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
- package/components/ln-upload/ln-upload.js +1 -1
- package/components/ln-upload/ln-upload.schema.json +193 -193
- package/components/ln-upload/src/upload-model.js +78 -78
- package/components/ln-validate/ln-validate.js +1 -1
- package/components/ln-validate/ln-validate.schema.json +50 -50
- package/components/ln-validate/src/validate-model.js +64 -64
- package/components/ln-websocket-connector/README.md +166 -166
- package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
- package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
- package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
- package/ln-ashlar.html-data.json +7 -8
- package/package.json +84 -84
- package/theme/brand.css +73 -73
- package/theme/components/_confirm.scss +6 -6
- package/theme/components/_picklist.scss +3 -3
- package/theme/config/_palette.scss +274 -274
- package/theme/config/_theme.scss +292 -292
- package/theme/config/_tokens.scss +285 -285
- package/theme/config/mixins/_index.scss +62 -62
- package/theme/config/mixins/_picklist.scss +66 -66
- package/theme/ln-ashlar-dev.scss +66 -66
- package/theme/ln-ashlar-landing.scss +56 -56
- package/theme/ln-ashlar-theme.scss +63 -63
- package/theme/presentation/_bento.scss +151 -151
- package/theme/presentation/_cta.scss +111 -111
- package/theme/presentation/_effects.scss +216 -216
- package/theme/presentation/_features.scss +108 -108
- package/theme/presentation/_footer.scss +129 -129
- package/theme/presentation/_hero.scss +158 -158
- package/theme/presentation/_layout.scss +170 -170
- package/theme/presentation/_navbar.scss +140 -140
- package/theme/presentation/_pricing.scss +262 -262
- package/theme/presentation/_stats.scss +87 -87
- package/theme/presentation/_testimonials.scss +124 -124
- package/theme/presentation/_tokens.scss +39 -39
- package/theme/presentation/_typography.scss +163 -163
- package/web-types.json +12 -12
|
@@ -1,41 +1,41 @@
|
|
|
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-options",
|
|
4
|
-
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
-
"attributes": {
|
|
6
|
-
"data-ln-debug": {
|
|
7
|
-
"direction": null,
|
|
8
|
-
"sources": [
|
|
9
|
-
"ln-options-dev.scss"
|
|
10
|
-
]
|
|
11
|
-
},
|
|
12
|
-
"data-ln-options": {
|
|
13
|
-
"direction": null,
|
|
14
|
-
"sources": [
|
|
15
|
-
"ln-options-dev.scss",
|
|
16
|
-
"src/ln-options.js"
|
|
17
|
-
],
|
|
18
|
-
"type": "string",
|
|
19
|
-
"fallback": "",
|
|
20
|
-
"description": "Store or coordinator addressing to populate options from"
|
|
21
|
-
},
|
|
22
|
-
"data-ln-options-label": {
|
|
23
|
-
"direction": null,
|
|
24
|
-
"sources": [
|
|
25
|
-
"src/ln-options.js"
|
|
26
|
-
],
|
|
27
|
-
"type": "string",
|
|
28
|
-
"fallback": "name",
|
|
29
|
-
"description": "Field name used for option visible label text"
|
|
30
|
-
},
|
|
31
|
-
"data-ln-options-value": {
|
|
32
|
-
"direction": null,
|
|
33
|
-
"sources": [
|
|
34
|
-
"src/ln-options.js"
|
|
35
|
-
],
|
|
36
|
-
"type": "string",
|
|
37
|
-
"fallback": "id",
|
|
38
|
-
"description": "Field name used for option value attribute"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
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-options",
|
|
4
|
+
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
+
"attributes": {
|
|
6
|
+
"data-ln-debug": {
|
|
7
|
+
"direction": null,
|
|
8
|
+
"sources": [
|
|
9
|
+
"ln-options-dev.scss"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"data-ln-options": {
|
|
13
|
+
"direction": null,
|
|
14
|
+
"sources": [
|
|
15
|
+
"ln-options-dev.scss",
|
|
16
|
+
"src/ln-options.js"
|
|
17
|
+
],
|
|
18
|
+
"type": "string",
|
|
19
|
+
"fallback": "",
|
|
20
|
+
"description": "Store or coordinator addressing to populate options from"
|
|
21
|
+
},
|
|
22
|
+
"data-ln-options-label": {
|
|
23
|
+
"direction": null,
|
|
24
|
+
"sources": [
|
|
25
|
+
"src/ln-options.js"
|
|
26
|
+
],
|
|
27
|
+
"type": "string",
|
|
28
|
+
"fallback": "name",
|
|
29
|
+
"description": "Field name used for option visible label text"
|
|
30
|
+
},
|
|
31
|
+
"data-ln-options-value": {
|
|
32
|
+
"direction": null,
|
|
33
|
+
"sources": [
|
|
34
|
+
"src/ln-options.js"
|
|
35
|
+
],
|
|
36
|
+
"type": "string",
|
|
37
|
+
"fallback": "id",
|
|
38
|
+
"description": "Field name used for option value attribute"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -23,6 +23,7 @@ import { registerComponent, dispatch, defineAttrs, attrSpec, attrStr } from '../
|
|
|
23
23
|
const self = this;
|
|
24
24
|
|
|
25
25
|
this._onSetData = function (e) {
|
|
26
|
+
if (!e.detail) return;
|
|
26
27
|
self._rebuild(e.detail.data || []);
|
|
27
28
|
};
|
|
28
29
|
dom.addEventListener('ln-options:set-data', this._onSetData);
|
|
@@ -1,151 +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.
|
|
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.
|
|
@@ -1 +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)})()})();
|
|
1
|
+
(function(){"use strict";function O(){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 I(e){window.lnCore=window.lnCore||{},window.lnCore._persistSink=e}function L(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 p(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){p(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,p(function(){new MutationObserver(function(){L(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 b={};function M(e,t){b[e]||(b[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=b[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 B(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 x(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=M(n,i);if(!f||(x(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=x,window.lnCore.fill=A,window.lnCore.lnFill=B,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 _(){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 U(e){const t=_(),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 v(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 K(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=_(),s=o.byAttr.get(n);if(O()&&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&&v(i,t,n,e.oldValue),o.reactiveWildcard.length&&v(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 H(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,p(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{K(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){U({observed:e,handler:t}),H()}function k(e){return(e||"").replace(/^#/,"")}function h(e){const t=e===void 0?location.hash:e,n={},o=k(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=h();return e in t?t[e]:null}function P(e,t){if(!e)return;const n=h();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("&");k(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=h,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 te(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.uuid=te),(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,$){if(!w.hasAttribute("data-ln-persist")||c.hashActive&&c.hashActive(w))return;const g=t(w,$);if(!g||!o())return;const D=w.getAttribute($);try{D===null?localStorage.removeItem(g):localStorage.setItem(g,D)}catch{}})),c.hashActive&&c.hashActive(r))return;const f=t(r,c.attr);if(!f||!o())return;const S=localStorage.getItem(f);S!==null&&r.setAttribute(c.attr,S)}I(i)})()})();
|
|
@@ -1,28 +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
|
-
}
|
|
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
|
+
}
|