@@ -43,9 +43,15 @@ why that was removed in favor of this:
43
43
want to clear some state and not other state. You might want to preserve some elements of a
44
44
sibling's state. Embedding it in the renderer would force an opinion on you, and in order to
45
45
work around it, you'd have to do something like this anyways.
46
+
47
+ As for the difference between `m.trackedList()` and `m.tracked()`, the first is for tracking lists
48
+ (and is explained above), and `m.tracked()` is for single values (but uses `m.trackedList()`
49
+ internally to avoid a ton of code duplication).
46
50
*/
47
51
48
- import { checkCallback } from "../util.js"
52
+ import m from "../core.js"
53
+
54
+ import { checkCallback , noop } from "../util.js"
49
55
50
56
/**
51
57
* @template K, V
@@ -55,6 +61,7 @@ import {checkCallback} from "../util.js"
55
61
* @property {V } value
56
62
* @property {AbortSignal } signal
57
63
* @property {() => void } release
64
+ * @property {() => void } remove
58
65
*/
59
66
60
67
/**
@@ -70,47 +77,64 @@ import {checkCallback} from "../util.js"
70
77
* @property {(key: K) => boolean } delete
71
78
*/
72
79
73
- /**
74
- * @template K, V
75
- * @param {Iterable<[K, V]> } [initial]
76
- * @param {() => void } redraw
77
- * @returns {Tracked<K, V> }
78
- */
79
- var tracked = ( redraw , initial ) => {
80
+ var trackedState = ( redraw ) => {
80
81
checkCallback ( redraw , false , "redraw" )
81
-
82
- /** @type {Map<K, TrackedHandle<K, V> & {_: AbortController}> } */ var state = new Map ( )
82
+ /** @type {Map<K, AbortController & TrackedHandle<K, V>> } */
83
+ var state = new Map ( )
84
+ var removed = new WeakSet ( )
83
85
/** @type {Set<TrackedHandle<K, V>> } */ var live = new Set ( )
84
86
87
+ /** @param {null | AbortController & TrackedHandle<K, V> } prev */
85
88
var abort = ( prev ) => {
86
89
try {
87
90
if ( prev ) {
88
- if ( prev . _ ) prev . _ . abort ( )
89
- else live . delete ( prev )
91
+ if ( removed . has ( prev ) ) {
92
+ live . delete ( prev )
93
+ } else {
94
+ prev . abort ( )
95
+ }
90
96
}
91
97
} catch ( e ) {
92
98
console . error ( e )
93
99
}
94
100
}
95
101
96
- // Bit 1 forcibly releases the old handle, and bit 2 causes an update notification to be sent
97
- // (something that's unwanted during initialization).
102
+ /** @param {K } k */
103
+ var remove = ( k , r ) => {
104
+ var prev = state . get ( k )
105
+ var result = state . delete ( k )
106
+ abort ( prev )
107
+ if ( r ) redraw ( )
108
+ return result
109
+ }
110
+
111
+ /**
112
+ * @param {K } k
113
+ * @param {V } v
114
+ * @param {number } bits
115
+ * Bit 1 forcibly releases the old handle, and bit 2 causes an update notification to be sent
116
+ * (something that's unwanted during initialization).
117
+ */
98
118
var setHandle = ( k , v , bits ) => {
99
119
var prev = state . get ( k )
100
- var ctrl = new AbortController ( )
101
- /** @type {TrackedHandle<K, V> } */
102
- var handle = {
103
- _ : ctrl ,
104
- key : k ,
105
- value : v ,
106
- signal : ctrl . signal ,
107
- release ( ) {
108
- if ( state . get ( handle . key ) === handle ) {
109
- handle . _ = null
110
- } else if ( live . delete ( handle ) ) {
111
- redraw ( )
112
- }
113
- } ,
120
+ // Note: it extending `AbortController` is an implementation detail. It exposing a `signal`
121
+ // property is *not*.
122
+ var handle = /** @type {AbortController & TrackedHandle<K, V> } */ ( new AbortController ( ) )
123
+ handle . key = k
124
+ handle . value = v
125
+ handle . release = ( ev ) => {
126
+ if ( ev ) m . capture ( ev )
127
+ if ( ! handle ) return
128
+ if ( state . get ( handle . key ) === handle ) {
129
+ removed . add ( handle )
130
+ handle = null
131
+ } else if ( live . delete ( handle ) ) {
132
+ redraw ( )
133
+ }
134
+ }
135
+ handle . remove = ( ev ) => {
136
+ if ( ev ) m . capture ( ev )
137
+ remove ( handle . key , 0 )
114
138
}
115
139
state . set ( k , handle )
116
140
live . add ( handle )
@@ -121,6 +145,18 @@ var tracked = (redraw, initial) => {
121
145
if ( bits & 2 ) redraw ( )
122
146
}
123
147
148
+ return { s : state , l : live , h : setHandle , r : remove }
149
+ }
150
+
151
+ /**
152
+ * @template K, V
153
+ * @param {Iterable<[K, V]> } [initial]
154
+ * @param {() => void } redraw
155
+ * @returns {TrackedList<K, V> }
156
+ */
157
+ var trackedList = ( redraw , initial ) => {
158
+ var { s : state , l : live , h : setHandle , r : remove } = trackedState ( redraw )
159
+
124
160
for ( var [ k , v ] of initial || [ ] ) setHandle ( k , v , 1 )
125
161
126
162
return {
@@ -130,14 +166,22 @@ var tracked = (redraw, initial) => {
130
166
get : ( k ) => ( k = state . get ( k ) ) && k . value ,
131
167
set : ( k , v ) => setHandle ( k , v , 3 ) ,
132
168
replace : ( k , v ) => setHandle ( k , v , 2 ) ,
133
- delete ( k ) {
134
- var prev = state . get ( k )
135
- var result = state . delete ( k )
136
- abort ( prev )
137
- redraw ( )
138
- return result
139
- } ,
169
+ delete : ( k ) => remove ( k , 1 ) ,
170
+ forget : ( k ) => ( k = state . get ( k ) ) && k . release ( ) ,
171
+ }
172
+ }
173
+
174
+ var tracked = ( redraw ) => {
175
+ var { l : live , h : setHandle , r : remove } = trackedState ( redraw )
176
+ var initial = noop
177
+ var id = - 1
178
+ return ( state ) => {
179
+ if ( ! Object . is ( initial , initial = state ) ) {
180
+ remove ( id ++ , 0 )
181
+ setHandle ( id , state , 1 )
182
+ }
183
+ return [ ...live ]
140
184
}
141
185
}
142
186
143
- export { tracked as default }
187
+ export { tracked , trackedList }
0 commit comments