@livenetworks/ashlar 2.0.7 → 2.0.9

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.
Files changed (213) hide show
  1. package/LICENSE +641 -641
  2. package/components/COMPONENTS.md +1116 -1116
  3. package/components/index.js +81 -81
  4. package/components/ln-accordion/ln-accordion-dev.scss +8 -8
  5. package/components/ln-accordion/ln-accordion.js +1 -1
  6. package/components/ln-accordion/ln-accordion.schema.json +30 -30
  7. package/components/ln-ajax/ln-ajax-dev.scss +8 -8
  8. package/components/ln-ajax/ln-ajax.js +1 -1
  9. package/components/ln-ajax/ln-ajax.schema.json +30 -30
  10. package/components/ln-api-connector/ln-api-connector.js +1 -1
  11. package/components/ln-api-connector/ln-api-connector.schema.json +9 -0
  12. package/components/ln-api-connector/src/ln-api-connector.js +2 -0
  13. package/components/ln-api-queue/ln-api-queue.js +1 -1
  14. package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
  15. package/components/ln-api-queue/src/ln-api-queue.js +3 -13
  16. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
  17. package/components/ln-autoresize/ln-autoresize.js +1 -1
  18. package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
  19. package/components/ln-autosave/ln-autosave-dev.scss +13 -13
  20. package/components/ln-autosave/ln-autosave.js +1 -1
  21. package/components/ln-autosave/ln-autosave.schema.json +48 -48
  22. package/components/ln-autosave/src/autosave-model.js +34 -34
  23. package/components/ln-chart/ln-chart.js +1 -1
  24. package/components/ln-chart/ln-chart.schema.json +146 -146
  25. package/components/ln-chart/src/chart-model.js +154 -154
  26. package/components/ln-circular-progress/ln-circular-progress.js +1 -1
  27. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
  28. package/components/ln-confirm/ln-confirm-dev.scss +24 -24
  29. package/components/ln-confirm/ln-confirm.js +1 -1
  30. package/components/ln-confirm/ln-confirm.schema.json +66 -66
  31. package/components/ln-confirm/src/ln-confirm.js +178 -178
  32. package/components/ln-core/attrs.js +13 -0
  33. package/components/ln-core/crypto.js +286 -265
  34. package/components/ln-core/dom.js +115 -115
  35. package/components/ln-core/form.js +146 -136
  36. package/components/ln-core/helpers.js +33 -33
  37. package/components/ln-core/http.js +48 -48
  38. package/components/ln-core/index.js +1 -1
  39. package/components/ln-core/lifecycle.js +5 -0
  40. package/components/ln-core/ln-core.schema.json +3 -2
  41. package/components/ln-core/locale.js +55 -55
  42. package/components/ln-core/matching.js +78 -78
  43. package/components/ln-core/number.js +125 -125
  44. package/components/ln-core/template.js +206 -206
  45. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  46. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
  47. package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
  48. package/components/ln-data-coordinator/README.md +9 -38
  49. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
  50. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
  51. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
  52. package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
  53. package/components/ln-data-coordinator/src/ln-data-coordinator.js +432 -1308
  54. package/components/ln-data-store/ln-data-store-dev.scss +13 -13
  55. package/components/ln-data-store/ln-data-store.js +1 -1
  56. package/components/ln-data-store/ln-data-store.schema.json +84 -84
  57. package/components/ln-data-store/src/data-store-model.js +184 -184
  58. package/components/ln-data-store/src/ln-data-store.js +1 -1
  59. package/components/ln-date/ln-date.js +1 -1
  60. package/components/ln-date/ln-date.schema.json +92 -92
  61. package/components/ln-date/src/date-model.js +186 -186
  62. package/components/ln-debug/ln-debug.js +1 -1
  63. package/components/ln-debug/ln-debug.schema.json +28 -28
  64. package/components/ln-debug/src/console-sink.js +19 -19
  65. package/components/ln-debug/src/gate.js +95 -95
  66. package/components/ln-debug/src/generated-attributes.js +4 -2
  67. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
  68. package/components/ln-dropdown/ln-dropdown.js +1 -1
  69. package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
  70. package/components/ln-editor/ln-editor.js +1 -1
  71. package/components/ln-editor/ln-editor.schema.json +58 -58
  72. package/components/ln-external-links/ln-external-links.js +1 -1
  73. package/components/ln-external-links/ln-external-links.schema.json +18 -18
  74. package/components/ln-fill/ln-fill.js +1 -1
  75. package/components/ln-fill/ln-fill.schema.json +23 -23
  76. package/components/ln-filter/ln-filter.js +1 -1
  77. package/components/ln-filter/ln-filter.schema.json +153 -153
  78. package/components/ln-filter/src/filter-model.js +185 -185
  79. package/components/ln-form/ln-form-dev.scss +8 -8
  80. package/components/ln-form/ln-form.js +1 -1
  81. package/components/ln-form/ln-form.schema.json +6 -0
  82. package/components/ln-form/src/ln-form.js +21 -0
  83. package/components/ln-http/ln-http.js +1 -1
  84. package/components/ln-http/ln-http.schema.json +6 -6
  85. package/components/ln-icon/ln-icon.schema.json +6 -6
  86. package/components/ln-include/ln-include.js +1 -1
  87. package/components/ln-include/ln-include.schema.json +23 -23
  88. package/components/ln-key/ln-key-dev.scss +18 -18
  89. package/components/ln-key/ln-key.js +1 -1
  90. package/components/ln-key/ln-key.schema.json +56 -56
  91. package/components/ln-key/src/ln-key.js +10 -10
  92. package/components/ln-link/ln-link-dev.scss +8 -8
  93. package/components/ln-link/ln-link.js +1 -1
  94. package/components/ln-link/ln-link.schema.json +22 -22
  95. package/components/ln-list/README.md +152 -152
  96. package/components/ln-list/ln-list-dev.scss +8 -8
  97. package/components/ln-list/ln-list.js +1 -1
  98. package/components/ln-list/ln-list.schema.json +166 -166
  99. package/components/ln-modal/ln-modal.js +1 -1
  100. package/components/ln-modal/ln-modal.schema.json +65 -65
  101. package/components/ln-nav/ln-nav.js +1 -1
  102. package/components/ln-nav/ln-nav.schema.json +25 -25
  103. package/components/ln-number/ln-number.js +1 -1
  104. package/components/ln-number/ln-number.schema.json +63 -63
  105. package/components/ln-number/src/ln-number.js +26 -0
  106. package/components/ln-number/src/number-model.js +31 -31
  107. package/components/ln-obfuscator/README.md +60 -60
  108. package/components/ln-obfuscator/ln-obfuscator.js +1 -1
  109. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
  110. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
  111. package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
  112. package/components/ln-options/ln-options.js +1 -1
  113. package/components/ln-options/ln-options.schema.json +6 -0
  114. package/components/ln-options/src/ln-options.js +4 -4
  115. package/components/ln-persist/README.md +151 -151
  116. package/components/ln-persist/ln-persist.js +1 -1
  117. package/components/ln-persist/ln-persist.schema.json +28 -28
  118. package/components/ln-persist/src/ln-persist.js +83 -83
  119. package/components/ln-picklist/README.md +230 -230
  120. package/components/ln-picklist/ln-picklist-dev.scss +22 -22
  121. package/components/ln-picklist/ln-picklist.js +1 -1
  122. package/components/ln-picklist/ln-picklist.schema.json +49 -49
  123. package/components/ln-picklist/src/ln-picklist.js +228 -228
  124. package/components/ln-popover/ln-popover-dev.scss +13 -13
  125. package/components/ln-popover/ln-popover.js +1 -1
  126. package/components/ln-popover/ln-popover.schema.json +82 -82
  127. package/components/ln-progress/ln-progress-dev.scss +8 -8
  128. package/components/ln-progress/ln-progress.js +1 -1
  129. package/components/ln-progress/ln-progress.schema.json +40 -40
  130. package/components/ln-router/ln-router.js +1 -1
  131. package/components/ln-router/ln-router.schema.json +76 -76
  132. package/components/ln-scroll/README.md +45 -45
  133. package/components/ln-scroll/ln-scroll.js +1 -1
  134. package/components/ln-scroll/ln-scroll.schema.json +85 -85
  135. package/components/ln-scroll/src/ln-scroll.js +150 -150
  136. package/components/ln-search/README.md +188 -188
  137. package/components/ln-search/ln-search.js +1 -1
  138. package/components/ln-search/ln-search.schema.json +103 -103
  139. package/components/ln-slug/ln-slug.js +1 -1
  140. package/components/ln-slug/ln-slug.schema.json +23 -23
  141. package/components/ln-slug/src/ln-slug.js +25 -1
  142. package/components/ln-sort/ln-sort.js +1 -1
  143. package/components/ln-sort/ln-sort.schema.json +104 -104
  144. package/components/ln-sort/src/ln-sort.js +321 -321
  145. package/components/ln-sort/src/sort-model.js +117 -117
  146. package/components/ln-sortable/ln-sortable-dev.scss +13 -13
  147. package/components/ln-sortable/ln-sortable.js +1 -1
  148. package/components/ln-sortable/ln-sortable.schema.json +36 -36
  149. package/components/ln-stat/ln-stat-dev.scss +8 -8
  150. package/components/ln-stat/ln-stat.js +1 -1
  151. package/components/ln-stat/ln-stat.schema.json +32 -32
  152. package/components/ln-stat/src/stat-model.js +28 -28
  153. package/components/ln-table/ln-table.js +1 -1
  154. package/components/ln-table/ln-table.schema.json +211 -211
  155. package/components/ln-table/src/table-model.js +98 -98
  156. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
  157. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
  158. package/components/ln-tabs/ln-tabs.js +1 -1
  159. package/components/ln-tabs/ln-tabs.schema.json +75 -75
  160. package/components/ln-tabs/src/ln-tabs.js +208 -208
  161. package/components/ln-tabs/src/tabs-model.js +82 -82
  162. package/components/ln-time/ln-time-dev.scss +13 -13
  163. package/components/ln-time/ln-time.js +1 -1
  164. package/components/ln-time/ln-time.schema.json +38 -38
  165. package/components/ln-toast/ln-toast.js +1 -1
  166. package/components/ln-toast/ln-toast.schema.json +65 -65
  167. package/components/ln-toggle/ln-toggle.js +1 -1
  168. package/components/ln-toggle/ln-toggle.schema.json +52 -52
  169. package/components/ln-tooltip/ln-tooltip.js +1 -1
  170. package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
  171. package/components/ln-translations/ln-translations.js +1 -1
  172. package/components/ln-translations/ln-translations.schema.json +120 -120
  173. package/components/ln-ui-coordinator/README.md +62 -62
  174. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
  175. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
  176. package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
  177. package/components/ln-upload/ln-upload.js +1 -1
  178. package/components/ln-upload/ln-upload.schema.json +193 -193
  179. package/components/ln-upload/src/upload-model.js +78 -78
  180. package/components/ln-validate/ln-validate.js +1 -1
  181. package/components/ln-validate/ln-validate.schema.json +50 -50
  182. package/components/ln-validate/src/validate-model.js +64 -64
  183. package/components/ln-websocket-connector/README.md +166 -166
  184. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
  185. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
  186. package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
  187. package/ln-ashlar.html-data.json +15 -8
  188. package/package.json +85 -84
  189. package/theme/brand.css +73 -73
  190. package/theme/components/_confirm.scss +6 -6
  191. package/theme/components/_picklist.scss +3 -3
  192. package/theme/config/_palette.scss +274 -274
  193. package/theme/config/_theme.scss +292 -292
  194. package/theme/config/_tokens.scss +285 -285
  195. package/theme/config/mixins/_index.scss +62 -62
  196. package/theme/config/mixins/_picklist.scss +66 -66
  197. package/theme/ln-ashlar-dev.scss +66 -66
  198. package/theme/ln-ashlar-landing.scss +56 -56
  199. package/theme/ln-ashlar-theme.scss +63 -63
  200. package/theme/presentation/_bento.scss +151 -151
  201. package/theme/presentation/_cta.scss +111 -111
  202. package/theme/presentation/_effects.scss +216 -216
  203. package/theme/presentation/_features.scss +108 -108
  204. package/theme/presentation/_footer.scss +129 -129
  205. package/theme/presentation/_hero.scss +158 -158
  206. package/theme/presentation/_layout.scss +170 -170
  207. package/theme/presentation/_navbar.scss +140 -140
  208. package/theme/presentation/_pricing.scss +262 -262
  209. package/theme/presentation/_stats.scss +87 -87
  210. package/theme/presentation/_testimonials.scss +124 -124
  211. package/theme/presentation/_tokens.scss +39 -39
  212. package/theme/presentation/_typography.scss +163 -163
  213. package/web-types.json +26 -12
@@ -1,230 +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.
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.
@@ -1,22 +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
- }
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
+ }
@@ -1 +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})})()})();
1
+ (function(){"use strict";function I(){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 o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function g(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 z(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o);const l=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:o});return e.dispatchEvent(l),l}function C(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){C(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function G(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function X(e,t){const n=!!(t&&t.typed),o=t&&t.exclude,l={},s=e.elements,i={};if(n)for(let a=0;a<s.length;a++){const r=s[a];r.name&&r.type==="checkbox"&&!r.disabled&&(i[r.name]=(i[r.name]||0)+1)}for(let a=0;a<s.length;a++){const r=s[a];if(!(!r.name||r.disabled||r.type==="file"||r.type==="submit"||r.type==="button")&&!(o&&r.matches&&r.matches(o)))if(r.type==="checkbox")n&&i[r.name]===1?l[r.name]=r.checked:(l[r.name]||(l[r.name]=[]),r.checked&&l[r.name].push(r.value));else if(r.type==="radio")r.checked&&(l[r.name]=r.value);else if(r.type==="select-multiple"){l[r.name]=[];for(let c=0;c<r.options.length;c++)r.options[c].selected&&l[r.name].push(r.options[c].value)}else if(n&&r.type==="hidden")l[r.name]=r.value;else if(n&&(r.type==="number"||r.type==="range")){const c=Number(r.value);l[r.name]=r.value===""||isNaN(c)?null:c}else l[r.name]=r.value}return l}function Y(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function Z(e,t){const n=e.elements,o=[],l={};for(let s=0;s<n.length;s++){const i=n[s];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let s=0;s<n.length;s++){const i=n[s];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const a=i.getAttribute("data-ln-fill-as")||i.name;if(!a||!(a in t))continue;const r=t[a];if(i.type==="checkbox"){if(Array.isArray(r))i.checked=r.indexOf(i.value)!==-1;else if(l[i.name]>1){const c=String(r).split(",").map(function(u){return u.trim()});i.checked=c.indexOf(i.value)!==-1}else i.checked=Y(r);o.push(i)}else if(i.type==="radio")i.checked=i.value===String(r),o.push(i);else if(i.type==="select-multiple"){if(Array.isArray(r))for(let c=0;c<i.options.length;c++)i.options[c].selected=r.indexOf(i.options[c].value)!==-1;o.push(i)}else i.value=r,i.tagName==="SELECT"&&r!=null&&i.setAttribute("data-ln-value",r),o.push(i)}return o}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=Z,window.lnCore.serializeForm=X,window.lnCore.resolveFormMethod=G);const T={};function ee(e,t){T[e]=t}function te(e){return T[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=ee,window.lnCore.getDataMapper=te);function ne(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,C(function(){new MutationObserver(function(){g(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const R={};function oe(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];R[n]=t}function re(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return R[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=oe,window.lnCore.getLocaleFallback=re,window.lnCore.ensureLocaleObserver=ne);const k={};function ie(e,t){k[e]||(k[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=k[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function $(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let i=0;i<n.length;i++){const a=n[i],r=a.getAttribute("data-ln-field");t[r]!=null&&(a.textContent=t[r])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const a=o[i],r=a.getAttribute("data-ln-attr").split(",");for(let c=0;c<r.length;c++){const u=r[c].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&a.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const a=l[i],r=a.getAttribute("data-ln-show");r in t&&a.classList.toggle("hidden",!t[r])}const s=e.querySelectorAll("[data-ln-class]");for(let i=0;i<s.length;i++){const a=s[i],r=a.getAttribute("data-ln-class").split(",");for(let c=0;c<r.length;c++){const u=r[c].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&a.classList.toggle(d,!!t[f])}}return e}function le(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)$(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 D(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const s=n.currentNode;s.textContent.indexOf("{{")!==-1&&(s.textContent=s.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,a){return t[a]!==void 0?t[a]:""}))}const o=function(s,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let s=0;s<l.length;s++){const i=l[s],a=i.attributes;for(let r=0;r<a.length;r++){const c=a[r];c.value.indexOf("{{")!==-1&&i.setAttribute(c.name,c.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function se(e,t,n,o,l,s){const i={};for(let r=0;r<e.children.length;r++){const c=e.children[r],u=c.getAttribute("data-ln-render-key");u&&(i[u]=c)}const a=document.createDocumentFragment();for(let r=0;r<t.length;r++){const c=t[r],u=String(o(c));let d=i[u];if(d)l(d,c,r);else{const f=ie(n,s);if(!f||(D(f,c),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),l(d,c,r)}a.appendChild(d)}e.textContent="",e.appendChild(a)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=D,window.lnCore.fill=$,window.lnCore.lnFill=le,window.lnCore.renderList=se);function v(e,t,n){const o=e.getAttribute(t);return o===null?n:o}function M(e,t,n){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?n:o}function q(e,t,n=!1){const o=e.getAttribute(t);if(o===null)return!!n;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function N(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const A=new Map;function B(e,t){const n=(e?e.join("|"):"")+"::"+t;if(A.has(n))return A.get(n);const o=new Set(e||[]),l=function(s,i){const a=s.getAttribute(i);return a!==null&&o.has(a)?a:t};return A.set(n,l),l}function j(e,t,n){const o=parseFloat(e.getAttribute(t));return isNaN(o)?n:o}function W(e,t,n){const o=e.getAttribute(t);if(!o)return n;try{return JSON.parse(o)}catch{return n}}function U(e,t,n,o,l){if(!e||t===null)return!0;const s=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const a=r=>{l?console.warn(r,l):console.warn(r)};if(i==="boolean"){const r=t.trim().toLowerCase();return r!==""&&r!=="true"&&r!=="false"&&r!=="1"&&r!=="0"?(a(`[${s}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const r=e.values||[];return r.includes(t)?!0:(a(`[${s}] Invalid value "${t}" for attribute "${n}". Allowed: ${r.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return a(`[${s}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const r=parseInt(t,10);return e.min!==void 0&&r<e.min?(a(`[${s}] Value ${r} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&r>e.max?(a(`[${s}] Value ${r} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(a(`[${s}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(r){return a(`[${s}] Invalid JSON for attribute "${n}": ${r.message}. Fallback: ${e.fallback}.`),!1}return!0}function ce(e,t,n){for(const o in n){const[l,s,i]=n[o];Object.defineProperty(e,o,{get:function(){return l(t,s,i)},enumerable:!0,configurable:!0})}return e}function ae(e){const t={};for(const n in e){const o=e[n];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=B(o.values,o.fallback);break;case"integer":l=M;break;case"float":l=j;break;case"boolean":l=q;break;case"list":l=N;break;case"json":l=W;break;case"string":default:l=v;break}l||(l=v),t[o.prop]=[l,n,o.fallback]}return t}function ue(e){const t={};for(const n in e){const o=e[n];o&&o.effect&&(t[n]=o.effect)}return Object.keys(t).length?t:null}function de(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}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=ce,window.lnCore.attrSpec=ae,window.lnCore.attrStr=v,window.lnCore.attrInt=M,window.lnCore.attrBool=q,window.lnCore.attrList=N,window.lnCore.attrEnum=B,window.lnCore.attrFloat=j,window.lnCore.attrJson=W);function x(e,t,n,o){if(e.nodeType!==1)return;const s=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i=Array.from(e.querySelectorAll(s));e.matches&&e.matches(s)&&i.push(e);for(const a of i)if(!a[n]){window.lnCore._persistSink&&a.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(a,t);try{a[n]=new o(a)}catch(r){console.error("["+n+"] init failed",a,r)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function fe(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function he(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 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 we(e){const t=V(),n=e.observed||[];for(let o=0;o<n.length;o++){const l=n[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).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 l=t.byDeclaredAttr.get(o);l.some(s=>s.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=de(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function H(e,t,n,o){for(let l=0;l<e.length;l++){const s=e[l];if(!t[s.attribute])continue;const i=s.effects&&s.effects[n];i?i(t,n,o):s.onAttrChange&&(!s.declared||s.declared.has(n))&&s.onAttrChange(t,n,o)}}function be(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=V(),l=o.byAttr.get(n);if(I()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&y(t)){const s=o.byDeclaredAttr.get(n),i=t.getAttribute(n);for(let a=0;a<s.length;a++)U(s[a].spec,i,n,s[a].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||l)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const s=o.byReactive.get(n);s&&H(s,t,n,e.oldValue),o.reactiveWildcard.length&&H(o.reactiveWildcard,t,n,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const i=l[s];if(i.handler){i.handler(t,n,e.oldValue);continue}i.onAttributeChange&&t[i.attribute]?i.onAttributeChange(t,n):(x(t,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(t))}}function pe(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,C(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{be(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 J(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function me(e){const t=J();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const o=t[n];if(o.onSubtreeChange){const l=o.query,s=e.target.nodeType===1?e.target.matches(l)?e.target:e.target.closest(l):e.target.parentElement?e.target.parentElement.closest(l):null;s&&o.onSubtreeChange(s,e)}}for(let n=0;n<e.addedNodes.length;n++){const o=e.addedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const s=t[l];x(o,s.selector,s.attribute,s.ComponentFn),s.onInit&&s.onInit(o)}}for(let n=0;n<e.removedNodes.length;n++){const o=e.removedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const s=t[l],i=s.query,a=Array.from(o.querySelectorAll(i));o.matches&&o.matches(i)&&a.push(o);for(let r=0;r<a.length;r++){const c=a[r];if(!document.contains(c)){const u=c[s.attribute];if(u&&typeof u.destroy=="function")try{u.destroy()}catch(d){console.error("["+s.attribute+"] destroy failed",c,d)}delete c[s.attribute]}}}}}}function ge(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,C(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const o=t[n];if(o.type==="childList")try{me(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function K(e,t,n,o,l={}){const s=l.extraAttributes||[],i=l.onAttributeChange||null,a=l.onSubtreeChange||null,r=l.onInit||null,c=l.onAttrChange||null,u=l.effects||null,d=l.attributes||null,f=d?ue(d):u,h=d?new Set(Object.keys(d)):null,w=l.persist||null;function p(E){const b=E||document.body;if(x(b,e,t,n),d&&I())for(const m in d){const Le=d[m],O=Array.from(b.querySelectorAll("["+m+"]"));b.matches&&b.matches("["+m+"]")&&O.push(b);for(let L=0;L<O.length;L++){const F=O[L];y(F)&&U(Le,F.getAttribute(m),m,o,F)}}r&&r(b)}const S=[];if(e.indexOf("[")!==-1){const E=/\[([\w-]+)/g;let b;for(;(b=E.exec(e))!==null;)S.push(b[1])}else S.push(e);we({selector:e,attribute:t,componentTag:o,attributes:d,ComponentFn:n,onInit:r,observed:S.concat(s),onAttributeChange:i,onAttrChange:c,effects:f,declared:h,persist:w}),pe();const Oe=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";J().push({selector:e,attribute:t,ComponentFn:n,onInit:r,onSubtreeChange:a,query:Oe}),ge(),window[t]=p,typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerComponent=K);function Q(){fe()>0?he(function(){p(document.body)}):p(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Q):Q(),p}function P(e){return(e||"").replace(/^#/,"")}function _(e){const t=e===void 0?location.hash:e,n={},o=P(t);if(!o)return n;const l=o.split("&");for(let s=0;s<l.length;s++){const i=l[s];if(!i)continue;const a=i.indexOf(":"),r=a>-1?i.slice(0,a):i,c=a>-1?i.slice(a+1):"";if(r)try{n[r]=decodeURIComponent(c)}catch{n[r]=c}}return n}function Ce(e){if(!e)return null;const t=_();return e in t?t[e]:null}function ye(e,t){if(!e)return;const n=_();t==null?delete n[e]:n[e]=String(t);const l=Object.keys(n).map(function(s){const i=n[s];return i===""?s:s+":"+encodeURIComponent(i)}).join("&");P(location.hash)!==l&&(location.hash=l)}function ke(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function ve(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 Ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function xe(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 _e(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Se(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),l=o?o.split(",").map(function(s){try{return decodeURIComponent(s)}catch{return s}}).filter(Boolean):[];return{key:n,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=_,window.lnCore.hashGet=Ce,window.lnCore.hashSet=ye,window.lnCore.hashLinkClick=ke,window.lnCore.resolveHashNamespace=ve,window.lnCore.hashSortEncode=Ae,window.lnCore.hashSortDecode=xe,window.lnCore.hashFilterEncode=_e,window.lnCore.hashFilterDecode=Se);function Ee(){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=Ee),(function(){const e="data-ln-picklist",t="lnPicklist",n="data-ln-picklist-list",o="data-ln-picklist-max";if(window[t]!==void 0)return;const l={"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:r,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 s(c){if(this.dom=c,this.isEnabled=c.getAttribute(e)!=="disabled",this.max=i(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 u of[this.available,this.selected])for(const d of u.children)this._initial.push(d);return this.sync(),this._form=c.closest("form"),this._form&&(this._onFormReset=this._onFormReset.bind(this),this._form.addEventListener("reset",this._onFormReset)),this}s.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])},s.prototype.enable=function(){this.dom.setAttribute(e,"")},s.prototype.disable=function(){this.dom.setAttribute(e,"disabled")},s.prototype.sync=function(){if(!this.available||!this.selected)return;const c=Array.from(this.available.children).concat(Array.from(this.selected.children));for(let d=0;d<c.length;d++)this._initial.includes(c[d])||this._initial.push(c[d]);let u=0;for(let d=0;d<this._initial.length;d++){const f=this._initial[d];if(!f.isConnected)continue;const h=f.querySelector('input[type="checkbox"]');if(!h)continue;let w;h.checked?this.max===null||u<this.max?(w=this.selected,u++):(h.checked=!1,w=this.available):w=this.available,w.appendChild(f)}},s.prototype._onFormReset=function(c){const u=this;setTimeout(function(){u._destroyed||c.defaultPrevented||u.sync()},0)},s.prototype._onChange=function(c){const u=c.target.closest('input[type="checkbox"]');if(!u)return;const d=u.closest("li");if(!d)return;const f=d.parentElement;if(f!==this.available&&f!==this.selected)return;if(!this.isEnabled){u.checked=!u.checked;return}const h=u.checked?this.selected:this.available;if(f===h)return;if(h===this.selected&&this.max!==null&&this.selected.children.length>=this.max){u.checked=!u.checked,g(this.dom,"ln-picklist:max-reached",{max:this.max,item:d,checkbox:u,count:this.selected.children.length});return}const w={item:d,from:f,to:h,checkbox:u};if(z(this.dom,"ln-picklist:before-move",w).defaultPrevented){u.checked=!u.checked;return}const p=document.activeElement===u;h.appendChild(d),p&&u.focus(),g(this.dom,"ln-picklist:move",w)};function i(c){const u=c.getAttribute(o);if(u===null||u==="")return null;const d=parseInt(u,10);return isNaN(d)||d<0?null:d}function a(c){const u=c[t];if(!u)return;const d=c.getAttribute(e)!=="disabled";d!==u.isEnabled&&(u.isEnabled=d,g(c,d?"ln-picklist:enabled":"ln-picklist:disabled",{target:c}))}function r(c){const u=c[t];u&&(u.max=i(c))}K(e,t,s,"ln-picklist",{attributes:l})})()})();