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 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.167",
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.167"
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
- get_delegated_event,
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 delegated_event = get_delegated_event(event_name, handler, state);
733
+ const is_delegated = is_delegated_event(attr.name.name, handler, context);
735
734
 
736
- if (delegated_event) {
735
+ if (is_delegated) {
737
736
  if (attr.metadata === undefined) {
738
737
  attr.metadata = {};
739
738
  }
740
739
 
741
- attr.metadata.delegated = delegated_event;
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 { is_event_attribute, is_passive_event } from '../../../../utils/events.js';
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
- let capture = name.endsWith('Capture');
980
- let event_name = capture
981
- ? name.slice(2, -7).toLowerCase()
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
- let delegated_assignment;
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), delegated_assignment)),
995
+ b.stmt(b.assignment('=', b.member(id, '__' + event_name), handler)),
1006
996
  );
1007
997
  } else {
1008
- const passive = is_passive_event(event_name);
1009
- const id = state.flush_node();
1010
-
1011
- state.init.push(
1012
- b.stmt(
1013
- b.call(
1014
- '_$_.event',
1015
- b.literal(event_name),
1016
- id,
1017
- handler,
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
@@ -1,8 +1,8 @@
1
- /** @import { Identifier, Pattern, Super, FunctionExpression, FunctionDeclaration, ArrowFunctionExpression, MemberExpression, AssignmentExpression, Expression, Node, AssignmentOperator, CallExpression } from 'estree' */
2
- /** @import { Component, Element, Attribute, SpreadAttribute, ScopeInterface, Binding, RippleNode, CompilerState, TransformContext, DelegatedEventResult, TextNode } from '#compiler' */
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 { get_attribute_event_name, is_delegated, is_event_attribute } from '../utils/events.js';
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 {CompilerState} state
176
+ * @param {TransformContext} context
177
177
  * @returns {boolean}
178
178
  */
179
- export function get_delegated_event(event_name, handler, state) {
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 (!handler || !is_delegated(event_name)) {
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;
@@ -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?: (event: MouseEvent) => void;
44
- onInput?: (event: InputEvent) => void;
45
- onChange?: (event: Event) => void;
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, fn, { compat });
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 { is_passive_event } from '../../../utils/events.js';
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 {AddEventListenerOptions} [options]
55
+ * @param {ExtendedEventOptions} [options]
23
56
  */
24
57
  export function on(element, type, handler, options = {}) {
25
- var target_handler = create_event(type.toLowerCase(), element, handler, options);
58
+ var remove_listener = create_event(type, element, handler, options);
26
59
 
27
60
  return () => {
28
- element.removeEventListener(type, target_handler, options);
61
+ remove_listener();
29
62
  };
30
63
  }
31
64
 
32
- let last_propagated_event = null;
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
- if (is_array(delegated)) {
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} [handler]
178
- * @param {AddEventListenerOptions} [options]
205
+ * @param {EventListener} handler
206
+ * @param {AddEventOptions} options
207
+ * @returns {() => void}
179
208
  */
180
- function create_event(event_name, dom, handler, options = {}) {
181
- /** @this {any} */
182
- function target_handler(/** @type {Event} */ event) {
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
- dom.addEventListener(event_name, target_handler, options);
273
+ var event_options = get_event_options(options);
207
274
 
208
- return target_handler;
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} [handler]
215
- * @param {boolean} [capture]
216
- * @param {boolean} [passive]
217
- * @returns {void}
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 event(event_name, dom, handler, capture, passive) {
220
- var options = { capture, passive };
221
- create_event(event_name, dom, handler, options);
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 { delegate, event } from './events.js';
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
- if (is_delegated(event_name)) {
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 (let symbol of get_own_property_symbols(effects)) {
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
- /** @type {Record<string | symbol, any>} */
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 (!(key in prev) || prev[key] !== value) {
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
- * @returns {void}
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
- // 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()))
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
- () => on(q, 'change', () => {
33
- // skip wrapping in untrack as createSubscriber already does it
34
- if (q.matches !== get(matches)) {
35
- set(matches, q.matches)
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
  }