@lavalogic/scoria 0.37.36 → 0.37.37

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.
@@ -10,9 +10,12 @@
10
10
  *
11
11
  * This helper sidesteps all of that:
12
12
  *
13
- * 1. On open, it reparents the dropdown to `document.body` (saving
14
- * the original parent + next-sibling so it can be restored on
15
- * close).
13
+ * 1. On first open, it reparents the dropdown to `document.body` (or
14
+ * to the nearest open `<dialog>` ancestor) ONCE and leaves it
15
+ * there for the lifetime of the SingleSelect. Moving the element
16
+ * on every open / close raced with the user's mouse click and
17
+ * broke Svelte 5's per-component event delegation, so options
18
+ * could only be selected with the keyboard.
16
19
  * 2. It switches the dropdown to `position: fixed` and sets
17
20
  * `top` / `left` / `width` / `bottom` from the trigger's
18
21
  * `getBoundingClientRect()`.
@@ -23,9 +26,10 @@
23
26
  * `resize` events so the position tracks the trigger while the
24
27
  * dropdown is open. Repositioning is rAF-throttled to keep the
25
28
  * cost off the scroll-event hot path.
26
- * 5. On close, the dropdown is moved back to its original DOM slot
27
- * and the inline positioning styles are cleared, so Svelecte's
28
- * next open re-runs the same flow on a clean element.
29
+ * 5. On close, the inline positioning styles are cleared and the
30
+ * dropdown is hidden with `display: none` (Svelecte will restore
31
+ * its own display rule on the next open via the class flip). The
32
+ * dropdown is NOT moved back to its original parent.
29
33
  *
30
34
  * The helper deliberately does NOT add the `is-open` class or fight
31
35
  * Svelecte's own state machine. Svelecte still owns open / closed
@@ -10,9 +10,12 @@
10
10
  *
11
11
  * This helper sidesteps all of that:
12
12
  *
13
- * 1. On open, it reparents the dropdown to `document.body` (saving
14
- * the original parent + next-sibling so it can be restored on
15
- * close).
13
+ * 1. On first open, it reparents the dropdown to `document.body` (or
14
+ * to the nearest open `<dialog>` ancestor) ONCE and leaves it
15
+ * there for the lifetime of the SingleSelect. Moving the element
16
+ * on every open / close raced with the user's mouse click and
17
+ * broke Svelte 5's per-component event delegation, so options
18
+ * could only be selected with the keyboard.
16
19
  * 2. It switches the dropdown to `position: fixed` and sets
17
20
  * `top` / `left` / `width` / `bottom` from the trigger's
18
21
  * `getBoundingClientRect()`.
@@ -23,9 +26,10 @@
23
26
  * `resize` events so the position tracks the trigger while the
24
27
  * dropdown is open. Repositioning is rAF-throttled to keep the
25
28
  * cost off the scroll-event hot path.
26
- * 5. On close, the dropdown is moved back to its original DOM slot
27
- * and the inline positioning styles are cleared, so Svelecte's
28
- * next open re-runs the same flow on a clean element.
29
+ * 5. On close, the inline positioning styles are cleared and the
30
+ * dropdown is hidden with `display: none` (Svelecte will restore
31
+ * its own display rule on the next open via the class flip). The
32
+ * dropdown is NOT moved back to its original parent.
29
33
  *
30
34
  * The helper deliberately does NOT add the `is-open` class or fight
31
35
  * Svelecte's own state machine. Svelecte still owns open / closed
@@ -139,28 +143,26 @@ export function attachDropdownPortal(options) {
139
143
  throttled.schedule();
140
144
  };
141
145
  // Track whether the dropdown has been moved to the portal target
142
- // at least once. We move it AT MOUNT and leave it there for the
143
- // lifetime of the SingleSelect, regardless of open / close. This
144
- // avoids two bugs the older "move on every open, move back on every
145
- // close" implementation suffered from:
146
+ // at least once. We move it AT FIRST OPEN and leave it there for
147
+ // the lifetime of the SingleSelect, regardless of open / close.
148
+ // This avoids two bugs the older "move on every open, move back on
149
+ // every close" implementation suffered from:
146
150
  // 1. Svelte 5's per-component event delegation walks the live
147
151
  // DOM parentNode chain at dispatch time; moving the dropdown
148
152
  // mid-click left the chain pointing at the old parent and
149
- // Svelecte's `on_click` selection handler never ran, so mouse
150
- // clicks did not select an option (keyboard Enter did,
153
+ // Svelecte's `on_click` selection handler never ran, so
154
+ // mouse clicks did not select an option (keyboard Enter did,
151
155
  // because keydown is wired on the input, not the dropdown).
152
156
  // 2. The MutationObserver that toggled the move based on the
153
157
  // `is-open` class fired between `mousedown` and `click`. The
154
158
  // observer's microtask moved the dropdown back to its
155
- // original parent before the `click` event reached an option,
156
- // so the click resolved against `body` instead of the
157
- // dropdown item.
159
+ // original parent before the `click` event reached an
160
+ // option, so the click resolved against `body` instead of
161
+ // the dropdown item.
158
162
  // Keeping the element in the portal target the whole time also
159
163
  // puts the dropdown in the body's stacking context unconditionally,
160
164
  // which means a `z-index: 10000` actually wins against the table's
161
- // own sticky-footer / pinned-column stacking contexts (the
162
- // `position: fixed`-only variant suffered from that footer being
163
- // painted ABOVE the open option list).
165
+ // own sticky-footer / pinned-column stacking contexts.
164
166
  let didMoveToPortalTarget = false;
165
167
  const moveToPortalTargetOnce = () => {
166
168
  if (didMoveToPortalTarget) {
@@ -222,18 +224,19 @@ export function attachDropdownPortal(options) {
222
224
  // Defensive: if Svelecte left the dropdown without its own
223
225
  // `display: none` (transition-cancel race paths where the
224
226
  // open/close class flip and the inline style update arrive
225
- // out of order), the dropdown would otherwise stay `position:
226
- // static; display: block` in `document.body` and contribute
227
- // its option-list height to the body scroll size, surfacing
228
- // as a stray page-level scrollbar after toggling a select.
227
+ // out of order), the dropdown would otherwise stay
228
+ // `position: static; display: block` in the portal target
229
+ // and contribute its option-list height to the page scroll
230
+ // size, surfacing as a stray page-level scrollbar after
231
+ // toggling a select.
229
232
  if (!dropdown.classList.contains('is-open')) {
230
233
  dropdown.style.display = 'none';
231
234
  }
232
235
  // Note: we do NOT move the dropdown back to its original
233
- // parent. Moving mid-click races with the click event (see the
234
- // `moveToPortalTargetOnce` comment above). The dropdown stays
235
- // in the portal target for the rest of its lifetime and is
236
- // removed on component unmount.
236
+ // parent. Moving mid-click races with the click event (see
237
+ // the `moveToPortalTargetOnce` comment above). The dropdown
238
+ // stays in the portal target for the rest of its lifetime
239
+ // and is removed on component unmount.
237
240
  isPortalled = false;
238
241
  };
239
242
  // Apply / unapply the portal whenever Svelecte toggles the
@@ -262,45 +265,12 @@ export function attachDropdownPortal(options) {
262
265
  observer.disconnect();
263
266
  portalIn();
264
267
  // Final cleanup: the dropdown was moved to the portal target
265
- // at first open and stayed there for the component's lifetime
266
- // (see `moveToPortalTargetOnce`). When the SingleSelect
267
- // unmounts, take the dropdown out of the portal target so it
268
- // is not orphaned in the document.
268
+ // at first open and stayed there for the component's
269
+ // lifetime. When the SingleSelect unmounts, take the dropdown
270
+ // out of the portal target so it is not orphaned in the
271
+ // document.
269
272
  if (didMoveToPortalTarget && dropdown.parentNode) {
270
273
  dropdown.parentNode.removeChild(dropdown);
271
274
  }
272
275
  };
273
276
  }
274
- tion;
275
- of;
276
- mutations;
277
- {
278
- if (mutation.attributeName !== 'class') {
279
- continue;
280
- }
281
- if (dropdown.classList.contains('is-open')) {
282
- portalOut();
283
- }
284
- else {
285
- portalIn();
286
- }
287
- }
288
- ;
289
- observer.observe(dropdown, { attributes: true, attributeFilter: ['class'] });
290
- // Honour the dropdown's initial state in case it mounted already
291
- // open (e.g. `focusOnLoad`).
292
- if (dropdown.classList.contains('is-open')) {
293
- portalOut();
294
- }
295
- return () => {
296
- observer.disconnect();
297
- portalIn();
298
- // Final cleanup: the dropdown was moved to the portal target
299
- // at first open and stayed there for the component's lifetime
300
- // (see `moveToPortalTargetOnce`). When the SingleSelect
301
- // unmounts, take the dropdown out of the portal target so it
302
- // is not orphaned in the document.
303
- if (didMoveToPortalTarget && dropdown.parentNode) {
304
- dropdown.parentNode.removeChild(dropdown);
305
- }
306
- };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.37.36",
4
+ "version": "0.37.37",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },