svelte 5.56.9 → 5.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/index.js +1 -1
- package/elements.d.ts +3 -0
- package/package.json +3 -3
- package/src/compiler/migrate/index.js +6 -1
- package/src/compiler/phases/1-parse/acorn.js +1 -1
- package/src/compiler/phases/1-parse/read/style.js +16 -6
- package/src/compiler/phases/1-parse/utils/html.js +12 -7
- package/src/compiler/phases/2-analyze/css/css-prune.js +10 -0
- package/src/compiler/phases/2-analyze/index.js +45 -18
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +10 -4
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +10 -10
- package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +14 -2
- package/src/compiler/phases/2-analyze/visitors/shared/element.js +2 -8
- package/src/compiler/phases/2-analyze/visitors/shared/utils.js +23 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +2 -2
- package/src/compiler/phases/3-transform/client/transform-template/index.js +6 -0
- package/src/compiler/phases/3-transform/client/utils.js +35 -8
- package/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +16 -3
- package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +6 -6
- package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +10 -13
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +19 -8
- package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +3 -2
- package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +4 -1
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -7
- package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +119 -46
- package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +0 -1
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +11 -4
- package/src/compiler/phases/3-transform/client/visitors/shared/element.js +6 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -6
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +5 -5
- package/src/compiler/phases/3-transform/css/index.js +5 -1
- package/src/compiler/phases/3-transform/server/transform-server.js +20 -9
- package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +8 -2
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +4 -4
- package/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js +1 -0
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +9 -4
- package/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +4 -2
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +53 -47
- package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +31 -1
- package/src/compiler/phases/bindings.js +3 -1
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/print/index.js +95 -24
- package/src/easing/index.js +62 -0
- package/src/internal/client/constants.js +9 -0
- package/src/internal/client/context.js +33 -56
- package/src/internal/client/dev/hmr.js +15 -4
- package/src/internal/client/dom/blocks/boundary.js +21 -12
- package/src/internal/client/dom/blocks/css-props.js +3 -3
- package/src/internal/client/dom/blocks/slot.js +19 -0
- package/src/internal/client/dom/blocks/svelte-head.js +8 -0
- package/src/internal/client/dom/css.js +6 -1
- package/src/internal/client/dom/elements/attributes.js +37 -35
- package/src/internal/client/dom/elements/bindings/select.js +99 -9
- package/src/internal/client/dom/elements/bindings/this.js +7 -2
- package/src/internal/client/dom/elements/events.js +19 -1
- package/src/internal/client/dom/elements/transitions.js +81 -68
- package/src/internal/client/dom/operations.js +21 -1
- package/src/internal/client/error-handling.js +10 -2
- package/src/internal/client/index.js +9 -2
- package/src/internal/client/proxy.js +8 -3
- package/src/internal/client/reactivity/async.js +23 -3
- package/src/internal/client/reactivity/batch.js +1 -0
- package/src/internal/client/reactivity/effects.js +14 -1
- package/src/internal/client/reactivity/sources.js +7 -1
- package/src/internal/client/render.js +4 -8
- package/src/internal/client/runtime.js +45 -33
- package/src/internal/client/warnings.js +0 -11
- package/src/internal/server/abort-signal.js +8 -9
- package/src/internal/server/context.js +9 -48
- package/src/internal/server/index.js +1 -1
- package/src/internal/server/renderer.js +164 -52
- package/src/internal/shared/attributes.js +4 -2
- package/src/internal/shared/clone.js +2 -0
- package/src/internal/shared/context.js +53 -0
- package/src/internal/shared/errors.js +2 -2
- package/src/legacy/legacy-server.js +1 -1
- package/src/reactivity/map.js +22 -0
- package/src/reactivity/url.js +2 -0
- package/src/transition/index.js +18 -7
- package/src/version.js +1 -1
- package/types/index.d.ts +148 -46
- package/types/index.d.ts.map +2 -2
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
/** @import { Effect, Source, TemplateNode, } from '#client' */
|
|
2
|
-
import {
|
|
3
|
-
BOUNDARY_EFFECT,
|
|
4
|
-
DIRTY,
|
|
5
|
-
EFFECT_PRESERVED,
|
|
6
|
-
EFFECT_TRANSPARENT,
|
|
7
|
-
MAYBE_DIRTY
|
|
8
|
-
} from '#client/constants';
|
|
2
|
+
import { BOUNDARY_EFFECT, EFFECT_PRESERVED, EFFECT_TRANSPARENT } from '#client/constants';
|
|
9
3
|
import { HYDRATION_START_ELSE, HYDRATION_START_FAILED } from '../../../../constants.js';
|
|
10
4
|
import { component_context, set_component_context } from '../../context.js';
|
|
11
|
-
import {
|
|
5
|
+
import { invoke_error_boundary } from '../../error-handling.js';
|
|
12
6
|
import {
|
|
13
7
|
block,
|
|
14
8
|
branch,
|
|
@@ -271,13 +265,31 @@ export class Boundary {
|
|
|
271
265
|
queue_micro_task(() => {
|
|
272
266
|
var fragment = (this.#offscreen_fragment = document.createDocumentFragment());
|
|
273
267
|
var anchor = create_text();
|
|
268
|
+
var handled = false;
|
|
274
269
|
|
|
275
270
|
fragment.append(anchor);
|
|
276
271
|
|
|
277
272
|
this.#main_effect = this.#run(() => {
|
|
278
|
-
|
|
273
|
+
try {
|
|
274
|
+
return branch(() => this.#children(anchor));
|
|
275
|
+
} catch (error) {
|
|
276
|
+
try {
|
|
277
|
+
this.error(error);
|
|
278
|
+
handled = true;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
invoke_error_boundary(error, this.#effect.parent);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
279
285
|
});
|
|
280
286
|
|
|
287
|
+
if (this.#main_effect === null) {
|
|
288
|
+
this.#offscreen_fragment = null;
|
|
289
|
+
if (handled) this.#resolve(/** @type {Batch} */ (current_batch));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
281
293
|
if (this.#pending_count === 0) {
|
|
282
294
|
this.#anchor.before(fragment);
|
|
283
295
|
this.#offscreen_fragment = null;
|
|
@@ -362,9 +374,6 @@ export class Boundary {
|
|
|
362
374
|
try {
|
|
363
375
|
Batch.ensure();
|
|
364
376
|
return fn();
|
|
365
|
-
} catch (e) {
|
|
366
|
-
handle_error(e);
|
|
367
|
-
return null;
|
|
368
377
|
} finally {
|
|
369
378
|
set_active_effect(previous_effect);
|
|
370
379
|
set_active_reaction(previous_reaction);
|
|
@@ -18,10 +18,10 @@ export function css_props(element, get_styles) {
|
|
|
18
18
|
for (var key in styles) {
|
|
19
19
|
var value = styles[key];
|
|
20
20
|
|
|
21
|
-
if (value) {
|
|
22
|
-
element.style.setProperty(key, value);
|
|
23
|
-
} else {
|
|
21
|
+
if (value == null || value === '') {
|
|
24
22
|
element.style.removeProperty(key);
|
|
23
|
+
} else {
|
|
24
|
+
element.style.setProperty(key, value);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { hydrate_next, hydrating } from '../hydration.js';
|
|
2
|
+
import { create_element, create_text } from '../operations.js';
|
|
3
|
+
import { append } from '../template.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* @param {Comment} anchor
|
|
@@ -12,6 +14,23 @@ export function slot(anchor, $$props, name, slot_props, fallback_fn) {
|
|
|
12
14
|
hydrate_next();
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
// Custom element slots are native DOM slots.
|
|
18
|
+
// Use the stored reference because the shadow root may be closed.
|
|
19
|
+
if ($$props.$$host?.$$shadowRoot) {
|
|
20
|
+
const element = create_element('slot');
|
|
21
|
+
if (name !== 'default') element.name = name;
|
|
22
|
+
|
|
23
|
+
append(anchor, element);
|
|
24
|
+
|
|
25
|
+
if (fallback_fn !== null) {
|
|
26
|
+
const fallback_anchor = create_text();
|
|
27
|
+
element.append(fallback_anchor);
|
|
28
|
+
fallback_fn(fallback_anchor);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
15
34
|
var slot_fn = $$props.$$slots?.[name];
|
|
16
35
|
// Interop: Can use snippets to fill slots
|
|
17
36
|
var is_interop = false;
|
|
@@ -52,6 +52,14 @@ export function head(hash, render_fn) {
|
|
|
52
52
|
block(() => {
|
|
53
53
|
var e = branch(() => render_fn(anchor));
|
|
54
54
|
e.f |= HEAD_EFFECT;
|
|
55
|
+
|
|
56
|
+
if (!hydrating) {
|
|
57
|
+
if (e.nodes === null) {
|
|
58
|
+
e.nodes = { start: anchor, end: anchor, a: null, t: null };
|
|
59
|
+
} else {
|
|
60
|
+
e.nodes.end = anchor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
55
63
|
});
|
|
56
64
|
} finally {
|
|
57
65
|
if (was_hydrating) {
|
|
@@ -2,14 +2,19 @@ import { DEV } from 'esm-env';
|
|
|
2
2
|
import { register_style } from '../dev/css.js';
|
|
3
3
|
import { effect } from '../reactivity/effects.js';
|
|
4
4
|
import { create_element } from './operations.js';
|
|
5
|
+
import { active_effect } from '../runtime.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {Node} anchor
|
|
8
9
|
* @param {{ hash: string, code: string }} css
|
|
9
10
|
*/
|
|
10
11
|
export function append_styles(anchor, css) {
|
|
11
|
-
// Use
|
|
12
|
+
// Use an effect to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results
|
|
12
13
|
effect(() => {
|
|
14
|
+
// Bit of a hack: branches.js/each.js use offscreen fragments with temporary text nodes that will
|
|
15
|
+
// never be connected to the real dom. Therfore walk up to the branch that has created the component
|
|
16
|
+
// whose styles we want to append, and check its node instead. It will be connected by the time we get here.
|
|
17
|
+
anchor = active_effect?.parent?.nodes?.start ?? anchor;
|
|
13
18
|
var root = anchor.getRootNode();
|
|
14
19
|
|
|
15
20
|
var target = /** @type {ShadowRoot} */ (root).host
|
|
@@ -26,7 +26,12 @@ import { set_class } from './class.js';
|
|
|
26
26
|
import { set_style } from './style.js';
|
|
27
27
|
import { ATTACHMENT_KEY, NAMESPACE_HTML, UNINITIALIZED } from '../../../../constants.js';
|
|
28
28
|
import { branch, destroy_effect, effect, managed } from '../../reactivity/effects.js';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
init_select,
|
|
31
|
+
select_option,
|
|
32
|
+
set_default_select_value,
|
|
33
|
+
set_selected
|
|
34
|
+
} from './bindings/select.js';
|
|
30
35
|
import { flatten } from '../../reactivity/async.js';
|
|
31
36
|
|
|
32
37
|
export const CLASS = Symbol('class');
|
|
@@ -122,25 +127,6 @@ export function set_checked(element, checked) {
|
|
|
122
127
|
element.checked = checked;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
/**
|
|
126
|
-
* Sets the `selected` attribute on an `option` element.
|
|
127
|
-
* Not set through the property because that doesn't reflect to the DOM,
|
|
128
|
-
* which means it wouldn't be taken into account when a form is reset.
|
|
129
|
-
* @param {HTMLOptionElement} element
|
|
130
|
-
* @param {boolean} selected
|
|
131
|
-
*/
|
|
132
|
-
export function set_selected(element, selected) {
|
|
133
|
-
if (selected) {
|
|
134
|
-
// The selected option could've changed via user selection, and
|
|
135
|
-
// setting the value without this check would set it back.
|
|
136
|
-
if (!element.hasAttribute('selected')) {
|
|
137
|
-
element.setAttribute('selected', '');
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
element.removeAttribute('selected');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
130
|
/**
|
|
145
131
|
* Applies the default checked property without influencing the current checked property.
|
|
146
132
|
* @param {HTMLInputElement} element
|
|
@@ -201,7 +187,7 @@ export function set_attribute(element, attribute, value, skip_warning) {
|
|
|
201
187
|
|
|
202
188
|
if (value == null) {
|
|
203
189
|
element.removeAttribute(attribute);
|
|
204
|
-
} else if (typeof value !== 'string' && get_setters(element).
|
|
190
|
+
} else if (typeof value !== 'string' && get_setters(element).has(attribute)) {
|
|
205
191
|
// @ts-ignore
|
|
206
192
|
element[attribute] = value;
|
|
207
193
|
} else {
|
|
@@ -252,7 +238,7 @@ export function set_custom_element_data(node, prop, value) {
|
|
|
252
238
|
// customElements may not be available in browser extension contexts
|
|
253
239
|
!customElements ||
|
|
254
240
|
customElements.get(node.getAttribute('is') || node.nodeName.toLowerCase())
|
|
255
|
-
? get_setters(node).
|
|
241
|
+
? get_setters(node).has(prop)
|
|
256
242
|
: value && typeof value === 'object')
|
|
257
243
|
) {
|
|
258
244
|
// @ts-expect-error
|
|
@@ -291,11 +277,8 @@ function set_attributes(
|
|
|
291
277
|
skip_warning = false
|
|
292
278
|
) {
|
|
293
279
|
if (hydrating && should_remove_defaults && element.nodeName === INPUT_TAG) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!(attribute in next)) {
|
|
298
|
-
remove_input_defaults(input);
|
|
280
|
+
if (!('defaultValue' in next || 'defaultChecked' in next)) {
|
|
281
|
+
remove_input_defaults(/** @type {HTMLInputElement} */ (element));
|
|
299
282
|
}
|
|
300
283
|
}
|
|
301
284
|
|
|
@@ -313,9 +296,11 @@ function set_attributes(
|
|
|
313
296
|
|
|
314
297
|
var current = prev || {};
|
|
315
298
|
var is_option_element = element.nodeName === OPTION_TAG;
|
|
299
|
+
var is_select_element = element.nodeName === SELECT_TAG;
|
|
316
300
|
|
|
317
301
|
for (var key in prev) {
|
|
318
|
-
|
|
302
|
+
// don't null our internal $$onX listeners
|
|
303
|
+
if (!(key in next) && key[0] + key[1] !== '$$') {
|
|
319
304
|
next[key] = null;
|
|
320
305
|
}
|
|
321
306
|
}
|
|
@@ -448,6 +433,9 @@ function set_attributes(
|
|
|
448
433
|
|
|
449
434
|
var is_default = name === 'defaultValue' || name === 'defaultChecked';
|
|
450
435
|
|
|
436
|
+
// A select's default value is represented by selected options, not a property.
|
|
437
|
+
if (is_select_element && name === 'defaultValue') continue;
|
|
438
|
+
|
|
451
439
|
if (value == null && !is_custom_element && !is_default) {
|
|
452
440
|
attributes[key] = null;
|
|
453
441
|
|
|
@@ -472,7 +460,7 @@ function set_attributes(
|
|
|
472
460
|
}
|
|
473
461
|
} else if (
|
|
474
462
|
is_default ||
|
|
475
|
-
(
|
|
463
|
+
((is_custom_element || typeof value !== 'string') && setters.has(name))
|
|
476
464
|
) {
|
|
477
465
|
// @ts-ignore
|
|
478
466
|
element[name] = value;
|
|
@@ -533,8 +521,16 @@ export function attribute_effect(
|
|
|
533
521
|
skip_warning
|
|
534
522
|
);
|
|
535
523
|
|
|
536
|
-
if (inited && is_select
|
|
537
|
-
|
|
524
|
+
if (inited && is_select) {
|
|
525
|
+
var select = /** @type {HTMLSelectElement} */ (element);
|
|
526
|
+
|
|
527
|
+
if ('defaultValue' in next) {
|
|
528
|
+
set_default_select_value(select, next.defaultValue);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if ('value' in next) {
|
|
532
|
+
select_option(select, next.value);
|
|
533
|
+
}
|
|
538
534
|
}
|
|
539
535
|
|
|
540
536
|
for (let symbol of Object.getOwnPropertySymbols(effects)) {
|
|
@@ -559,7 +555,13 @@ export function attribute_effect(
|
|
|
559
555
|
var select = /** @type {HTMLSelectElement} */ (element);
|
|
560
556
|
|
|
561
557
|
effect(() => {
|
|
562
|
-
|
|
558
|
+
var attrs = /** @type {Record<string | symbol, any>} */ (prev);
|
|
559
|
+
|
|
560
|
+
if ('defaultValue' in attrs) {
|
|
561
|
+
set_default_select_value(select, attrs.defaultValue);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
select_option(select, attrs.value, true);
|
|
563
565
|
init_select(select);
|
|
564
566
|
});
|
|
565
567
|
}
|
|
@@ -581,7 +583,7 @@ function get_attributes(element) {
|
|
|
581
583
|
);
|
|
582
584
|
}
|
|
583
585
|
|
|
584
|
-
/** @type {Map<string, string
|
|
586
|
+
/** @type {Map<string, Set<string>>} */
|
|
585
587
|
var setters_cache = new Map();
|
|
586
588
|
|
|
587
589
|
/** @param {Element} element */
|
|
@@ -589,7 +591,7 @@ function get_setters(element) {
|
|
|
589
591
|
var cache_key = element.getAttribute('is') || element.nodeName;
|
|
590
592
|
var setters = setters_cache.get(cache_key);
|
|
591
593
|
if (setters) return setters;
|
|
592
|
-
setters_cache.set(cache_key, (setters =
|
|
594
|
+
setters_cache.set(cache_key, (setters = new Set()));
|
|
593
595
|
|
|
594
596
|
var descriptors;
|
|
595
597
|
var proto = element; // In the case of custom elements there might be setters on the instance
|
|
@@ -608,7 +610,7 @@ function get_setters(element) {
|
|
|
608
610
|
key !== 'textContent' &&
|
|
609
611
|
key !== 'innerText'
|
|
610
612
|
) {
|
|
611
|
-
setters.
|
|
613
|
+
setters.add(key);
|
|
612
614
|
}
|
|
613
615
|
}
|
|
614
616
|
|
|
@@ -6,6 +6,71 @@ import * as w from '../../../warnings.js';
|
|
|
6
6
|
import { Batch, current_batch, previous_batch } from '../../../reactivity/batch.js';
|
|
7
7
|
import { async_mode_flag } from '../../../../flags/index.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Sets the `selected` attribute on an option so form reset can restore it.
|
|
11
|
+
* @param {HTMLOptionElement} option
|
|
12
|
+
* @param {boolean} selected
|
|
13
|
+
*/
|
|
14
|
+
export function set_selected(option, selected) {
|
|
15
|
+
if (selected) {
|
|
16
|
+
if (!option.hasAttribute('selected')) option.setAttribute('selected', '');
|
|
17
|
+
} else {
|
|
18
|
+
option.removeAttribute('selected');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Sets the options a form reset should restore. The first call selects
|
|
24
|
+
* them if nothing has set a value, later calls leave the current selection alone.
|
|
25
|
+
* @param {HTMLSelectElement} select
|
|
26
|
+
* @param {any} value
|
|
27
|
+
*/
|
|
28
|
+
export function set_default_select_value(select, value) {
|
|
29
|
+
var mounting = !('__defaultValue' in select);
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
if (!mounting && select.__defaultValue === value) return;
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
select.__defaultValue = value;
|
|
34
|
+
apply_default_select_value(select, !mounting || '__value' in select);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Marks the options matching `__defaultValue` as selected. Without `preserve`
|
|
39
|
+
* a newly matching option gets selected, as an inserted `<option selected>` would.
|
|
40
|
+
* @param {HTMLSelectElement} select
|
|
41
|
+
* @param {boolean} preserve
|
|
42
|
+
*/
|
|
43
|
+
function apply_default_select_value(select, preserve) {
|
|
44
|
+
// @ts-expect-error
|
|
45
|
+
var value = select.__defaultValue;
|
|
46
|
+
var multiple = select.multiple;
|
|
47
|
+
var values = multiple ? value ?? [] : null;
|
|
48
|
+
|
|
49
|
+
if (multiple && !is_array(values)) return;
|
|
50
|
+
|
|
51
|
+
var index = select.selectedIndex;
|
|
52
|
+
var selected = preserve && multiple ? new Set(select.selectedOptions) : null;
|
|
53
|
+
|
|
54
|
+
for (var option of select.options) {
|
|
55
|
+
var option_value = get_option_value(option);
|
|
56
|
+
set_selected(
|
|
57
|
+
option,
|
|
58
|
+
multiple ? /** @type {any[]} */ (values).includes(option_value) : is(option_value, value)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!preserve) return;
|
|
63
|
+
|
|
64
|
+
if (selected !== null) {
|
|
65
|
+
for (option of select.options) {
|
|
66
|
+
var was_selected = selected.has(option);
|
|
67
|
+
if (option.selected !== was_selected) option.selected = was_selected;
|
|
68
|
+
}
|
|
69
|
+
} else if (select.selectedIndex !== index) {
|
|
70
|
+
select.selectedIndex = index;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
9
74
|
/**
|
|
10
75
|
* Selects the correct option(s) (depending on whether this is a multiple select)
|
|
11
76
|
* @template V
|
|
@@ -47,17 +112,25 @@ export function select_option(select, value, mounting = false) {
|
|
|
47
112
|
}
|
|
48
113
|
|
|
49
114
|
/**
|
|
50
|
-
*
|
|
51
|
-
* and
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* inside an `#each` block.
|
|
115
|
+
* Sets up a mutation observer to sync the current selection
|
|
116
|
+
* and default to the dom when the options change, for example
|
|
117
|
+
* when they are inside an `#each` block. Called once per `<select>`,
|
|
118
|
+
* by the compiled output or by `attribute_effect` for spreads.
|
|
55
119
|
* @param {HTMLSelectElement} select
|
|
56
120
|
*/
|
|
57
121
|
export function init_select(select) {
|
|
58
|
-
var observer = new MutationObserver(() => {
|
|
122
|
+
var observer = new MutationObserver((entries) => {
|
|
123
|
+
// Mutations related to `<selectedcontent>` can never affect the option list.
|
|
124
|
+
// Reacting to them could revert a user-initiated selection change, because the
|
|
125
|
+
// records are delivered as soon as any listener returns (e.g. a delegated `input`
|
|
126
|
+
// handler), which can happen before the `change` handler has updated `__value`
|
|
127
|
+
if (entries.every(is_selectedcontent_mutation)) return;
|
|
128
|
+
|
|
129
|
+
if ('__defaultValue' in select) {
|
|
130
|
+
apply_default_select_value(select, false);
|
|
131
|
+
}
|
|
132
|
+
|
|
59
133
|
if ('__value' in select) {
|
|
60
|
-
// @ts-ignore
|
|
61
134
|
select_option(select, select.__value);
|
|
62
135
|
}
|
|
63
136
|
// Deliberately don't update the potential binding value,
|
|
@@ -151,8 +224,6 @@ export function bind_select_value(select, get, set = get) {
|
|
|
151
224
|
select.__value = value;
|
|
152
225
|
mounting = false;
|
|
153
226
|
});
|
|
154
|
-
|
|
155
|
-
init_select(select);
|
|
156
227
|
}
|
|
157
228
|
|
|
158
229
|
/** @param {HTMLOptionElement} option */
|
|
@@ -164,3 +235,22 @@ function get_option_value(option) {
|
|
|
164
235
|
return option.value;
|
|
165
236
|
}
|
|
166
237
|
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Returns `true` if the mutation stems from the browser mirroring the selected
|
|
241
|
+
* option's content into `<selectedcontent>`, or from us replacing the
|
|
242
|
+
* `<selectedcontent>` element with a clone of itself
|
|
243
|
+
* @param {MutationRecord} entry
|
|
244
|
+
*/
|
|
245
|
+
function is_selectedcontent_mutation(entry) {
|
|
246
|
+
if (/** @type {Element} */ (entry.target).closest('selectedcontent') !== null) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (entry.type === 'childList') {
|
|
251
|
+
var nodes = [...entry.addedNodes, ...entry.removedNodes];
|
|
252
|
+
return nodes.length > 0 && nodes.every((node) => node.nodeName === 'SELECTEDCONTENT');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { ComponentContext, Effect } from '#client' */
|
|
2
2
|
import { DESTROYING, STATE_SYMBOL } from '#client/constants';
|
|
3
|
-
import { component_context } from '../../../context.js';
|
|
3
|
+
import { component_context, mark_as_component } from '../../../context.js';
|
|
4
4
|
import { effect, render_effect } from '../../../reactivity/effects.js';
|
|
5
5
|
import { active_effect, untrack } from '../../../runtime.js';
|
|
6
6
|
|
|
@@ -23,7 +23,12 @@ function is_bound_this(bound_value, element_or_component) {
|
|
|
23
23
|
* returns all the parts of the each block context that are used in the expression
|
|
24
24
|
* @returns {void}
|
|
25
25
|
*/
|
|
26
|
-
export function bind_this(
|
|
26
|
+
export function bind_this(
|
|
27
|
+
element_or_component = mark_as_component(),
|
|
28
|
+
update,
|
|
29
|
+
get_value,
|
|
30
|
+
get_parts
|
|
31
|
+
) {
|
|
27
32
|
var component_effect = /** @type {ComponentContext} */ (component_context).r;
|
|
28
33
|
var parent = /** @type {Effect} */ (active_effect);
|
|
29
34
|
|
|
@@ -159,12 +159,15 @@ export function delegate(events) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// used to store the reference to the currently propagated event
|
|
162
|
-
// to prevent garbage collection between microtasks in Firefox
|
|
162
|
+
// to prevent garbage collection between microtasks in Firefox (<= 141)
|
|
163
163
|
// If the event object is GCed too early, the expando __root property
|
|
164
164
|
// set on the event object is lost, causing the event delegation
|
|
165
165
|
// to process the event twice
|
|
166
166
|
let last_propagated_event = null;
|
|
167
167
|
|
|
168
|
+
// whether a task is already queued to clear `last_propagated_event`
|
|
169
|
+
let last_propagated_event_clear_scheduled = false;
|
|
170
|
+
|
|
168
171
|
/**
|
|
169
172
|
* @this {EventTarget}
|
|
170
173
|
* @param {Event} event
|
|
@@ -179,6 +182,21 @@ export function handle_event_propagation(event) {
|
|
|
179
182
|
|
|
180
183
|
last_propagated_event = event;
|
|
181
184
|
|
|
185
|
+
// The reference is only needed while the event can still reach another
|
|
186
|
+
// delegated root, i.e. during the current (synchronous) dispatch and its
|
|
187
|
+
// microtask checkpoints. Clearing it in a later task preserves the
|
|
188
|
+
// Firefox workaround while making sure the slot doesn't retain the last
|
|
189
|
+
// event forever — through `event.target` it would otherwise keep the
|
|
190
|
+
// entire detached subtree of whatever the user last clicked in alive
|
|
191
|
+
// until the next delegated event happens to arrive.
|
|
192
|
+
if (!last_propagated_event_clear_scheduled) {
|
|
193
|
+
last_propagated_event_clear_scheduled = true;
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
last_propagated_event_clear_scheduled = false;
|
|
196
|
+
last_propagated_event = null;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
182
200
|
// composedPath contains list of nodes the event has propagated through.
|
|
183
201
|
// We check `event_symbol` to skip all nodes below it in case this is a
|
|
184
202
|
// parent of the `event_symbol` node, which indicates that there's nested
|