ripple 0.2.167 → 0.2.169
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/package.json +2 -2
- package/src/compiler/phases/2-analyze/index.js +4 -5
- package/src/compiler/phases/3-transform/client/index.js +20 -36
- package/src/compiler/utils.js +13 -6
- package/src/jsx-runtime.d.ts +5 -3
- package/src/runtime/internal/client/blocks.js +7 -5
- package/src/runtime/internal/client/events.js +134 -25
- package/src/runtime/internal/client/index.js +3 -2
- package/src/runtime/internal/client/render.js +36 -26
- package/src/runtime/internal/client/runtime.js +9 -5
- package/src/runtime/media-query.js +14 -8
- package/src/utils/events.js +120 -43
- package/tests/client/basic/basic.attributes.test.ripple +442 -0
- package/tests/client/basic/basic.events.test.ripple +2 -2
- package/tests/client/composite/composite.reactivity.test.ripple +6 -5
- package/tests/client/input-value.test.ripple +4 -4
- package/tests/client/media-query.test.ripple +1 -1
- package/tests/utils/events.test.js +57 -46
- package/types/index.d.ts +15 -5
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.169",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -81,6 +81,6 @@
|
|
|
81
81
|
"typescript": "^5.9.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"ripple": "0.2.
|
|
84
|
+
"ripple": "0.2.169"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -2,7 +2,7 @@ import * as b from '../../../utils/builders.js';
|
|
|
2
2
|
import { walk } from 'zimmerframe';
|
|
3
3
|
import { create_scopes, ScopeRoot } from '../../scope.js';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
is_delegated_event,
|
|
6
6
|
get_parent_block_node,
|
|
7
7
|
is_element_dom_element,
|
|
8
8
|
is_inside_component,
|
|
@@ -729,16 +729,15 @@ const visitors = {
|
|
|
729
729
|
}
|
|
730
730
|
|
|
731
731
|
if (is_event_attribute(attr.name.name)) {
|
|
732
|
-
const event_name = attr.name.name.slice(2).toLowerCase();
|
|
733
732
|
const handler = visit(attr.value, state);
|
|
734
|
-
const
|
|
733
|
+
const is_delegated = is_delegated_event(attr.name.name, handler, context);
|
|
735
734
|
|
|
736
|
-
if (
|
|
735
|
+
if (is_delegated) {
|
|
737
736
|
if (attr.metadata === undefined) {
|
|
738
737
|
attr.metadata = {};
|
|
739
738
|
}
|
|
740
739
|
|
|
741
|
-
attr.metadata.delegated =
|
|
740
|
+
attr.metadata.delegated = is_delegated;
|
|
742
741
|
}
|
|
743
742
|
} else if (attr.value !== null) {
|
|
744
743
|
visit(attr.value, state);
|
|
@@ -40,7 +40,11 @@ import {
|
|
|
40
40
|
import is_reference from 'is-reference';
|
|
41
41
|
import { object } from '../../../../utils/ast.js';
|
|
42
42
|
import { render_stylesheets } from '../stylesheet.js';
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
get_original_event_name,
|
|
45
|
+
is_event_attribute,
|
|
46
|
+
normalize_event_name,
|
|
47
|
+
} from '../../../../utils/events.js';
|
|
44
48
|
import { createHash } from 'node:crypto';
|
|
45
49
|
|
|
46
50
|
function add_ripple_internal_import(context) {
|
|
@@ -976,55 +980,35 @@ const visitors = {
|
|
|
976
980
|
}
|
|
977
981
|
|
|
978
982
|
if (is_event_attribute(name)) {
|
|
979
|
-
|
|
980
|
-
let
|
|
981
|
-
|
|
982
|
-
: name.slice(2).toLowerCase();
|
|
983
|
-
let handler = visit(attr.value, state);
|
|
983
|
+
const metadata = { tracking: false, await: false };
|
|
984
|
+
let handler = visit(attr.value, { ...state, metadata });
|
|
985
|
+
const id = state.flush_node();
|
|
984
986
|
|
|
985
987
|
if (attr.metadata?.delegated) {
|
|
986
|
-
|
|
988
|
+
const event_name = normalize_event_name(name);
|
|
987
989
|
|
|
988
990
|
if (!state.events.has(event_name)) {
|
|
989
991
|
state.events.add(event_name);
|
|
990
992
|
}
|
|
991
993
|
|
|
992
|
-
if (
|
|
993
|
-
(handler.type === 'Identifier' &&
|
|
994
|
-
is_declared_function_within_component(handler, context)) ||
|
|
995
|
-
handler.type === 'ArrowFunctionExpression' ||
|
|
996
|
-
handler.type === 'FunctionExpression'
|
|
997
|
-
) {
|
|
998
|
-
delegated_assignment = handler;
|
|
999
|
-
} else {
|
|
1000
|
-
delegated_assignment = b.array([handler, b.id('__block')]);
|
|
1001
|
-
}
|
|
1002
|
-
const id = state.flush_node();
|
|
1003
|
-
|
|
1004
994
|
state.init.push(
|
|
1005
|
-
b.stmt(b.assignment('=', b.member(id, '__' + event_name),
|
|
995
|
+
b.stmt(b.assignment('=', b.member(id, '__' + event_name), handler)),
|
|
1006
996
|
);
|
|
1007
997
|
} else {
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
b.call(
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
capture && b.true,
|
|
1019
|
-
passive === undefined ? undefined : b.literal(passive),
|
|
1020
|
-
),
|
|
1021
|
-
),
|
|
1022
|
-
);
|
|
998
|
+
const event_name = get_original_event_name(name);
|
|
999
|
+
// Check if handler is reactive (contains tracking)
|
|
1000
|
+
if (metadata.tracking) {
|
|
1001
|
+
// Use reactive_event with a thunk to re-evaluate when dependencies change
|
|
1002
|
+
state.init.push(
|
|
1003
|
+
b.stmt(b.call('_$_.render_event', b.literal(event_name), id, b.thunk(handler))),
|
|
1004
|
+
);
|
|
1005
|
+
} else {
|
|
1006
|
+
state.init.push(b.stmt(b.call('_$_.event', b.literal(event_name), id, handler)));
|
|
1007
|
+
}
|
|
1023
1008
|
}
|
|
1024
1009
|
|
|
1025
1010
|
continue;
|
|
1026
1011
|
}
|
|
1027
|
-
|
|
1028
1012
|
const metadata = { tracking: false, await: false };
|
|
1029
1013
|
const expression = visit(attr.value, { ...state, metadata });
|
|
1030
1014
|
// All other attributes
|
package/src/compiler/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @import { Identifier, Pattern, Super,
|
|
2
|
-
/** @import { Component, Element,
|
|
1
|
+
/** @import { Identifier, Pattern, Super, MemberExpression, AssignmentExpression, Expression, Node, AssignmentOperator } from 'estree' */
|
|
2
|
+
/** @import { Component, Element, RippleNode, TransformContext } from '#compiler' */
|
|
3
3
|
import { build_assignment_value, extract_paths } from '../utils/ast.js';
|
|
4
4
|
import * as b from '../utils/builders.js';
|
|
5
|
-
import {
|
|
5
|
+
import { is_capture_event, is_non_delegated, normalize_event_name } from '../utils/events.js';
|
|
6
6
|
|
|
7
7
|
const regex_return_characters = /\r/g;
|
|
8
8
|
|
|
@@ -173,12 +173,19 @@ export function is_dom_property(name) {
|
|
|
173
173
|
* Determines if an event handler can be delegated
|
|
174
174
|
* @param {string} event_name
|
|
175
175
|
* @param {Expression} handler
|
|
176
|
-
* @param {
|
|
176
|
+
* @param {TransformContext} context
|
|
177
177
|
* @returns {boolean}
|
|
178
178
|
*/
|
|
179
|
-
export function
|
|
179
|
+
export function is_delegated_event(event_name, handler, context) {
|
|
180
180
|
// Handle delegated event handlers. Bail out if not a delegated event.
|
|
181
|
-
if (
|
|
181
|
+
if (
|
|
182
|
+
!handler ||
|
|
183
|
+
is_capture_event(event_name) ||
|
|
184
|
+
is_non_delegated(normalize_event_name(event_name)) ||
|
|
185
|
+
(handler.type !== 'FunctionExpression' &&
|
|
186
|
+
handler.type !== 'ArrowFunctionExpression' &&
|
|
187
|
+
!is_declared_function_within_component(/** @type {Identifier}*/ (handler), context))
|
|
188
|
+
) {
|
|
182
189
|
return false;
|
|
183
190
|
}
|
|
184
191
|
return true;
|
package/src/jsx-runtime.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AddEventObject } from '#public';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Ripple JSX Runtime Type Definitions
|
|
3
5
|
* Ripple components are imperative and don't return JSX elements
|
|
@@ -40,9 +42,9 @@ interface HTMLAttributes {
|
|
|
40
42
|
className?: string;
|
|
41
43
|
id?: string;
|
|
42
44
|
style?: string | Record<string, string | number>;
|
|
43
|
-
onClick?:
|
|
44
|
-
onInput?:
|
|
45
|
-
onChange?:
|
|
45
|
+
onClick?: EventListener | AddEventObject;
|
|
46
|
+
onInput?: EventListener | AddEventObject;
|
|
47
|
+
onChange?: EventListener | AddEventObject;
|
|
46
48
|
children?: any;
|
|
47
49
|
[key: string]: any;
|
|
48
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { Block, Derived, CompatOptions } from '#client' */
|
|
1
|
+
/** @import { Block, Derived, CompatOptions, Component } from '#client' */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
BLOCK_HAS_RUN,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
active_block,
|
|
20
20
|
active_component,
|
|
21
21
|
active_reaction,
|
|
22
|
+
create_component_ctx,
|
|
22
23
|
is_block_dirty,
|
|
23
24
|
run_block,
|
|
24
25
|
run_teardown,
|
|
@@ -151,7 +152,7 @@ export function root(fn, compat) {
|
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
return block(ROOT_BLOCK,
|
|
155
|
+
return block(ROOT_BLOCK, target_fn, { compat }, create_component_ctx());
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
/**
|
|
@@ -181,13 +182,14 @@ function push_block(block, parent_block) {
|
|
|
181
182
|
/**
|
|
182
183
|
* @param {number} flags
|
|
183
184
|
* @param {Function} fn
|
|
184
|
-
* @param {any} state
|
|
185
|
+
* @param {any} [state]
|
|
186
|
+
* @param {Component} [co]
|
|
185
187
|
* @returns {Block}
|
|
186
188
|
*/
|
|
187
|
-
export function block(flags, fn, state = null) {
|
|
189
|
+
export function block(flags, fn, state = null, co) {
|
|
188
190
|
/** @type {Block} */
|
|
189
191
|
var block = {
|
|
190
|
-
co: active_component,
|
|
192
|
+
co: co || active_component,
|
|
191
193
|
d: null,
|
|
192
194
|
first: null,
|
|
193
195
|
f: flags,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
/** @import { AddEventObject, AddEventOptions, ExtendedEventOptions } from '#public'*/
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {EventTarget & Record<string, any>} DelegatedEventTarget
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
event_name_from_capture,
|
|
7
|
+
is_capture_event,
|
|
8
|
+
is_non_delegated,
|
|
9
|
+
is_passive_event,
|
|
10
|
+
} from '../../../utils/events.js';
|
|
2
11
|
import {
|
|
3
12
|
active_block,
|
|
4
13
|
active_reaction,
|
|
@@ -8,6 +17,7 @@ import {
|
|
|
8
17
|
tracking,
|
|
9
18
|
} from './runtime.js';
|
|
10
19
|
import { array_from, define_property, is_array } from './utils.js';
|
|
20
|
+
import { render } from './blocks.js';
|
|
11
21
|
|
|
12
22
|
/** @type {Set<string>} */
|
|
13
23
|
var all_registered_events = new Set();
|
|
@@ -15,21 +25,44 @@ var all_registered_events = new Set();
|
|
|
15
25
|
/** @type {Set<(events: Array<string>) => void>} */
|
|
16
26
|
var root_event_handles = new Set();
|
|
17
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {AddEventOptions} options
|
|
30
|
+
* @returns {AddEventListenerOptions}
|
|
31
|
+
*/
|
|
32
|
+
function get_event_options(options) {
|
|
33
|
+
/** @type AddEventListenerOptions */
|
|
34
|
+
var event_options = {};
|
|
35
|
+
|
|
36
|
+
if (options.capture) {
|
|
37
|
+
event_options.capture = true;
|
|
38
|
+
}
|
|
39
|
+
if (options.once) {
|
|
40
|
+
event_options.once = true;
|
|
41
|
+
}
|
|
42
|
+
if (options.passive) {
|
|
43
|
+
event_options.passive = true;
|
|
44
|
+
}
|
|
45
|
+
if (options.signal) {
|
|
46
|
+
event_options.signal = options.signal;
|
|
47
|
+
}
|
|
48
|
+
return event_options;
|
|
49
|
+
}
|
|
50
|
+
|
|
18
51
|
/**
|
|
19
52
|
* @param {EventTarget} element
|
|
20
53
|
* @param {string} type
|
|
21
54
|
* @param {EventListener} handler
|
|
22
|
-
* @param {
|
|
55
|
+
* @param {ExtendedEventOptions} [options]
|
|
23
56
|
*/
|
|
24
57
|
export function on(element, type, handler, options = {}) {
|
|
25
|
-
var
|
|
58
|
+
var remove_listener = create_event(type, element, handler, options);
|
|
26
59
|
|
|
27
60
|
return () => {
|
|
28
|
-
|
|
61
|
+
remove_listener();
|
|
29
62
|
};
|
|
30
63
|
}
|
|
31
64
|
|
|
32
|
-
|
|
65
|
+
var last_propagated_event = null;
|
|
33
66
|
|
|
34
67
|
/**
|
|
35
68
|
* @this {EventTarget}
|
|
@@ -130,12 +163,7 @@ export function handle_event_propagation(event) {
|
|
|
130
163
|
var delegated = current_target['__' + event_name];
|
|
131
164
|
|
|
132
165
|
if (delegated !== undefined && !(/** @type {any} */ (current_target).disabled)) {
|
|
133
|
-
|
|
134
|
-
var [fn, block, ...data] = delegated;
|
|
135
|
-
fn.apply(current_target, [event, ...data, block]);
|
|
136
|
-
} else {
|
|
137
|
-
delegated.call(current_target, event);
|
|
138
|
-
}
|
|
166
|
+
delegated.call(current_target, event);
|
|
139
167
|
}
|
|
140
168
|
} catch (error) {
|
|
141
169
|
if (throw_error) {
|
|
@@ -174,12 +202,51 @@ export function handle_event_propagation(event) {
|
|
|
174
202
|
/**
|
|
175
203
|
* @param {string} event_name
|
|
176
204
|
* @param {EventTarget} dom
|
|
177
|
-
* @param {EventListener}
|
|
178
|
-
* @param {
|
|
205
|
+
* @param {EventListener} handler
|
|
206
|
+
* @param {AddEventOptions} options
|
|
207
|
+
* @returns {() => void}
|
|
179
208
|
*/
|
|
180
|
-
function create_event(event_name, dom, handler, options
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
function create_event(event_name, dom, handler, options) {
|
|
210
|
+
var is_delegated = true;
|
|
211
|
+
|
|
212
|
+
if (is_capture_event(event_name)) {
|
|
213
|
+
event_name = event_name_from_capture(event_name);
|
|
214
|
+
|
|
215
|
+
if (!('capture' in options) || options.capture !== false) {
|
|
216
|
+
options.capture = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
event_name =
|
|
221
|
+
options.customName && options.customName?.length
|
|
222
|
+
? options.customName
|
|
223
|
+
: event_name.toLowerCase();
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
options.delegated === false ||
|
|
227
|
+
options.capture ||
|
|
228
|
+
options.passive ||
|
|
229
|
+
options.once ||
|
|
230
|
+
options.signal ||
|
|
231
|
+
is_non_delegated(event_name)
|
|
232
|
+
) {
|
|
233
|
+
is_delegated = false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (is_delegated) {
|
|
237
|
+
var prop = '__' + event_name;
|
|
238
|
+
/** @type {DelegatedEventTarget} */ (dom)[prop] = handler;
|
|
239
|
+
delegate([event_name]);
|
|
240
|
+
return () => {
|
|
241
|
+
/** @type {DelegatedEventTarget} */ (dom)[prop] = undefined;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @type {EventListener}
|
|
247
|
+
* @this {Element}
|
|
248
|
+
*/
|
|
249
|
+
function target_handler(event) {
|
|
183
250
|
var previous_block = active_block;
|
|
184
251
|
var previous_reaction = active_reaction;
|
|
185
252
|
var previous_tracking = tracking;
|
|
@@ -203,22 +270,64 @@ function create_event(event_name, dom, handler, options = {}) {
|
|
|
203
270
|
}
|
|
204
271
|
}
|
|
205
272
|
|
|
206
|
-
|
|
273
|
+
var event_options = get_event_options(options);
|
|
207
274
|
|
|
208
|
-
|
|
275
|
+
dom.addEventListener(event_name, target_handler, event_options);
|
|
276
|
+
return () => {
|
|
277
|
+
dom.removeEventListener(event_name, target_handler, event_options);
|
|
278
|
+
};
|
|
209
279
|
}
|
|
210
280
|
|
|
211
281
|
/**
|
|
212
282
|
* @param {string} event_name
|
|
213
283
|
* @param {Element} dom
|
|
214
|
-
* @param {EventListener}
|
|
215
|
-
* @
|
|
216
|
-
|
|
217
|
-
|
|
284
|
+
* @param {EventListener | AddEventObject} handler
|
|
285
|
+
* @returns {() => void}
|
|
286
|
+
*/
|
|
287
|
+
export function event(event_name, dom, handler) {
|
|
288
|
+
/** @type AddEventOptions */
|
|
289
|
+
var options = {};
|
|
290
|
+
/** @type {EventListener} */
|
|
291
|
+
var event_handler;
|
|
292
|
+
|
|
293
|
+
if (typeof handler === 'object' && 'handleEvent' in handler) {
|
|
294
|
+
({ handleEvent: event_handler, ...options } = handler);
|
|
295
|
+
} else {
|
|
296
|
+
event_handler = handler;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return create_event(event_name, dom, event_handler, options);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Reactive version of event that automatically cleans up and re-attaches
|
|
304
|
+
* when the handler changes
|
|
305
|
+
* @param {string} event_name
|
|
306
|
+
* @param {Element} dom
|
|
307
|
+
* @param {() => EventListener | AddEventObject} get_handler
|
|
218
308
|
*/
|
|
219
|
-
export function
|
|
220
|
-
|
|
221
|
-
|
|
309
|
+
export function render_event(event_name, dom, get_handler) {
|
|
310
|
+
/** @type {EventListener | AddEventObject | undefined} */
|
|
311
|
+
var prev;
|
|
312
|
+
/** @type {(() => void) | undefined} */
|
|
313
|
+
var remove_listener;
|
|
314
|
+
|
|
315
|
+
render(() => {
|
|
316
|
+
var handler = get_handler();
|
|
317
|
+
|
|
318
|
+
if (handler !== prev) {
|
|
319
|
+
if (remove_listener) {
|
|
320
|
+
remove_listener();
|
|
321
|
+
remove_listener = undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
prev = handler;
|
|
325
|
+
|
|
326
|
+
if (handler) {
|
|
327
|
+
remove_listener = event(event_name, dom, handler);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
222
331
|
}
|
|
223
332
|
|
|
224
333
|
/**
|
|
@@ -4,6 +4,7 @@ export {
|
|
|
4
4
|
next_sibling as sibling,
|
|
5
5
|
document,
|
|
6
6
|
create_text,
|
|
7
|
+
init_operations,
|
|
7
8
|
} from './operations.js';
|
|
8
9
|
|
|
9
10
|
export {
|
|
@@ -16,9 +17,9 @@ export {
|
|
|
16
17
|
set_selected,
|
|
17
18
|
} from './render.js';
|
|
18
19
|
|
|
19
|
-
export { render, render_spread, async, ref, branch, destroy_block } from './blocks.js';
|
|
20
|
+
export { render, render_spread, async, ref, branch, destroy_block, root } from './blocks.js';
|
|
20
21
|
|
|
21
|
-
export { event, delegate } from './events.js';
|
|
22
|
+
export { event, render_event, delegate } from './events.js';
|
|
22
23
|
|
|
23
24
|
export {
|
|
24
25
|
active_block,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { destroy_block, ref } from './blocks.js';
|
|
3
|
+
import { branch, destroy_block, ref } from './blocks.js';
|
|
4
4
|
import { REF_PROP } from './constants.js';
|
|
5
5
|
import {
|
|
6
6
|
get_descriptors,
|
|
@@ -8,12 +8,8 @@ import {
|
|
|
8
8
|
get_prototype_of,
|
|
9
9
|
is_tracked_object,
|
|
10
10
|
} from './utils.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
get_attribute_event_name,
|
|
14
|
-
is_delegated,
|
|
15
|
-
is_event_attribute,
|
|
16
|
-
} from '../../../utils/events.js';
|
|
11
|
+
import { event } from './events.js';
|
|
12
|
+
import { get_attribute_event_name, is_event_attribute } from '../../../utils/events.js';
|
|
17
13
|
import { get } from './runtime.js';
|
|
18
14
|
import { clsx } from 'clsx';
|
|
19
15
|
import { normalize_css_property_name } from '../../../utils/normalize_css_property_name.js';
|
|
@@ -131,8 +127,9 @@ export function apply_styles(element, new_styles) {
|
|
|
131
127
|
* @param {Element} element
|
|
132
128
|
* @param {string} key
|
|
133
129
|
* @param {any} value
|
|
130
|
+
* @param {Record<string, (() => void) | undefined>} remove_listeners
|
|
134
131
|
*/
|
|
135
|
-
function set_attribute_helper(element, key, value) {
|
|
132
|
+
function set_attribute_helper(element, key, value, remove_listeners) {
|
|
136
133
|
if (key === 'class') {
|
|
137
134
|
const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
|
|
138
135
|
set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
|
|
@@ -143,16 +140,11 @@ function set_attribute_helper(element, key, value) {
|
|
|
143
140
|
element.classList.add(value);
|
|
144
141
|
} else if (typeof key === 'string' && is_event_attribute(key)) {
|
|
145
142
|
// Handle event handlers in spread props
|
|
146
|
-
const event_name = get_attribute_event_name(key);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Use delegation for delegated events
|
|
150
|
-
/** @type {any} */ (element)['__' + event_name] = value;
|
|
151
|
-
delegate([event_name]);
|
|
152
|
-
} else {
|
|
153
|
-
// Use addEventListener for non-delegated events
|
|
154
|
-
event(event_name, element, value);
|
|
143
|
+
const event_name = get_attribute_event_name(key, value);
|
|
144
|
+
if (remove_listeners[key]) {
|
|
145
|
+
remove_listeners[key]();
|
|
155
146
|
}
|
|
147
|
+
remove_listeners[key] = event(event_name, element, value);
|
|
156
148
|
} else {
|
|
157
149
|
set_attribute(element, key, value);
|
|
158
150
|
}
|
|
@@ -257,22 +249,25 @@ export function set_selected(element, selected) {
|
|
|
257
249
|
export function apply_element_spread(element, fn) {
|
|
258
250
|
/** @type {Record<string | symbol, any>} */
|
|
259
251
|
var prev = {};
|
|
260
|
-
/** @type {Record<symbol, Block>} */
|
|
252
|
+
/** @type {Record<symbol, Block | undefined>} */
|
|
261
253
|
var effects = {};
|
|
254
|
+
/** @type {Record<string | symbol, (() => void) | undefined>} */
|
|
255
|
+
var remove_listeners = {};
|
|
262
256
|
|
|
263
257
|
return () => {
|
|
264
258
|
var next = fn();
|
|
265
259
|
|
|
266
|
-
for (
|
|
267
|
-
if (!next[symbol]) {
|
|
260
|
+
for (const symbol of get_own_property_symbols(effects)) {
|
|
261
|
+
if (!next[symbol] && effects[symbol]) {
|
|
268
262
|
destroy_block(effects[symbol]);
|
|
263
|
+
effects[symbol] = undefined;
|
|
269
264
|
}
|
|
270
265
|
}
|
|
271
266
|
|
|
272
267
|
for (const symbol of get_own_property_symbols(next)) {
|
|
273
268
|
var ref_fn = next[symbol];
|
|
274
269
|
|
|
275
|
-
if (symbol.description === REF_PROP && (!prev || ref_fn !== prev[symbol])) {
|
|
270
|
+
if (symbol.description === REF_PROP && (!(symbol in prev) || ref_fn !== prev[symbol])) {
|
|
276
271
|
if (effects[symbol]) {
|
|
277
272
|
destroy_block(effects[symbol]);
|
|
278
273
|
}
|
|
@@ -282,7 +277,24 @@ export function apply_element_spread(element, fn) {
|
|
|
282
277
|
next[symbol] = ref_fn;
|
|
283
278
|
}
|
|
284
279
|
|
|
285
|
-
|
|
280
|
+
for (let key in remove_listeners) {
|
|
281
|
+
// Remove event listeners that are no longer present
|
|
282
|
+
if (!(key in next) && remove_listeners[key]) {
|
|
283
|
+
remove_listeners[key]();
|
|
284
|
+
remove_listeners[key] = undefined;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (const key in prev) {
|
|
289
|
+
if (!(key in next)) {
|
|
290
|
+
if (key === '#class') {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
set_attribute_helper(element, key, null, remove_listeners);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** @type {typeof prev} */
|
|
286
298
|
const current = {};
|
|
287
299
|
for (const key in next) {
|
|
288
300
|
if (key === 'children') continue;
|
|
@@ -293,13 +305,11 @@ export function apply_element_spread(element, fn) {
|
|
|
293
305
|
}
|
|
294
306
|
current[key] = value;
|
|
295
307
|
|
|
296
|
-
if (
|
|
297
|
-
prev[key] = value;
|
|
298
|
-
} else if (key !== '#class') {
|
|
308
|
+
if (key in prev && prev[key] === value && key !== '#class') {
|
|
299
309
|
continue;
|
|
300
310
|
}
|
|
301
311
|
|
|
302
|
-
set_attribute_helper(element, key, value);
|
|
312
|
+
set_attribute_helper(element, key, value, remove_listeners);
|
|
303
313
|
}
|
|
304
314
|
prev = current;
|
|
305
315
|
};
|
|
@@ -1197,16 +1197,20 @@ export function safe_scope(err = 'Cannot access outside of a component context')
|
|
|
1197
1197
|
return /** @type {Block} */ (active_scope);
|
|
1198
1198
|
}
|
|
1199
1199
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
*/
|
|
1203
|
-
export function push_component() {
|
|
1204
|
-
var component = {
|
|
1200
|
+
export function create_component_ctx() {
|
|
1201
|
+
return {
|
|
1205
1202
|
c: null,
|
|
1206
1203
|
e: null,
|
|
1207
1204
|
m: false,
|
|
1208
1205
|
p: active_component,
|
|
1209
1206
|
};
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* @returns {void}
|
|
1211
|
+
*/
|
|
1212
|
+
export function push_component() {
|
|
1213
|
+
var component = create_component_ctx();
|
|
1210
1214
|
active_component = component;
|
|
1211
1215
|
}
|
|
1212
1216
|
|
|
@@ -20,8 +20,8 @@ export function MediaQuery(query, fallback) {
|
|
|
20
20
|
|
|
21
21
|
let final_query =
|
|
22
22
|
parenthesis_regex.test(query) ||
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// we need to use `some` here because technically this `window.matchMedia('random,screen')` still returns true
|
|
24
|
+
query.split(/[\s,]+/).some((keyword) => non_parenthesized_keywords.has(keyword.trim()))
|
|
25
25
|
? query
|
|
26
26
|
: `(${query})`;
|
|
27
27
|
const q = window.matchMedia(final_query);
|
|
@@ -29,11 +29,17 @@ export function MediaQuery(query, fallback) {
|
|
|
29
29
|
|
|
30
30
|
return new ReactiveValue(
|
|
31
31
|
() => get(matches),
|
|
32
|
-
() =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
() =>
|
|
33
|
+
on(
|
|
34
|
+
q,
|
|
35
|
+
'change',
|
|
36
|
+
() => {
|
|
37
|
+
// skip wrapping in untrack as createSubscriber already does it
|
|
38
|
+
if (q.matches !== get(matches)) {
|
|
39
|
+
set(matches, q.matches);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{ delegated: false },
|
|
43
|
+
),
|
|
38
44
|
);
|
|
39
45
|
}
|