@fictjs/runtime 0.0.8 → 0.0.10
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/index.cjs +2542 -2457
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -26
- package/dist/index.d.ts +30 -26
- package/dist/index.dev.js +2583 -2495
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +2542 -2457
- package/dist/index.js.map +1 -1
- package/dist/slim.cjs +2123 -1988
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.d.cts +13 -11
- package/dist/slim.d.ts +13 -11
- package/dist/slim.js +2123 -1988
- package/dist/slim.js.map +1 -1
- package/package.json +1 -1
- package/src/binding.ts +115 -92
- package/src/dom.ts +90 -38
- package/src/effect.ts +2 -1
- package/src/error-boundary.ts +3 -1
- package/src/hooks.ts +14 -1
- package/src/list-helpers.ts +141 -50
- package/src/ref.ts +1 -1
- package/src/signal.ts +16 -2
- package/src/store.ts +30 -2
- package/src/types.ts +3 -3
package/src/list-helpers.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createRootContext,
|
|
12
12
|
destroyRoot,
|
|
13
13
|
flushOnMount,
|
|
14
|
+
getCurrentRoot,
|
|
14
15
|
popRoot,
|
|
15
16
|
pushRoot,
|
|
16
17
|
type RootContext,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
import { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'
|
|
19
20
|
import reconcileArrays from './reconcile'
|
|
20
21
|
import { batch } from './scheduler'
|
|
21
|
-
import { createSignal, setActiveSub, type Signal } from './signal'
|
|
22
|
+
import { createSignal, flush, setActiveSub, type Signal } from './signal'
|
|
22
23
|
import type { FictNode } from './types'
|
|
23
24
|
|
|
24
25
|
// Re-export shared DOM helpers for compiler-generated code
|
|
@@ -200,7 +201,7 @@ function removeBlockRange(block: MarkerBlock): void {
|
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
|
|
203
|
-
function createVersionedSignalAccessor<T>(initialValue: T): Signal<T> {
|
|
204
|
+
export function createVersionedSignalAccessor<T>(initialValue: T): Signal<T> {
|
|
204
205
|
let current = initialValue
|
|
205
206
|
let version = 0
|
|
206
207
|
const track = createSignal(version)
|
|
@@ -243,6 +244,16 @@ export function createKeyedListContainer<T = unknown>(): KeyedListContainer<T> {
|
|
|
243
244
|
container.nextBlocks.clear()
|
|
244
245
|
|
|
245
246
|
// Remove nodes (including markers)
|
|
247
|
+
// Check if markers are still in DOM before using Range
|
|
248
|
+
if (!startMarker.parentNode || !endMarker.parentNode) {
|
|
249
|
+
// Markers already removed, nothing to do
|
|
250
|
+
container.currentNodes = []
|
|
251
|
+
container.nextNodes = []
|
|
252
|
+
container.orderedBlocks.length = 0
|
|
253
|
+
container.nextOrderedBlocks.length = 0
|
|
254
|
+
container.orderedIndexByKey.clear()
|
|
255
|
+
return
|
|
256
|
+
}
|
|
246
257
|
const range = document.createRange()
|
|
247
258
|
range.setStartBefore(startMarker)
|
|
248
259
|
range.setEndAfter(endMarker)
|
|
@@ -292,6 +303,7 @@ export function createKeyedBlock<T>(
|
|
|
292
303
|
index: number,
|
|
293
304
|
render: (item: Signal<T>, index: Signal<number>, key: string | number) => Node[],
|
|
294
305
|
needsIndex = true,
|
|
306
|
+
hostRoot?: RootContext,
|
|
295
307
|
): KeyedBlock<T> {
|
|
296
308
|
// Use versioned signal for all item types; avoid diffing proxy overhead for objects
|
|
297
309
|
const itemSig = createVersionedSignalAccessor(item)
|
|
@@ -303,7 +315,7 @@ export function createKeyedBlock<T>(
|
|
|
303
315
|
index = next as number
|
|
304
316
|
return index
|
|
305
317
|
}) as Signal<number>)
|
|
306
|
-
const root = createRootContext()
|
|
318
|
+
const root = createRootContext(hostRoot)
|
|
307
319
|
const prevRoot = pushRoot(root)
|
|
308
320
|
|
|
309
321
|
// Isolate child effects from the outer effect (e.g., performDiff) by clearing activeSub.
|
|
@@ -327,7 +339,6 @@ export function createKeyedBlock<T>(
|
|
|
327
339
|
} finally {
|
|
328
340
|
setActiveSub(prevSub)
|
|
329
341
|
popRoot(prevRoot)
|
|
330
|
-
flushOnMount(root)
|
|
331
342
|
}
|
|
332
343
|
|
|
333
344
|
return {
|
|
@@ -399,18 +410,34 @@ function createFineGrainedKeyedList<T>(
|
|
|
399
410
|
needsIndex: boolean,
|
|
400
411
|
): KeyedListBinding {
|
|
401
412
|
const container = createKeyedListContainer<T>()
|
|
413
|
+
const hostRoot = getCurrentRoot()
|
|
402
414
|
const fragment = document.createDocumentFragment()
|
|
403
415
|
fragment.append(container.startMarker, container.endMarker)
|
|
404
|
-
let pendingItems: T[] | null = null
|
|
405
416
|
let disposed = false
|
|
417
|
+
let effectDispose: (() => void) | undefined
|
|
418
|
+
let connectObserver: MutationObserver | null = null
|
|
419
|
+
let effectStarted = false
|
|
420
|
+
let startScheduled = false
|
|
421
|
+
|
|
422
|
+
const getConnectedParent = (): (ParentNode & Node) | null => {
|
|
423
|
+
const endParent = container.endMarker.parentNode
|
|
424
|
+
const startParent = container.startMarker.parentNode
|
|
425
|
+
if (
|
|
426
|
+
endParent &&
|
|
427
|
+
startParent &&
|
|
428
|
+
endParent === startParent &&
|
|
429
|
+
(endParent as Node).nodeType !== 11
|
|
430
|
+
) {
|
|
431
|
+
return endParent as ParentNode & Node
|
|
432
|
+
}
|
|
433
|
+
return null
|
|
434
|
+
}
|
|
406
435
|
|
|
407
436
|
const performDiff = () => {
|
|
408
437
|
if (disposed) return
|
|
409
|
-
|
|
438
|
+
const parent = getConnectedParent()
|
|
439
|
+
if (!parent) return
|
|
410
440
|
batch(() => {
|
|
411
|
-
const newItems = pendingItems || getItems()
|
|
412
|
-
pendingItems = null
|
|
413
|
-
|
|
414
441
|
const oldBlocks = container.blocks
|
|
415
442
|
const newBlocks = container.nextBlocks
|
|
416
443
|
const prevOrderedBlocks = container.orderedBlocks
|
|
@@ -419,27 +446,20 @@ function createFineGrainedKeyedList<T>(
|
|
|
419
446
|
newBlocks.clear()
|
|
420
447
|
nextOrderedBlocks.length = 0
|
|
421
448
|
orderedIndexByKey.clear()
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
const startParent = container.startMarker.parentNode
|
|
425
|
-
const parent =
|
|
426
|
-
endParent && startParent && endParent === startParent && (endParent as Node).isConnected
|
|
427
|
-
? (endParent as ParentNode & Node)
|
|
428
|
-
: null
|
|
429
|
-
|
|
430
|
-
// If markers aren't mounted yet, store items and retry in microtask
|
|
431
|
-
if (!parent) {
|
|
432
|
-
pendingItems = newItems
|
|
433
|
-
queueMicrotask(performDiff)
|
|
434
|
-
return
|
|
435
|
-
}
|
|
449
|
+
const createdBlocks: KeyedBlock<T>[] = []
|
|
450
|
+
const newItems = getItems()
|
|
436
451
|
|
|
437
452
|
if (newItems.length === 0) {
|
|
438
453
|
if (oldBlocks.size > 0) {
|
|
454
|
+
// Destroy all block roots first
|
|
439
455
|
for (const block of oldBlocks.values()) {
|
|
440
456
|
destroyRoot(block.root)
|
|
441
|
-
removeNodes(block.nodes)
|
|
442
457
|
}
|
|
458
|
+
// Use Range.deleteContents for efficient bulk DOM removal
|
|
459
|
+
const range = document.createRange()
|
|
460
|
+
range.setStartAfter(container.startMarker)
|
|
461
|
+
range.setEndBefore(container.endMarker)
|
|
462
|
+
range.deleteContents()
|
|
443
463
|
}
|
|
444
464
|
oldBlocks.clear()
|
|
445
465
|
newBlocks.clear()
|
|
@@ -459,8 +479,9 @@ function createFineGrainedKeyedList<T>(
|
|
|
459
479
|
// Phase 1: Build new blocks map (reuse or create)
|
|
460
480
|
newItems.forEach((item, index) => {
|
|
461
481
|
const key = keyFn(item, index)
|
|
462
|
-
|
|
482
|
+
// Micro-optimization: single Map.get instead of has+get
|
|
463
483
|
let block = oldBlocks.get(key)
|
|
484
|
+
const existed = block !== undefined
|
|
464
485
|
|
|
465
486
|
if (block) {
|
|
466
487
|
if (block.rawItem !== item) {
|
|
@@ -473,45 +494,30 @@ function createFineGrainedKeyedList<T>(
|
|
|
473
494
|
}
|
|
474
495
|
}
|
|
475
496
|
|
|
476
|
-
// If newBlocks already has this key (duplicate key case), clean up the previous block
|
|
477
|
-
const existingBlock = newBlocks.get(key)
|
|
478
|
-
if (existingBlock && existingBlock !== block) {
|
|
479
|
-
destroyRoot(existingBlock.root)
|
|
480
|
-
removeNodes(existingBlock.nodes)
|
|
481
|
-
}
|
|
482
|
-
|
|
483
497
|
if (block) {
|
|
498
|
+
// Reusing existing block from oldBlocks
|
|
484
499
|
newBlocks.set(key, block)
|
|
485
500
|
oldBlocks.delete(key)
|
|
486
501
|
} else {
|
|
502
|
+
// If newBlocks already has this key (duplicate key case), clean up the previous block
|
|
487
503
|
const existingBlock = newBlocks.get(key)
|
|
488
504
|
if (existingBlock) {
|
|
489
505
|
destroyRoot(existingBlock.root)
|
|
490
506
|
removeNodes(existingBlock.nodes)
|
|
491
507
|
}
|
|
492
|
-
|
|
493
508
|
// Create new block
|
|
494
|
-
block = createKeyedBlock(key, item, index, renderItem, needsIndex)
|
|
509
|
+
block = createKeyedBlock(key, item, index, renderItem, needsIndex, hostRoot)
|
|
510
|
+
createdBlocks.push(block)
|
|
495
511
|
}
|
|
496
512
|
|
|
497
|
-
const resolvedBlock = block
|
|
513
|
+
const resolvedBlock = block
|
|
498
514
|
|
|
499
515
|
newBlocks.set(key, resolvedBlock)
|
|
500
516
|
|
|
517
|
+
// Micro-optimization: single Map.get instead of checking position multiple times
|
|
501
518
|
const position = orderedIndexByKey.get(key)
|
|
502
519
|
if (position !== undefined) {
|
|
503
520
|
appendCandidate = false
|
|
504
|
-
}
|
|
505
|
-
if (appendCandidate) {
|
|
506
|
-
if (index < prevCount) {
|
|
507
|
-
if (!prevOrderedBlocks[index] || prevOrderedBlocks[index]!.key !== key) {
|
|
508
|
-
appendCandidate = false
|
|
509
|
-
}
|
|
510
|
-
} else if (existed) {
|
|
511
|
-
appendCandidate = false
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (position !== undefined) {
|
|
515
521
|
const prior = nextOrderedBlocks[position]
|
|
516
522
|
if (prior && prior !== resolvedBlock) {
|
|
517
523
|
destroyRoot(prior.root)
|
|
@@ -519,6 +525,15 @@ function createFineGrainedKeyedList<T>(
|
|
|
519
525
|
}
|
|
520
526
|
nextOrderedBlocks[position] = resolvedBlock
|
|
521
527
|
} else {
|
|
528
|
+
if (appendCandidate) {
|
|
529
|
+
if (index < prevCount) {
|
|
530
|
+
if (!prevOrderedBlocks[index] || prevOrderedBlocks[index]!.key !== key) {
|
|
531
|
+
appendCandidate = false
|
|
532
|
+
}
|
|
533
|
+
} else if (existed) {
|
|
534
|
+
appendCandidate = false
|
|
535
|
+
}
|
|
536
|
+
}
|
|
522
537
|
orderedIndexByKey.set(key, nextOrderedBlocks.length)
|
|
523
538
|
nextOrderedBlocks.push(resolvedBlock)
|
|
524
539
|
}
|
|
@@ -555,6 +570,11 @@ function createFineGrainedKeyedList<T>(
|
|
|
555
570
|
container.nextBlocks = oldBlocks
|
|
556
571
|
container.orderedBlocks = nextOrderedBlocks
|
|
557
572
|
container.nextOrderedBlocks = prevOrderedBlocks
|
|
573
|
+
for (const block of createdBlocks) {
|
|
574
|
+
if (newBlocks.get(block.key) === block) {
|
|
575
|
+
flushOnMount(block.root)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
558
578
|
return
|
|
559
579
|
}
|
|
560
580
|
|
|
@@ -595,24 +615,95 @@ function createFineGrainedKeyedList<T>(
|
|
|
595
615
|
container.nextBlocks = oldBlocks
|
|
596
616
|
container.orderedBlocks = nextOrderedBlocks
|
|
597
617
|
container.nextOrderedBlocks = prevOrderedBlocks
|
|
618
|
+
for (const block of createdBlocks) {
|
|
619
|
+
if (newBlocks.get(block.key) === block) {
|
|
620
|
+
flushOnMount(block.root)
|
|
621
|
+
}
|
|
622
|
+
}
|
|
598
623
|
})
|
|
599
624
|
}
|
|
600
625
|
|
|
601
|
-
const
|
|
626
|
+
const disconnectObserver = () => {
|
|
627
|
+
connectObserver?.disconnect()
|
|
628
|
+
connectObserver = null
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const ensureEffectStarted = (): boolean => {
|
|
632
|
+
if (disposed || effectStarted) return effectStarted
|
|
633
|
+
const parent = getConnectedParent()
|
|
634
|
+
if (!parent) return false
|
|
635
|
+
const start = () => {
|
|
636
|
+
effectDispose = createRenderEffect(performDiff)
|
|
637
|
+
effectStarted = true
|
|
638
|
+
}
|
|
639
|
+
if (hostRoot) {
|
|
640
|
+
const prev = pushRoot(hostRoot)
|
|
641
|
+
try {
|
|
642
|
+
start()
|
|
643
|
+
} finally {
|
|
644
|
+
popRoot(prev)
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
start()
|
|
648
|
+
}
|
|
649
|
+
return true
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const waitForConnection = () => {
|
|
653
|
+
if (connectObserver || typeof MutationObserver === 'undefined') return
|
|
654
|
+
connectObserver = new MutationObserver(() => {
|
|
655
|
+
if (disposed) return
|
|
656
|
+
if (getConnectedParent()) {
|
|
657
|
+
disconnectObserver()
|
|
658
|
+
if (ensureEffectStarted()) {
|
|
659
|
+
flush()
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
connectObserver.observe(document, { childList: true, subtree: true })
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const scheduleStart = () => {
|
|
667
|
+
if (startScheduled || disposed || effectStarted) return
|
|
668
|
+
startScheduled = true
|
|
669
|
+
const run = () => {
|
|
670
|
+
startScheduled = false
|
|
671
|
+
if (!ensureEffectStarted()) {
|
|
672
|
+
waitForConnection()
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (typeof queueMicrotask === 'function') {
|
|
676
|
+
queueMicrotask(run)
|
|
677
|
+
} else {
|
|
678
|
+
Promise.resolve()
|
|
679
|
+
.then(run)
|
|
680
|
+
.catch(() => undefined)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
scheduleStart()
|
|
602
685
|
|
|
603
686
|
return {
|
|
604
|
-
marker
|
|
687
|
+
get marker() {
|
|
688
|
+
scheduleStart()
|
|
689
|
+
return fragment
|
|
690
|
+
},
|
|
605
691
|
startMarker: container.startMarker,
|
|
606
692
|
endMarker: container.endMarker,
|
|
607
693
|
// Flush pending items - call after markers are inserted into DOM
|
|
608
694
|
flush: () => {
|
|
609
|
-
if (
|
|
610
|
-
|
|
695
|
+
if (disposed) return
|
|
696
|
+
scheduleStart()
|
|
697
|
+
if (ensureEffectStarted()) {
|
|
698
|
+
flush()
|
|
699
|
+
} else {
|
|
700
|
+
waitForConnection()
|
|
611
701
|
}
|
|
612
702
|
},
|
|
613
703
|
dispose: () => {
|
|
614
704
|
disposed = true
|
|
615
705
|
effectDispose?.()
|
|
706
|
+
disconnectObserver()
|
|
616
707
|
container.dispose()
|
|
617
708
|
},
|
|
618
709
|
}
|
package/src/ref.ts
CHANGED
package/src/signal.ts
CHANGED
|
@@ -1057,10 +1057,20 @@ export function endBatch(): void {
|
|
|
1057
1057
|
*/
|
|
1058
1058
|
export function batch<T>(fn: () => T): T {
|
|
1059
1059
|
++batchDepth
|
|
1060
|
+
let _error: unknown
|
|
1061
|
+
let hasError = false
|
|
1060
1062
|
try {
|
|
1061
1063
|
return fn()
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
_error = e
|
|
1066
|
+
hasError = true
|
|
1067
|
+
throw e
|
|
1062
1068
|
} finally {
|
|
1063
|
-
|
|
1069
|
+
--batchDepth
|
|
1070
|
+
// Only flush if no error occurred to avoid interfering with error propagation
|
|
1071
|
+
if (!hasError && batchDepth === 0) {
|
|
1072
|
+
flush()
|
|
1073
|
+
}
|
|
1064
1074
|
}
|
|
1065
1075
|
}
|
|
1066
1076
|
/**
|
|
@@ -1253,7 +1263,7 @@ export function createSelector<T>(
|
|
|
1253
1263
|
let current = source()
|
|
1254
1264
|
const observers = new Map<T, SignalAccessor<boolean>>()
|
|
1255
1265
|
|
|
1256
|
-
effect(() => {
|
|
1266
|
+
const dispose = effect(() => {
|
|
1257
1267
|
const next = source()
|
|
1258
1268
|
if (equalityFn(current, next)) return
|
|
1259
1269
|
|
|
@@ -1265,6 +1275,10 @@ export function createSelector<T>(
|
|
|
1265
1275
|
|
|
1266
1276
|
current = next
|
|
1267
1277
|
})
|
|
1278
|
+
registerRootCleanup(() => {
|
|
1279
|
+
dispose()
|
|
1280
|
+
observers.clear()
|
|
1281
|
+
})
|
|
1268
1282
|
|
|
1269
1283
|
return (key: T) => {
|
|
1270
1284
|
let sig = observers.get(key)
|
package/src/store.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { signal, batch, type SignalAccessor } from './signal'
|
|
|
2
2
|
|
|
3
3
|
const PROXY = Symbol('fict:store-proxy')
|
|
4
4
|
const TARGET = Symbol('fict:store-target')
|
|
5
|
+
const ITERATE_KEY = Symbol('fict:iterate')
|
|
5
6
|
|
|
6
7
|
// ============================================================================
|
|
7
8
|
// Store (Deep Proxy)
|
|
@@ -57,22 +58,43 @@ function wrap<T>(value: T): T {
|
|
|
57
58
|
// Recursively wrap objects
|
|
58
59
|
return wrap(value)
|
|
59
60
|
},
|
|
61
|
+
has(target, prop) {
|
|
62
|
+
const result = Reflect.has(target, prop)
|
|
63
|
+
track(target, prop)
|
|
64
|
+
return result
|
|
65
|
+
},
|
|
66
|
+
ownKeys(target) {
|
|
67
|
+
track(target, ITERATE_KEY)
|
|
68
|
+
return Reflect.ownKeys(target)
|
|
69
|
+
},
|
|
70
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
71
|
+
track(target, prop)
|
|
72
|
+
return Reflect.getOwnPropertyDescriptor(target, prop)
|
|
73
|
+
},
|
|
60
74
|
set(target, prop, value, receiver) {
|
|
61
75
|
if (prop === PROXY || prop === TARGET) return false
|
|
62
76
|
|
|
77
|
+
const hadKey = Object.prototype.hasOwnProperty.call(target, prop)
|
|
63
78
|
const oldValue = Reflect.get(target, prop, receiver)
|
|
64
79
|
if (oldValue === value) return true
|
|
65
80
|
|
|
66
81
|
const result = Reflect.set(target, prop, value, receiver)
|
|
67
82
|
if (result) {
|
|
68
83
|
trigger(target, prop)
|
|
84
|
+
if (!hadKey) {
|
|
85
|
+
trigger(target, ITERATE_KEY)
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
88
|
return result
|
|
71
89
|
},
|
|
72
90
|
deleteProperty(target, prop) {
|
|
91
|
+
const hadKey = Object.prototype.hasOwnProperty.call(target, prop)
|
|
73
92
|
const result = Reflect.deleteProperty(target, prop)
|
|
74
93
|
if (result) {
|
|
75
94
|
trigger(target, prop)
|
|
95
|
+
if (hadKey) {
|
|
96
|
+
trigger(target, ITERATE_KEY)
|
|
97
|
+
}
|
|
76
98
|
}
|
|
77
99
|
return result
|
|
78
100
|
},
|
|
@@ -99,7 +121,9 @@ function track(target: object, prop: string | symbol) {
|
|
|
99
121
|
|
|
100
122
|
let s = signals.get(prop)
|
|
101
123
|
if (!s) {
|
|
102
|
-
|
|
124
|
+
const initial =
|
|
125
|
+
prop === ITERATE_KEY ? (Reflect.ownKeys(target).length as number) : getLastValue(target, prop)
|
|
126
|
+
s = signal(initial)
|
|
103
127
|
signals.set(prop, s)
|
|
104
128
|
}
|
|
105
129
|
s() // subscribe
|
|
@@ -110,7 +134,11 @@ function trigger(target: object, prop: string | symbol) {
|
|
|
110
134
|
if (signals) {
|
|
111
135
|
const s = signals.get(prop)
|
|
112
136
|
if (s) {
|
|
113
|
-
|
|
137
|
+
if (prop === ITERATE_KEY) {
|
|
138
|
+
s(Reflect.ownKeys(target).length)
|
|
139
|
+
} else {
|
|
140
|
+
s(getLastValue(target, prop)) // notify with new value
|
|
141
|
+
}
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
}
|
package/src/types.ts
CHANGED
|
@@ -128,15 +128,15 @@ export interface DOMEventHandlers {
|
|
|
128
128
|
// ============================================================================
|
|
129
129
|
|
|
130
130
|
/** Ref callback type */
|
|
131
|
-
export type RefCallback<T extends
|
|
131
|
+
export type RefCallback<T extends Element = HTMLElement> = (element: T) => void
|
|
132
132
|
|
|
133
133
|
/** Ref object type (for future use with createRef) */
|
|
134
|
-
export interface RefObject<T extends
|
|
134
|
+
export interface RefObject<T extends Element = HTMLElement> {
|
|
135
135
|
current: T | null
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/** Ref type that can be either callback or object */
|
|
139
|
-
export type Ref<T extends
|
|
139
|
+
export type Ref<T extends Element = HTMLElement> = RefCallback<T> | RefObject<T>
|
|
140
140
|
|
|
141
141
|
// ============================================================================
|
|
142
142
|
// Style Types
|