@fictjs/runtime 0.16.0 → 0.17.1
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/dist/advanced.cjs +9 -9
- package/dist/advanced.d.cts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +4 -4
- package/dist/{binding-CQUGLLBI.d.ts → binding-BfzY9rae.d.ts} +2 -2
- package/dist/{binding-BlABuUiG.d.cts → binding-CDR2ERoq.d.cts} +2 -2
- package/dist/{chunk-CBRGOLTR.cjs → chunk-2J4INHDT.cjs} +40 -40
- package/dist/{chunk-CBRGOLTR.cjs.map → chunk-2J4INHDT.cjs.map} +1 -1
- package/dist/{chunk-BADX4WTQ.cjs → chunk-CKKZDUHM.cjs} +21 -18
- package/dist/chunk-CKKZDUHM.cjs.map +1 -0
- package/dist/{chunk-ZWQLXWSV.js → chunk-DHRRJJ6W.js} +8 -5
- package/dist/chunk-DHRRJJ6W.js.map +1 -0
- package/dist/{chunk-4P4DYWLQ.js → chunk-LFLFSJFU.js} +3 -3
- package/dist/{chunk-WJMZ7X46.cjs → chunk-NBDEMBBX.cjs} +47 -85
- package/dist/chunk-NBDEMBBX.cjs.map +1 -0
- package/dist/{chunk-MAHWGB55.js → chunk-OKPQWORE.js} +47 -85
- package/dist/chunk-OKPQWORE.js.map +1 -0
- package/dist/{chunk-RK2WSQYL.js → chunk-OLHZBAIF.js} +3 -3
- package/dist/{chunk-ZJZ6LMDN.js → chunk-R2HYEOP7.js} +470 -172
- package/dist/chunk-R2HYEOP7.js.map +1 -0
- package/dist/{chunk-AR2T7JEX.cjs → chunk-UG2IFQOY.cjs} +650 -352
- package/dist/chunk-UG2IFQOY.cjs.map +1 -0
- package/dist/{chunk-ECNK25S4.cjs → chunk-VP2WC7X3.cjs} +8 -8
- package/dist/{chunk-ECNK25S4.cjs.map → chunk-VP2WC7X3.cjs.map} +1 -1
- package/dist/{devtools-DWIZRe7L.d.cts → devtools-BwkkQ6DN.d.cts} +1 -1
- package/dist/{devtools-DNnnDGu1.d.ts → devtools-CK3SVU_w.d.ts} +1 -1
- package/dist/index.cjs +55 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.dev.js +260 -156
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -1
- package/dist/internal-list.cjs +4 -4
- package/dist/internal-list.js +3 -3
- package/dist/internal.cjs +5 -5
- package/dist/internal.d.cts +4 -4
- package/dist/internal.d.ts +4 -4
- package/dist/internal.js +4 -4
- package/dist/jsx-dev-runtime.cjs.map +1 -1
- package/dist/jsx-dev-runtime.d.cts +46 -0
- package/dist/jsx-dev-runtime.d.ts +46 -0
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.cjs.map +1 -1
- package/dist/jsx-runtime.d.cts +46 -0
- package/dist/jsx-runtime.d.ts +46 -0
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/loader.cjs +143 -26
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.js +122 -5
- package/dist/loader.js.map +1 -1
- package/dist/{props-DabFQwLR.d.ts → props-CFoQ471Y.d.ts} +47 -1
- package/dist/{props-tImUZAty.d.cts → props-D4tK8Gn0.d.cts} +47 -1
- package/dist/{resume-C5IKAIdh.d.ts → resume-C166aAVg.d.ts} +2 -2
- package/dist/{resume-DPZxmA95.d.cts → resume-C20cRVj9.d.cts} +2 -2
- package/dist/{scope-gpOMWTlf.d.ts → scope-BFzD_7hx.d.ts} +1 -1
- package/dist/{scope-GwC4DJ50.d.cts → scope-Ck3mTQVS.d.cts} +1 -1
- package/package.json +1 -1
- package/src/binding.ts +561 -166
- package/src/context.ts +8 -1
- package/src/dom.ts +26 -44
- package/src/effect.ts +9 -12
- package/src/error-boundary.ts +8 -0
- package/src/hydration.ts +25 -6
- package/src/jsx.ts +46 -0
- package/src/lifecycle.ts +31 -79
- package/src/loader.ts +153 -4
- package/src/resume.ts +5 -5
- package/src/signal.ts +4 -1
- package/src/suspense.ts +8 -0
- package/dist/chunk-AR2T7JEX.cjs.map +0 -1
- package/dist/chunk-BADX4WTQ.cjs.map +0 -1
- package/dist/chunk-MAHWGB55.js.map +0 -1
- package/dist/chunk-WJMZ7X46.cjs.map +0 -1
- package/dist/chunk-ZJZ6LMDN.js.map +0 -1
- package/dist/chunk-ZWQLXWSV.js.map +0 -1
- /package/dist/{chunk-4P4DYWLQ.js.map → chunk-LFLFSJFU.js.map} +0 -0
- /package/dist/{chunk-RK2WSQYL.js.map → chunk-OLHZBAIF.js.map} +0 -0
package/src/binding.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
SVGNamespace,
|
|
22
22
|
} from './constants'
|
|
23
23
|
import { createRenderEffect } from './effect'
|
|
24
|
-
import { withHydrationRange, isHydratingActive } from './hydration'
|
|
24
|
+
import { withHydration, withHydrationRange, isHydratingActive } from './hydration'
|
|
25
25
|
import { Fragment } from './jsx'
|
|
26
26
|
import {
|
|
27
27
|
createRootContext,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import { toNodeArray, removeNodes, insertNodesBefore } from './node-ops'
|
|
39
39
|
import { __fictIsHydrating } from './resume'
|
|
40
40
|
import { batch } from './scheduler'
|
|
41
|
-
import { computed, untrack, isSignal, isComputed, isEffect, isEffectScope } from './signal'
|
|
41
|
+
import { computed, signal, untrack, isSignal, isComputed, isEffect, isEffectScope } from './signal'
|
|
42
42
|
import type { Cleanup, FictNode } from './types'
|
|
43
43
|
|
|
44
44
|
const isDev =
|
|
@@ -52,7 +52,9 @@ const PROP_CACHE = Symbol('fict:prop')
|
|
|
52
52
|
const STYLE_CACHE = Symbol('fict:style')
|
|
53
53
|
const CLASS_STATE_CACHE = Symbol('fict:class-state')
|
|
54
54
|
const CLASS_VALUE_CACHE = Symbol('fict:class-value')
|
|
55
|
-
const
|
|
55
|
+
const EVENT_LISTENER_CACHE = Symbol('fict:event-listener-cache')
|
|
56
|
+
const REF_ASSIGN_CACHE = Symbol('fict:ref-assign-cache')
|
|
57
|
+
const CHILDREN_BINDING_CACHE = Symbol('fict:children-binding-cache')
|
|
56
58
|
const NON_REACTIVE_FN_MARKER = Symbol.for('fict:non-reactive-fn')
|
|
57
59
|
const REACTIVE_FN_MARKER = Symbol.for('fict:reactive-fn')
|
|
58
60
|
const NON_REACTIVE_FN_REGISTRY_KEY = Symbol.for('fict:non-reactive-fn-registry')
|
|
@@ -61,7 +63,25 @@ type NonReactiveRegistryHost = typeof globalThis & {
|
|
|
61
63
|
[NON_REACTIVE_FN_REGISTRY_KEY]?: WeakSet<(...args: unknown[]) => unknown>
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
interface StoredEventListener {
|
|
67
|
+
listener: EventListener
|
|
68
|
+
options: boolean | AddEventListenerOptions | undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ChildrenBindingState {
|
|
72
|
+
cleanup: Cleanup | undefined
|
|
73
|
+
value: (next?: FictNode | undefined) => FictNode | void
|
|
74
|
+
owner: RootContext | undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface AssignedRefState {
|
|
78
|
+
cleanup: Cleanup | undefined
|
|
79
|
+
owner: RootContext | undefined
|
|
80
|
+
registeredCleanup: boolean
|
|
81
|
+
value: ((next?: unknown) => unknown | void) | undefined
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type EventListenerStore = Map<string, StoredEventListener>
|
|
65
85
|
|
|
66
86
|
const PROPERTY_BINDING_KEYS = new Set([
|
|
67
87
|
'value',
|
|
@@ -105,6 +125,12 @@ export type MaybeReactive<T> = T | (() => T)
|
|
|
105
125
|
/** Internal type for createElement function reference */
|
|
106
126
|
export type CreateElementFn = (node: FictNode) => Node
|
|
107
127
|
|
|
128
|
+
let registeredCreateElement: CreateElementFn | undefined
|
|
129
|
+
|
|
130
|
+
export function registerCreateElement(fn: CreateElementFn): void {
|
|
131
|
+
registeredCreateElement = fn
|
|
132
|
+
}
|
|
133
|
+
|
|
108
134
|
/** Handle returned by conditional/list bindings for cleanup */
|
|
109
135
|
export interface BindingHandle {
|
|
110
136
|
/** Marker node(s) used for positioning */
|
|
@@ -1215,28 +1241,19 @@ function globalEventHandler(e: Event): void {
|
|
|
1215
1241
|
if (!node) return false
|
|
1216
1242
|
const handler = node[key]
|
|
1217
1243
|
if (handler && !(node as HTMLButtonElement).disabled) {
|
|
1218
|
-
const resolveData = (value: unknown): unknown => {
|
|
1219
|
-
if (typeof value === 'function') {
|
|
1220
|
-
try {
|
|
1221
|
-
const fn = value as (event?: Event) => unknown
|
|
1222
|
-
return fn.length > 0 ? fn(e) : fn()
|
|
1223
|
-
} catch {
|
|
1224
|
-
return (value as () => unknown)()
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
return value
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
1244
|
const rawData = (node as any)[dataKey] as unknown
|
|
1231
1245
|
const hasData = rawData !== undefined
|
|
1232
|
-
const resolvedNodeData = hasData ?
|
|
1246
|
+
const resolvedNodeData = hasData ? resolveEventData(rawData, e) : undefined
|
|
1233
1247
|
// Wrap event handler calls in batch for synchronous flush & reduced microtasks
|
|
1234
1248
|
batch(() => {
|
|
1235
1249
|
if (typeof handler === 'function') {
|
|
1236
1250
|
callEventHandler(handler, e, node, hasData ? resolvedNodeData : undefined)
|
|
1237
1251
|
} else if (Array.isArray(handler)) {
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1252
|
+
const tupleHandler = resolveEventHandlerValue(
|
|
1253
|
+
handler[0] as EventListenerOrEventListenerObject | null | undefined,
|
|
1254
|
+
)
|
|
1255
|
+
const tupleData = resolveEventData(handler[1], e)
|
|
1256
|
+
callEventHandler(tupleHandler, e, node, tupleData)
|
|
1240
1257
|
}
|
|
1241
1258
|
})
|
|
1242
1259
|
if (e.cancelBubble) return false
|
|
@@ -1314,12 +1331,21 @@ function globalEventHandler(e: Event): void {
|
|
|
1314
1331
|
export function addEventListener(
|
|
1315
1332
|
node: Element,
|
|
1316
1333
|
name: string,
|
|
1317
|
-
handler:
|
|
1334
|
+
handler:
|
|
1335
|
+
| EventListenerOrEventListenerObject
|
|
1336
|
+
| [EventListenerOrEventListenerObject, unknown]
|
|
1337
|
+
| null
|
|
1338
|
+
| undefined,
|
|
1318
1339
|
delegate?: boolean,
|
|
1340
|
+
options?: boolean | AddEventListenerOptions,
|
|
1319
1341
|
): void {
|
|
1320
1342
|
if (delegate) {
|
|
1321
1343
|
const key = `$$${name}`
|
|
1322
1344
|
const dataKey = `${key}Data`
|
|
1345
|
+
const rootRef = getCurrentRoot()
|
|
1346
|
+
const delegationDocument = resolveDelegationDocument(node, rootRef)
|
|
1347
|
+
|
|
1348
|
+
delegateEvents([name], delegationDocument)
|
|
1323
1349
|
|
|
1324
1350
|
if (handler == null) {
|
|
1325
1351
|
;(node as unknown as Record<string, unknown>)[key] = undefined
|
|
@@ -1327,61 +1353,130 @@ export function addEventListener(
|
|
|
1327
1353
|
return
|
|
1328
1354
|
}
|
|
1329
1355
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1356
|
+
;(node as unknown as Record<string, unknown>)[key] = createEventInvoker(
|
|
1357
|
+
name,
|
|
1358
|
+
handler,
|
|
1359
|
+
node,
|
|
1360
|
+
rootRef,
|
|
1361
|
+
)
|
|
1362
|
+
;(node as unknown as Record<string, unknown>)[dataKey] = undefined
|
|
1338
1363
|
return
|
|
1339
1364
|
}
|
|
1340
1365
|
|
|
1366
|
+
removeStoredEventListener(node, name, options)
|
|
1341
1367
|
if (handler == null) return
|
|
1342
1368
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
const wrapped = (e: Event) => handlerFn.call(node, handler[1], e)
|
|
1352
|
-
store.set(name, wrapped)
|
|
1353
|
-
node.addEventListener(name, wrapped)
|
|
1354
|
-
return
|
|
1355
|
-
}
|
|
1369
|
+
const rootRef = getCurrentRoot()
|
|
1370
|
+
const wrapped = createEventInvoker(name, handler, node, rootRef)
|
|
1371
|
+
node.addEventListener(name, wrapped, options)
|
|
1372
|
+
getStoredEventListenerStore(node).set(getEventListenerStoreKey(name, options), {
|
|
1373
|
+
listener: wrapped,
|
|
1374
|
+
options,
|
|
1375
|
+
})
|
|
1376
|
+
}
|
|
1356
1377
|
|
|
1357
|
-
|
|
1358
|
-
node.
|
|
1378
|
+
function resolveDelegationDocument(node: Element, rootRef: RootContext | undefined): Document {
|
|
1379
|
+
const nodeDocument = node.ownerDocument ?? undefined
|
|
1380
|
+
if (rootRef?.ownerDocument && nodeDocument?.defaultView == null) {
|
|
1381
|
+
return rootRef.ownerDocument
|
|
1382
|
+
}
|
|
1383
|
+
return nodeDocument ?? rootRef?.ownerDocument ?? document
|
|
1359
1384
|
}
|
|
1360
1385
|
|
|
1361
|
-
function
|
|
1386
|
+
function getStoredEventListenerStore(node: Element): EventListenerStore {
|
|
1362
1387
|
const host = node as unknown as {
|
|
1363
|
-
[
|
|
1388
|
+
[EVENT_LISTENER_CACHE]?: EventListenerStore
|
|
1364
1389
|
}
|
|
1365
|
-
if (!host[
|
|
1366
|
-
host[
|
|
1390
|
+
if (!host[EVENT_LISTENER_CACHE]) {
|
|
1391
|
+
host[EVENT_LISTENER_CACHE] = new Map<string, StoredEventListener>()
|
|
1367
1392
|
}
|
|
1368
|
-
return host[
|
|
1393
|
+
return host[EVENT_LISTENER_CACHE]!
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
function getEventListenerStoreKey(
|
|
1397
|
+
name: string,
|
|
1398
|
+
options?: boolean | AddEventListenerOptions,
|
|
1399
|
+
): string {
|
|
1400
|
+
const capture = typeof options === 'boolean' ? options : options?.capture === true
|
|
1401
|
+
const passive = typeof options === 'object' && options?.passive === true
|
|
1402
|
+
const once = typeof options === 'object' && options?.once === true
|
|
1403
|
+
return `${name}:${capture ? 1 : 0}:${passive ? 1 : 0}:${once ? 1 : 0}`
|
|
1369
1404
|
}
|
|
1370
1405
|
|
|
1371
|
-
function
|
|
1406
|
+
function removeStoredEventListener(
|
|
1407
|
+
node: Element,
|
|
1408
|
+
name: string,
|
|
1409
|
+
options?: boolean | AddEventListenerOptions,
|
|
1410
|
+
): void {
|
|
1372
1411
|
const host = node as unknown as {
|
|
1373
|
-
[
|
|
1412
|
+
[EVENT_LISTENER_CACHE]?: EventListenerStore
|
|
1374
1413
|
}
|
|
1375
|
-
const store = host[
|
|
1414
|
+
const store = host[EVENT_LISTENER_CACHE]
|
|
1376
1415
|
if (!store) return
|
|
1377
1416
|
|
|
1378
|
-
const
|
|
1379
|
-
if (!
|
|
1417
|
+
const entry = store.get(getEventListenerStoreKey(name, options))
|
|
1418
|
+
if (!entry) return
|
|
1380
1419
|
|
|
1381
|
-
node.removeEventListener(name,
|
|
1382
|
-
store.delete(name)
|
|
1420
|
+
node.removeEventListener(name, entry.listener, entry.options)
|
|
1421
|
+
store.delete(getEventListenerStoreKey(name, options))
|
|
1383
1422
|
if (store.size === 0) {
|
|
1384
|
-
delete host[
|
|
1423
|
+
delete host[EVENT_LISTENER_CACHE]
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
function resolveEventData(value: unknown, event: Event): unknown {
|
|
1428
|
+
if (typeof value !== 'function') return value
|
|
1429
|
+
if (isReactive(value)) {
|
|
1430
|
+
return (value as () => unknown)()
|
|
1431
|
+
}
|
|
1432
|
+
try {
|
|
1433
|
+
const fn = value as (event?: Event) => unknown
|
|
1434
|
+
return fn.length > 0 ? fn(event) : fn()
|
|
1435
|
+
} catch {
|
|
1436
|
+
return (value as () => unknown)()
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function resolveEventHandlerValue(
|
|
1441
|
+
value: EventListenerOrEventListenerObject | null | undefined,
|
|
1442
|
+
): EventListenerOrEventListenerObject | null | undefined {
|
|
1443
|
+
if (isStrictlyReactive(value)) {
|
|
1444
|
+
return (value as () => EventListenerOrEventListenerObject | null | undefined)()
|
|
1445
|
+
}
|
|
1446
|
+
return value
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function createEventInvoker(
|
|
1450
|
+
eventName: string,
|
|
1451
|
+
handler:
|
|
1452
|
+
| EventListenerOrEventListenerObject
|
|
1453
|
+
| [EventListenerOrEventListenerObject, unknown]
|
|
1454
|
+
| null
|
|
1455
|
+
| undefined,
|
|
1456
|
+
node: Element,
|
|
1457
|
+
rootRef: RootContext | undefined,
|
|
1458
|
+
): EventListener {
|
|
1459
|
+
return (event: Event) => {
|
|
1460
|
+
try {
|
|
1461
|
+
if (Array.isArray(handler)) {
|
|
1462
|
+
const resolvedHandler = resolveEventHandlerValue(
|
|
1463
|
+
handler[0] as EventListenerOrEventListenerObject | null | undefined,
|
|
1464
|
+
)
|
|
1465
|
+
const data = resolveEventData(handler[1], event)
|
|
1466
|
+
callEventHandler(resolvedHandler, event, node, data)
|
|
1467
|
+
return
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
const resolvedHandler = resolveEventHandlerValue(
|
|
1471
|
+
handler as EventListenerOrEventListenerObject | null | undefined,
|
|
1472
|
+
)
|
|
1473
|
+
callEventHandler(resolvedHandler, event, node)
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
if (handleError(err, { source: 'event', eventName }, rootRef)) {
|
|
1476
|
+
return
|
|
1477
|
+
}
|
|
1478
|
+
throw err
|
|
1479
|
+
}
|
|
1385
1480
|
}
|
|
1386
1481
|
}
|
|
1387
1482
|
|
|
@@ -1412,63 +1507,28 @@ export function bindEvent(
|
|
|
1412
1507
|
options?: boolean | AddEventListenerOptions,
|
|
1413
1508
|
): Cleanup {
|
|
1414
1509
|
if (handler == null) return () => {}
|
|
1415
|
-
const rootRef = getCurrentRoot()
|
|
1416
1510
|
|
|
1417
1511
|
// Optimization: Global Event Delegation
|
|
1418
1512
|
// If the event is delegatable and no options were provided,
|
|
1419
1513
|
// we attach the handler to the element property and rely on the global listener.
|
|
1420
1514
|
const shouldDelegate = options == null && DelegatedEvents.has(eventName)
|
|
1421
1515
|
if (shouldDelegate) {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
// Ensure global delegation is active for this event
|
|
1425
|
-
delegateEvents([eventName])
|
|
1426
|
-
|
|
1427
|
-
// Use stricter check - don't misidentify regular callbacks as reactive
|
|
1428
|
-
const resolveHandler = isStrictlyReactive(handler)
|
|
1429
|
-
? (handler as () => EventListenerOrEventListenerObject | null | undefined)
|
|
1430
|
-
: () => handler
|
|
1431
|
-
|
|
1432
|
-
// Cache a single wrapper that resolves the latest handler when invoked
|
|
1433
|
-
// @ts-expect-error - using dynamic property for delegation
|
|
1434
|
-
el[key] = function (this: any, ...args: any[]) {
|
|
1435
|
-
try {
|
|
1436
|
-
const fn = resolveHandler()
|
|
1437
|
-
callEventHandler(fn as EventListenerOrEventListenerObject, args[0] as Event, el)
|
|
1438
|
-
} catch (err) {
|
|
1439
|
-
if (!handleError(err, { source: 'event', eventName }, rootRef)) {
|
|
1440
|
-
throw err
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1516
|
+
addEventListener(el, eventName, handler, true)
|
|
1444
1517
|
|
|
1445
1518
|
// Cleanup: remove property (no effect needed for static or reactive)
|
|
1446
1519
|
return () => {
|
|
1447
|
-
|
|
1448
|
-
el[key] = undefined
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
// Fallback: Native addEventListener
|
|
1453
|
-
// Used for non-delegated events or when options are present
|
|
1454
|
-
// Use stricter check - don't misidentify regular callbacks as reactive
|
|
1455
|
-
const getHandler = isStrictlyReactive(handler) ? (handler as () => unknown) : () => handler
|
|
1456
|
-
|
|
1457
|
-
// Create wrapped handler that resolves reactive handlers
|
|
1458
|
-
const wrapped: EventListener = event => {
|
|
1459
|
-
try {
|
|
1460
|
-
const resolved = getHandler()
|
|
1461
|
-
callEventHandler(resolved as EventListenerOrEventListenerObject, event, el)
|
|
1462
|
-
} catch (err) {
|
|
1463
|
-
if (handleError(err, { source: 'event', eventName }, rootRef)) {
|
|
1464
|
-
return
|
|
1465
|
-
}
|
|
1466
|
-
throw err
|
|
1520
|
+
addEventListener(el, eventName, null, true)
|
|
1467
1521
|
}
|
|
1468
1522
|
}
|
|
1469
1523
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1524
|
+
addEventListener(
|
|
1525
|
+
el,
|
|
1526
|
+
eventName,
|
|
1527
|
+
handler as EventListenerOrEventListenerObject | null | undefined,
|
|
1528
|
+
false,
|
|
1529
|
+
options,
|
|
1530
|
+
)
|
|
1531
|
+
const cleanup = () => removeStoredEventListener(el, eventName, options)
|
|
1472
1532
|
registerRootCleanup(cleanup)
|
|
1473
1533
|
return cleanup
|
|
1474
1534
|
}
|
|
@@ -1498,61 +1558,383 @@ export function bindEvent(
|
|
|
1498
1558
|
* bindRef(el, () => props.ref)
|
|
1499
1559
|
* ```
|
|
1500
1560
|
*/
|
|
1501
|
-
export function bindRef(el: Element, ref: unknown): Cleanup {
|
|
1561
|
+
export function bindRef(el: Element, ref: unknown, registerCleanup = true): Cleanup {
|
|
1502
1562
|
if (ref == null) return () => {}
|
|
1503
1563
|
|
|
1504
|
-
// Handle reactive refs (getters)
|
|
1505
1564
|
const getRef = isReactive(ref) ? (ref as () => unknown) : () => ref
|
|
1565
|
+
let currentRef: unknown
|
|
1506
1566
|
|
|
1507
|
-
const
|
|
1567
|
+
const applyRefValue = (refValue: unknown, value: Element | null) => {
|
|
1508
1568
|
if (refValue == null) return
|
|
1509
|
-
|
|
1510
1569
|
if (typeof refValue === 'function') {
|
|
1511
|
-
|
|
1512
|
-
;(refValue as (el: Element) => void)(el)
|
|
1570
|
+
;(refValue as (el: Element | null) => void)(value)
|
|
1513
1571
|
} else if (typeof refValue === 'object' && 'current' in refValue) {
|
|
1514
|
-
|
|
1515
|
-
;(refValue as { current: Element | null }).current = el
|
|
1572
|
+
;(refValue as { current: Element | null }).current = value
|
|
1516
1573
|
}
|
|
1517
1574
|
}
|
|
1518
1575
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1576
|
+
const clearCurrentRef = () => {
|
|
1577
|
+
if (currentRef == null) return
|
|
1578
|
+
applyRefValue(currentRef, null)
|
|
1579
|
+
currentRef = undefined
|
|
1580
|
+
}
|
|
1522
1581
|
|
|
1523
|
-
|
|
1582
|
+
const syncRef = (nextRef: unknown) => {
|
|
1583
|
+
if (nextRef === currentRef) return
|
|
1584
|
+
clearCurrentRef()
|
|
1585
|
+
currentRef = nextRef
|
|
1586
|
+
applyRefValue(currentRef, el)
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
let disposeTracking: Cleanup | undefined
|
|
1524
1590
|
if (isReactive(ref)) {
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
applyRef(currentRef)
|
|
1591
|
+
disposeTracking = createRenderEffect(() => {
|
|
1592
|
+
syncRef(getRef())
|
|
1528
1593
|
})
|
|
1529
|
-
|
|
1594
|
+
} else {
|
|
1595
|
+
syncRef(getRef())
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
if (registerCleanup) {
|
|
1599
|
+
registerRootCleanup(clearCurrentRef)
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
return () => {
|
|
1603
|
+
disposeTracking?.()
|
|
1604
|
+
clearCurrentRef()
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
function resolveAssignedChildrenValue(value: FictNode | undefined): FictNode {
|
|
1609
|
+
if (typeof value === 'function') {
|
|
1610
|
+
return isReactive(value) ? (value as () => FictNode)() : null
|
|
1611
|
+
}
|
|
1612
|
+
return value ?? null
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
function resolveAssignedRefValue(value: unknown): unknown {
|
|
1616
|
+
if (isReactive(value)) {
|
|
1617
|
+
return (value as () => unknown)()
|
|
1618
|
+
}
|
|
1619
|
+
return value
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
function createAssignedRefState(
|
|
1623
|
+
node: Element,
|
|
1624
|
+
owner: RootContext | undefined,
|
|
1625
|
+
initialValue: unknown,
|
|
1626
|
+
): AssignedRefState {
|
|
1627
|
+
const valueSignal = signal<unknown>(initialValue)
|
|
1628
|
+
let currentRef: unknown
|
|
1629
|
+
|
|
1630
|
+
const applyRefValue = (refValue: unknown, value: Element | null) => {
|
|
1631
|
+
if (refValue == null) return
|
|
1632
|
+
if (typeof refValue === 'function') {
|
|
1633
|
+
;(refValue as (el: Element | null) => void)(value)
|
|
1634
|
+
} else if (typeof refValue === 'object' && 'current' in refValue) {
|
|
1635
|
+
;(refValue as { current: Element | null }).current = value
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
const clearCurrentRef = () => {
|
|
1640
|
+
if (currentRef == null) return
|
|
1641
|
+
applyRefValue(currentRef, null)
|
|
1642
|
+
currentRef = undefined
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
const syncRef = (nextRef: unknown) => {
|
|
1646
|
+
if (nextRef === currentRef) return
|
|
1647
|
+
clearCurrentRef()
|
|
1648
|
+
currentRef = nextRef
|
|
1649
|
+
applyRefValue(currentRef, node)
|
|
1650
|
+
}
|
|
1530
1651
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1652
|
+
const disposeTracking = createRenderEffect(() => {
|
|
1653
|
+
syncRef(resolveAssignedRefValue(valueSignal() as unknown))
|
|
1654
|
+
})
|
|
1655
|
+
|
|
1656
|
+
return {
|
|
1657
|
+
cleanup: () => {
|
|
1658
|
+
disposeTracking()
|
|
1659
|
+
clearCurrentRef()
|
|
1660
|
+
},
|
|
1661
|
+
owner,
|
|
1662
|
+
registeredCleanup: false,
|
|
1663
|
+
value: (next?: unknown) => {
|
|
1664
|
+
valueSignal(next)
|
|
1665
|
+
syncRef(resolveAssignedRefValue(next))
|
|
1666
|
+
},
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function bindAssignedChildren(
|
|
1671
|
+
node: Element,
|
|
1672
|
+
getValue: () => FictNode,
|
|
1673
|
+
createElementFn?: CreateElementFn,
|
|
1674
|
+
): Cleanup {
|
|
1675
|
+
const hostRoot = getCurrentRoot()
|
|
1676
|
+
const createFn = createElementFn ?? registeredCreateElement
|
|
1677
|
+
let currentNodes: Node[] = []
|
|
1678
|
+
let currentText: Text | null = null
|
|
1679
|
+
let currentRoot: RootContext | null = null
|
|
1680
|
+
let initialHydrating = __fictIsHydrating()
|
|
1681
|
+
|
|
1682
|
+
const collectCurrentChildren = (): Node[] => Array.from(node.childNodes)
|
|
1683
|
+
|
|
1684
|
+
const clearCurrentNodes = () => {
|
|
1685
|
+
if (currentRoot) {
|
|
1686
|
+
destroyRoot(currentRoot)
|
|
1687
|
+
currentRoot = null
|
|
1688
|
+
}
|
|
1689
|
+
if (currentNodes.length > 0) {
|
|
1690
|
+
removeNodes(currentNodes)
|
|
1691
|
+
currentNodes = []
|
|
1692
|
+
}
|
|
1693
|
+
currentText = null
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
const setTextNode = (textValue: string, shouldInsert: boolean) => {
|
|
1697
|
+
if (!shouldInsert) {
|
|
1698
|
+
clearCurrentNodes()
|
|
1699
|
+
if (node.childNodes.length > 0) {
|
|
1700
|
+
node.replaceChildren()
|
|
1536
1701
|
}
|
|
1702
|
+
initialHydrating = false
|
|
1703
|
+
return
|
|
1537
1704
|
}
|
|
1538
|
-
registerRootCleanup(nullifyCleanup)
|
|
1539
1705
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1706
|
+
if (initialHydrating && isHydratingActive()) {
|
|
1707
|
+
const hydratedNodes = collectCurrentChildren()
|
|
1708
|
+
if (hydratedNodes.length === 1 && hydratedNodes[0]?.nodeType === Node.TEXT_NODE) {
|
|
1709
|
+
const hydratedText = hydratedNodes[0] as Text
|
|
1710
|
+
if (hydratedText.data !== textValue) {
|
|
1711
|
+
hydratedText.data = textValue
|
|
1712
|
+
}
|
|
1713
|
+
currentText = hydratedText
|
|
1714
|
+
currentNodes = [hydratedText]
|
|
1715
|
+
initialHydrating = false
|
|
1716
|
+
return
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
const textNode = currentText ?? (node.ownerDocument ?? document).createTextNode(textValue)
|
|
1721
|
+
if (textNode.data !== textValue) {
|
|
1722
|
+
textNode.data = textValue
|
|
1543
1723
|
}
|
|
1724
|
+
|
|
1725
|
+
if (currentNodes.length === 1 && currentNodes[0] === textNode) {
|
|
1726
|
+
currentText = textNode
|
|
1727
|
+
return
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
if (currentRoot) {
|
|
1731
|
+
destroyRoot(currentRoot)
|
|
1732
|
+
currentRoot = null
|
|
1733
|
+
}
|
|
1734
|
+
if (currentNodes.length > 0) {
|
|
1735
|
+
removeNodes(currentNodes)
|
|
1736
|
+
currentNodes = []
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
node.replaceChildren(textNode)
|
|
1740
|
+
currentText = textNode
|
|
1741
|
+
currentNodes = [textNode]
|
|
1742
|
+
initialHydrating = false
|
|
1544
1743
|
}
|
|
1545
1744
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
|
|
1745
|
+
const dispose = createRenderEffect(() => {
|
|
1746
|
+
const value = getValue()
|
|
1747
|
+
const isPrimitive =
|
|
1748
|
+
value == null ||
|
|
1749
|
+
value === false ||
|
|
1750
|
+
typeof value === 'string' ||
|
|
1751
|
+
typeof value === 'number' ||
|
|
1752
|
+
typeof value === 'boolean'
|
|
1753
|
+
|
|
1754
|
+
if (isPrimitive) {
|
|
1755
|
+
const textValue = value == null || value === false ? '' : String(value)
|
|
1756
|
+
const shouldInsert = value != null && value !== false
|
|
1757
|
+
setTextNode(textValue, shouldInsert)
|
|
1758
|
+
return
|
|
1551
1759
|
}
|
|
1760
|
+
|
|
1761
|
+
clearCurrentNodes()
|
|
1762
|
+
|
|
1763
|
+
const root = createRootContext(hostRoot)
|
|
1764
|
+
const prev = pushRoot(root)
|
|
1765
|
+
let nodes: Node[]
|
|
1766
|
+
let currentHydratedNodes: Node[] | undefined
|
|
1767
|
+
let handledError = false
|
|
1768
|
+
try {
|
|
1769
|
+
const ownerDocument = node.ownerDocument ?? hostRoot?.ownerDocument ?? document
|
|
1770
|
+
const createValue = () => {
|
|
1771
|
+
if (value instanceof Node) {
|
|
1772
|
+
return value
|
|
1773
|
+
}
|
|
1774
|
+
if (Array.isArray(value)) {
|
|
1775
|
+
if (value.every(v => v instanceof Node)) {
|
|
1776
|
+
return value as Node[]
|
|
1777
|
+
}
|
|
1778
|
+
if (createFn) {
|
|
1779
|
+
const mapped: Node[] = []
|
|
1780
|
+
for (const item of value) {
|
|
1781
|
+
mapped.push(...toNodeArray(createFn(item as any), ownerDocument))
|
|
1782
|
+
}
|
|
1783
|
+
return mapped
|
|
1784
|
+
}
|
|
1785
|
+
return ownerDocument.createTextNode(String(value))
|
|
1786
|
+
}
|
|
1787
|
+
return createFn ? createFn(value) : ownerDocument.createTextNode(String(value))
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
const newNode =
|
|
1791
|
+
initialHydrating && isHydratingActive()
|
|
1792
|
+
? withHydration(node, () => createValue())
|
|
1793
|
+
: createValue()
|
|
1794
|
+
|
|
1795
|
+
nodes = toNodeArray(newNode, ownerDocument)
|
|
1796
|
+
if (root.suspended) {
|
|
1797
|
+
handledError = true
|
|
1798
|
+
destroyRoot(root)
|
|
1799
|
+
return
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
if (initialHydrating) {
|
|
1803
|
+
const hydratedNodes = collectCurrentChildren()
|
|
1804
|
+
const reuseHydratedNodes =
|
|
1805
|
+
hydratedNodes.length === nodes.length &&
|
|
1806
|
+
nodes.every((candidate, index) => candidate === hydratedNodes[index])
|
|
1807
|
+
if (reuseHydratedNodes) {
|
|
1808
|
+
currentHydratedNodes = hydratedNodes
|
|
1809
|
+
} else {
|
|
1810
|
+
node.replaceChildren(...nodes)
|
|
1811
|
+
}
|
|
1812
|
+
} else {
|
|
1813
|
+
node.replaceChildren(...nodes)
|
|
1814
|
+
}
|
|
1815
|
+
} catch (err) {
|
|
1816
|
+
if (handleSuspend(err as any, root)) {
|
|
1817
|
+
handledError = true
|
|
1818
|
+
destroyRoot(root)
|
|
1819
|
+
return
|
|
1820
|
+
}
|
|
1821
|
+
if (handleError(err, { source: 'renderChild' }, root)) {
|
|
1822
|
+
handledError = true
|
|
1823
|
+
destroyRoot(root)
|
|
1824
|
+
return
|
|
1825
|
+
}
|
|
1826
|
+
throw err
|
|
1827
|
+
} finally {
|
|
1828
|
+
popRoot(prev)
|
|
1829
|
+
if (!handledError) {
|
|
1830
|
+
flushOnMount(root)
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
currentRoot = root
|
|
1835
|
+
currentNodes = currentHydratedNodes ?? nodes
|
|
1836
|
+
initialHydrating = false
|
|
1837
|
+
})
|
|
1838
|
+
|
|
1839
|
+
return () => {
|
|
1840
|
+
dispose()
|
|
1841
|
+
clearCurrentNodes()
|
|
1552
1842
|
}
|
|
1553
|
-
|
|
1843
|
+
}
|
|
1554
1844
|
|
|
1555
|
-
|
|
1845
|
+
function updateChildrenBinding(
|
|
1846
|
+
node: Element,
|
|
1847
|
+
value: FictNode | undefined,
|
|
1848
|
+
createElementFn?: CreateElementFn,
|
|
1849
|
+
): void {
|
|
1850
|
+
const host = node as {
|
|
1851
|
+
[CHILDREN_BINDING_CACHE]?: ChildrenBindingState
|
|
1852
|
+
}
|
|
1853
|
+
const createFn = createElementFn ?? registeredCreateElement
|
|
1854
|
+
const owner = getCurrentRoot()
|
|
1855
|
+
let state = host[CHILDREN_BINDING_CACHE]
|
|
1856
|
+
|
|
1857
|
+
if (state && state.owner !== owner) {
|
|
1858
|
+
state.cleanup?.()
|
|
1859
|
+
state.cleanup = undefined
|
|
1860
|
+
delete host[CHILDREN_BINDING_CACHE]
|
|
1861
|
+
state = undefined
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
if (!state) {
|
|
1865
|
+
const valueSignal = signal<FictNode | undefined>(value)
|
|
1866
|
+
const cleanup = bindAssignedChildren(
|
|
1867
|
+
node,
|
|
1868
|
+
() => resolveAssignedChildrenValue(valueSignal() as FictNode | undefined),
|
|
1869
|
+
createFn,
|
|
1870
|
+
)
|
|
1871
|
+
const nextState: ChildrenBindingState = {
|
|
1872
|
+
cleanup,
|
|
1873
|
+
owner,
|
|
1874
|
+
value: valueSignal,
|
|
1875
|
+
}
|
|
1876
|
+
state = nextState
|
|
1877
|
+
host[CHILDREN_BINDING_CACHE] = nextState
|
|
1878
|
+
registerRootCleanup(() => {
|
|
1879
|
+
state?.cleanup?.()
|
|
1880
|
+
if (state) {
|
|
1881
|
+
state.cleanup = undefined
|
|
1882
|
+
}
|
|
1883
|
+
if (host[CHILDREN_BINDING_CACHE] === state) {
|
|
1884
|
+
delete host[CHILDREN_BINDING_CACHE]
|
|
1885
|
+
}
|
|
1886
|
+
})
|
|
1887
|
+
return
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
state.value(value)
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function updateAssignedRefBinding(node: Element, value: unknown): void {
|
|
1894
|
+
const host = node as {
|
|
1895
|
+
[REF_ASSIGN_CACHE]?: AssignedRefState
|
|
1896
|
+
}
|
|
1897
|
+
const owner = getCurrentRoot()
|
|
1898
|
+
let state = host[REF_ASSIGN_CACHE]
|
|
1899
|
+
|
|
1900
|
+
if (state && state.owner !== owner) {
|
|
1901
|
+
state.cleanup?.()
|
|
1902
|
+
state.cleanup = undefined
|
|
1903
|
+
delete host[REF_ASSIGN_CACHE]
|
|
1904
|
+
state = undefined
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (!state && value == null) {
|
|
1908
|
+
return
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
if (!state) {
|
|
1912
|
+
state = createAssignedRefState(node, owner, value)
|
|
1913
|
+
host[REF_ASSIGN_CACHE] = state
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
state.value?.(value)
|
|
1917
|
+
|
|
1918
|
+
if (value == null) {
|
|
1919
|
+
if (!state.registeredCleanup) {
|
|
1920
|
+
state.cleanup?.()
|
|
1921
|
+
state.cleanup = undefined
|
|
1922
|
+
delete host[REF_ASSIGN_CACHE]
|
|
1923
|
+
}
|
|
1924
|
+
return
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
if (!state.registeredCleanup && getCurrentRoot()) {
|
|
1928
|
+
state.registeredCleanup = true
|
|
1929
|
+
registerRootCleanup(() => {
|
|
1930
|
+
state.cleanup?.()
|
|
1931
|
+
state.cleanup = undefined
|
|
1932
|
+
state.value = undefined
|
|
1933
|
+
if (host[REF_ASSIGN_CACHE] === state) {
|
|
1934
|
+
delete host[REF_ASSIGN_CACHE]
|
|
1935
|
+
}
|
|
1936
|
+
})
|
|
1937
|
+
}
|
|
1556
1938
|
}
|
|
1557
1939
|
|
|
1558
1940
|
// ============================================================================
|
|
@@ -1590,27 +1972,15 @@ export function spread(
|
|
|
1590
1972
|
return next
|
|
1591
1973
|
}
|
|
1592
1974
|
|
|
1593
|
-
// Handle children if not skipped
|
|
1594
|
-
if (!skipChildren) {
|
|
1595
|
-
createRenderEffect(() => {
|
|
1596
|
-
const nextProps = resolveProps()
|
|
1597
|
-
if ('children' in nextProps) {
|
|
1598
|
-
prevProps.children = nextProps.children
|
|
1599
|
-
}
|
|
1600
|
-
})
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
1975
|
// Handle ref
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
1609
|
-
})
|
|
1976
|
+
bindRef(
|
|
1977
|
+
node,
|
|
1978
|
+
(typeof props === 'function' ? () => resolveProps().ref : resolveProps().ref) ?? null,
|
|
1979
|
+
)
|
|
1610
1980
|
|
|
1611
1981
|
// Handle all other props
|
|
1612
1982
|
createRenderEffect(() => {
|
|
1613
|
-
assign(node, resolveProps(), isSVG,
|
|
1983
|
+
assign(node, resolveProps(), isSVG, skipChildren, prevProps, true, excludedProps)
|
|
1614
1984
|
})
|
|
1615
1985
|
|
|
1616
1986
|
return prevProps
|
|
@@ -1642,7 +2012,13 @@ export function assign(
|
|
|
1642
2012
|
for (const prop in prevProps) {
|
|
1643
2013
|
if (excludedProps?.has(prop)) continue
|
|
1644
2014
|
if (!(prop in props)) {
|
|
1645
|
-
if (prop === 'children')
|
|
2015
|
+
if (prop === 'children') {
|
|
2016
|
+
if (!skipChildren) {
|
|
2017
|
+
updateChildrenBinding(node, undefined)
|
|
2018
|
+
prevProps.children = undefined
|
|
2019
|
+
}
|
|
2020
|
+
continue
|
|
2021
|
+
}
|
|
1646
2022
|
prevProps[prop] = assignProp(node, prop, null, prevProps[prop], isSVG, skipRef, props)
|
|
1647
2023
|
}
|
|
1648
2024
|
}
|
|
@@ -1650,14 +2026,14 @@ export function assign(
|
|
|
1650
2026
|
// Set or update props
|
|
1651
2027
|
for (const prop in props) {
|
|
1652
2028
|
if (excludedProps?.has(prop)) continue
|
|
2029
|
+
const value = props[prop]
|
|
1653
2030
|
if (prop === 'children') {
|
|
1654
2031
|
if (!skipChildren) {
|
|
1655
|
-
|
|
2032
|
+
updateChildrenBinding(node, value as FictNode | undefined)
|
|
1656
2033
|
prevProps.children = props.children
|
|
1657
2034
|
}
|
|
1658
2035
|
continue
|
|
1659
2036
|
}
|
|
1660
|
-
const value = props[prop]
|
|
1661
2037
|
prevProps[prop] = assignProp(node, prop, value, prevProps[prop], isSVG, skipRef, props)
|
|
1662
2038
|
}
|
|
1663
2039
|
}
|
|
@@ -1690,8 +2066,8 @@ function assignProp(
|
|
|
1690
2066
|
|
|
1691
2067
|
// Ref handling
|
|
1692
2068
|
if (prop === 'ref') {
|
|
1693
|
-
if (!skipRef
|
|
1694
|
-
|
|
2069
|
+
if (!skipRef) {
|
|
2070
|
+
updateAssignedRefBinding(node, value)
|
|
1695
2071
|
}
|
|
1696
2072
|
return value
|
|
1697
2073
|
}
|
|
@@ -1703,16 +2079,35 @@ function assignProp(
|
|
|
1703
2079
|
node.setAttribute(prop, value)
|
|
1704
2080
|
return value
|
|
1705
2081
|
}
|
|
1706
|
-
if (prev && typeof prev !== 'string') node
|
|
1707
|
-
|
|
2082
|
+
if (prev && typeof prev !== 'string') removeStoredEventListener(node, eventName)
|
|
2083
|
+
addEventListener(
|
|
2084
|
+
node,
|
|
2085
|
+
eventName,
|
|
2086
|
+
value as
|
|
2087
|
+
| EventListenerOrEventListenerObject
|
|
2088
|
+
| [EventListenerOrEventListenerObject, unknown]
|
|
2089
|
+
| null
|
|
2090
|
+
| undefined,
|
|
2091
|
+
false,
|
|
2092
|
+
)
|
|
1708
2093
|
return value
|
|
1709
2094
|
}
|
|
1710
2095
|
|
|
1711
2096
|
// Capture event handling: oncapture:eventname
|
|
1712
2097
|
if (prop.slice(0, 10) === 'oncapture:') {
|
|
1713
2098
|
const eventName = prop.slice(10)
|
|
1714
|
-
if (prev) node
|
|
1715
|
-
|
|
2099
|
+
if (prev) removeStoredEventListener(node, eventName, true)
|
|
2100
|
+
addEventListener(
|
|
2101
|
+
node,
|
|
2102
|
+
eventName,
|
|
2103
|
+
value as
|
|
2104
|
+
| EventListenerOrEventListenerObject
|
|
2105
|
+
| [EventListenerOrEventListenerObject, unknown]
|
|
2106
|
+
| null
|
|
2107
|
+
| undefined,
|
|
2108
|
+
false,
|
|
2109
|
+
true,
|
|
2110
|
+
)
|
|
1716
2111
|
return value
|
|
1717
2112
|
}
|
|
1718
2113
|
|
|
@@ -1721,20 +2116,20 @@ function assignProp(
|
|
|
1721
2116
|
const eventName = prop.slice(2).toLowerCase()
|
|
1722
2117
|
const shouldDelegate = DelegatedEvents.has(eventName)
|
|
1723
2118
|
if (!shouldDelegate && prev) {
|
|
1724
|
-
|
|
1725
|
-
removeStoredTupleEventListener(node, eventName)
|
|
1726
|
-
} else {
|
|
1727
|
-
node.removeEventListener(eventName, prev as EventListener)
|
|
1728
|
-
}
|
|
2119
|
+
removeStoredEventListener(node, eventName)
|
|
1729
2120
|
}
|
|
1730
2121
|
if (shouldDelegate || value) {
|
|
1731
2122
|
addEventListener(
|
|
1732
2123
|
node,
|
|
1733
2124
|
eventName,
|
|
1734
|
-
value as
|
|
2125
|
+
value as
|
|
2126
|
+
| EventListenerOrEventListenerObject
|
|
2127
|
+
| [EventListenerOrEventListenerObject, unknown]
|
|
2128
|
+
| null
|
|
2129
|
+
| undefined,
|
|
1735
2130
|
shouldDelegate,
|
|
2131
|
+
false,
|
|
1736
2132
|
)
|
|
1737
|
-
if (shouldDelegate) delegateEvents([eventName])
|
|
1738
2133
|
}
|
|
1739
2134
|
return value
|
|
1740
2135
|
}
|