@@ -43,9 +43,15 @@ why that was removed in favor of this:
4343 want to clear some state and not other state. You might want to preserve some elements of a
4444 sibling's state. Embedding it in the renderer would force an opinion on you, and in order to
4545 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).
4650*/
4751
48- import { checkCallback } from "../util.js"
52+ import m from "../core.js"
53+
54+ import { checkCallback , noop } from "../util.js"
4955
5056/**
5157 * @template K, V
@@ -55,6 +61,7 @@ import {checkCallback} from "../util.js"
5561 * @property {V } value
5662 * @property {AbortSignal } signal
5763 * @property {() => void } release
64+ * @property {() => void } remove
5865 */
5966
6067/**
@@ -70,47 +77,64 @@ import {checkCallback} from "../util.js"
7077 * @property {(key: K) => boolean } delete
7178 */
7279
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 ) => {
8081 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 ( )
8385 /** @type {Set<TrackedHandle<K, V>> } */ var live = new Set ( )
8486
87+ /** @param {null | AbortController & TrackedHandle<K, V> } prev */
8588 var abort = ( prev ) => {
8689 try {
8790 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+ }
9096 }
9197 } catch ( e ) {
9298 console . error ( e )
9399 }
94100 }
95101
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+ */
98118 var setHandle = ( k , v , bits ) => {
99119 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 )
114138 }
115139 state . set ( k , handle )
116140 live . add ( handle )
@@ -121,6 +145,18 @@ var tracked = (redraw, initial) => {
121145 if ( bits & 2 ) redraw ( )
122146 }
123147
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+
124160 for ( var [ k , v ] of initial || [ ] ) setHandle ( k , v , 1 )
125161
126162 return {
@@ -130,14 +166,22 @@ var tracked = (redraw, initial) => {
130166 get : ( k ) => ( k = state . get ( k ) ) && k . value ,
131167 set : ( k , v ) => setHandle ( k , v , 3 ) ,
132168 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 ]
140184 }
141185}
142186
143- export { tracked as default }
187+ export { tracked , trackedList }
0 commit comments