@@ -3,17 +3,18 @@ import { computed, isReactive, reactive } from "vue";
33
44export type unObject = Record < string , unknown > ;
55
6- const configurable = true ,
7- /**
8- * Creates an array of node objects with parent and siblings
9- *
10- * @param {unObject[] } siblings - Array of sibling nodes
11- * @param {unObject } [parent] - Parent node
12- * @returns {{ node: unObject; parent: unObject; siblings: unObject }[] }
13- * Array of objects containing node, parent, and siblings
14- */
15- getItems = ( siblings : unObject [ ] , parent ?: unObject ) =>
16- [ ...siblings ] . reverse ( ) . map ( ( node ) => ( { node, parent, siblings } ) ) ;
6+ const configurable = true ;
7+
8+ /**
9+ * Creates an array of node objects with parent and siblings
10+ *
11+ * @param {unObject[] } siblings - Array of sibling nodes
12+ * @param {unObject } [parent] - Parent node
13+ * @returns {{ node: unObject; parent: unObject; siblings: unObject }[] } Array
14+ * of objects containing node, parent, and siblings
15+ */
16+ const getItems = ( siblings : unObject [ ] , parent ?: unObject ) =>
17+ [ ...siblings ] . reverse ( ) . map ( ( node : unObject ) => ( { node, parent, siblings } ) ) ;
1718
1819/**
1920 * Creates a flat representation of a JSON tree with helper functions to
@@ -70,9 +71,14 @@ export default (
7071 * @returns {number } Index of the node in its siblings array
7172 */
7273 get ( this : unObject ) : number {
73- return ( this [ keySiblings ] as unObject [ ] ) . findIndex (
74- ( sibling ) => this [ keyId ] === sibling [ keyId ] ,
75- ) ;
74+ /**
75+ * Checks if the given sibling is the same as the current node
76+ *
77+ * @param {unObject } sibling - The sibling to compare
78+ * @returns {boolean } True if the sibling matches the current node
79+ */
80+ const isSibling = ( sibling : unObject ) => this [ keyId ] === sibling [ keyId ] ;
81+ return ( this [ keySiblings ] as unObject [ ] ) . findIndex ( isSibling ) ;
7682 } ,
7783 } ,
7884 [ keyNext ] : {
@@ -101,18 +107,25 @@ export default (
101107 } ,
102108 } ,
103109 } ;
110+
104111 /**
105112 * Generator function that traverses the tree and yields each node
106113 *
107114 * @param {unObject[] } nodes - Array of nodes to traverse
108115 * @yields {unObject} Each node in the tree
109- * @returns {Generator<unObject> } Generator that yields nodes
110116 */
111- const getNodes = function * ( nodes : unObject [ ] ) {
117+ const genNodes = function * ( nodes : unObject [ ] ) {
112118 const stack = getItems ( nodes ) ;
113119 while ( stack . length ) {
114120 const { node, parent, siblings } = stack . pop ( ) ?? { } ;
115121 if ( node ) {
122+ /**
123+ * Checks if a property key is not already defined in the node
124+ *
125+ * @param {string } key - The property key to check
126+ * @returns {boolean } True if the key is not in the node
127+ */
128+ const isNotInNode = ( key : string ) => ! ( key in node ) ;
116129 if ( node [ keyParent ] !== parent )
117130 Object . defineProperty ( node , keyParent , {
118131 configurable,
@@ -123,7 +136,7 @@ export default (
123136 configurable,
124137 value : siblings ,
125138 } ) ;
126- if ( Object . keys ( properties ) . some ( ( key ) => ! ( key in node ) ) )
139+ if ( Object . keys ( properties ) . some ( isNotInNode ) )
127140 Object . defineProperties ( node , properties ) ;
128141 yield node ;
129142 stack . push (
@@ -132,93 +145,111 @@ export default (
132145 }
133146 }
134147 } ,
135- nodes = computed ( ( ) => [
136- ...getNodes ( isReactive ( tree ) ? tree : reactive ( tree ) ) ,
137- ] ) ,
138- nodesMap = computed ( ( ) =>
139- Object . fromEntries (
140- nodes . value . map ( ( node ) => [ node [ keyId ] as string , node ] ) ,
141- ) ,
142- ) ,
143148 /**
144- * Function to run actions on nodes
149+ * Generates a flat array of nodes from the tree structure
145150 *
146- * @param {string } pId - ID of the node to perform action on
147- * @param {string } action - Action to perform (add, addChild, remove, up,
148- * down, left, right)
149- * @returns {string | undefined } ID of the affected node or undefined
151+ * @returns {unObject[] } Array of nodes in the tree
150152 */
151- run = ( pId : string , action : string ) => {
152- const the = nodesMap . value [ pId ] ;
153- if ( the ) {
154- const [ root ] = nodes . value ,
155- index = the [ keyIndex ] as number ,
156- next = the [ keyNext ] as undefined | unObject ,
157- nextIndex = index + 1 ,
158- parent = the [ keyParent ] as undefined | unObject ,
159- prev = the [ keyPrev ] as undefined | unObject ,
160- prevIndex = index - 1 ,
161- siblings = the [ keySiblings ] as unObject [ ] ;
162- switch ( action ) {
163- case "add" : {
164- const id = uid ( ) ;
165- siblings . splice ( nextIndex , 0 , { [ keyId ] : id } ) ;
166- return id ;
167- }
168- case "addChild" : {
169- const id = uid ( ) ;
170- if ( ! Array . isArray ( the [ keyChildren ] ) ) the [ keyChildren ] = [ ] ;
171- ( the [ keyChildren ] as unObject [ ] ) . unshift ( { [ keyId ] : id } ) ;
172- return id ;
153+ getNodes = ( ) => [ ...genNodes ( isReactive ( tree ) ? tree : reactive ( tree ) ) ] ;
154+
155+ const nodes = computed ( getNodes ) ;
156+
157+ /**
158+ * Gets the entry (key-value pair) for a node using its ID
159+ *
160+ * @param {unObject } node - The node to create an entry for
161+ * @returns {[string, unObject] } A tuple with the node ID and the node object
162+ */
163+ const getNodeEntry = ( node : unObject ) => [ node [ keyId ] as string , node ] ,
164+ /**
165+ * Creates a map of nodes with node IDs as keys
166+ *
167+ * @returns {Record<string, unObject> } Object mapping node IDs to node
168+ * objects
169+ */
170+ getNodesMap = ( ) => Object . fromEntries ( nodes . value . map ( getNodeEntry ) ) ;
171+
172+ const nodesMap = computed ( getNodesMap ) ;
173+
174+ /**
175+ * Function to run actions on nodes
176+ *
177+ * @param {string } pId - ID of the node to perform action on
178+ * @param {string } action - Action to perform (add, addChild, remove, up,
179+ * down, left, right)
180+ * @returns {string | undefined } ID of the affected node or undefined
181+ */
182+ const run = ( pId : string , action : string ) => {
183+ const the = nodesMap . value [ pId ] ;
184+ if ( the ) {
185+ const [ root ] = nodes . value ,
186+ index = the [ keyIndex ] as number ,
187+ next = the [ keyNext ] as undefined | unObject ,
188+ nextIndex = index + 1 ,
189+ parent = the [ keyParent ] as undefined | unObject ,
190+ prev = the [ keyPrev ] as undefined | unObject ,
191+ prevIndex = index - 1 ,
192+ siblings = the [ keySiblings ] as unObject [ ] ;
193+ switch ( action ) {
194+ case "add" : {
195+ const id = uid ( ) ;
196+ siblings . splice ( nextIndex , 0 , { [ keyId ] : id } ) ;
197+ return id ;
198+ }
199+ case "addChild" : {
200+ const id = uid ( ) ;
201+ if ( ! Array . isArray ( the [ keyChildren ] ) ) the [ keyChildren ] = [ ] ;
202+ ( the [ keyChildren ] as unObject [ ] ) . unshift ( { [ keyId ] : id } ) ;
203+ return id ;
204+ }
205+ case "down" :
206+ if (
207+ index < siblings . length - 1 &&
208+ siblings [ index ] &&
209+ siblings [ nextIndex ]
210+ )
211+ [ siblings [ index ] , siblings [ nextIndex ] ] = [
212+ siblings [ nextIndex ] ,
213+ siblings [ index ] ,
214+ ] ;
215+ break ;
216+ case "left" :
217+ if ( parent ?. [ keyParent ] ) {
218+ ( parent [ keySiblings ] as unObject [ ] ) . splice (
219+ ( parent [ keyIndex ] as number ) + 1 ,
220+ 0 ,
221+ ...siblings . splice ( index , 1 ) ,
222+ ) ;
223+ return parent [ keyId ] as string ;
173224 }
174- case "down" :
175- if (
176- index < siblings . length - 1 &&
177- siblings [ index ] &&
178- siblings [ nextIndex ]
179- )
180- [ siblings [ index ] , siblings [ nextIndex ] ] = [
181- siblings [ nextIndex ] ,
182- siblings [ index ] ,
183- ] ;
184- break ;
185- case "left" :
186- if ( parent ?. [ keyParent ] ) {
187- ( parent [ keySiblings ] as unObject [ ] ) . splice (
188- ( parent [ keyIndex ] as number ) + 1 ,
189- 0 ,
190- ...siblings . splice ( index , 1 ) ,
191- ) ;
192- return parent [ keyId ] as string ;
193- }
194- break ;
195- case "remove" : {
196- const id = ( next ?. [ keyId ] ??
197- prev ?. [ keyId ] ??
198- parent ?. [ keyId ] ??
199- root ?. [ keyId ] ) as string | undefined ;
200- siblings . splice ( index , 1 ) ;
225+ break ;
226+ case "remove" : {
227+ const id = ( next ?. [ keyId ] ??
228+ prev ?. [ keyId ] ??
229+ parent ?. [ keyId ] ??
230+ root ?. [ keyId ] ) as string | undefined ;
231+ siblings . splice ( index , 1 ) ;
232+ return id ;
233+ }
234+ case "right" :
235+ if ( prev ) {
236+ const children = ( prev [ keyChildren ] ?? [ ] ) as unObject [ ] ,
237+ id = prev [ keyId ] as string ;
238+ prev [ keyChildren ] = [ ...children , ...siblings . splice ( index , 1 ) ] ;
201239 return id ;
202240 }
203- case "right" :
204- if ( prev ) {
205- const children = ( prev [ keyChildren ] ?? [ ] ) as unObject [ ] ,
206- id = prev [ keyId ] as string ;
207- prev [ keyChildren ] = [ ...children , ...siblings . splice ( index , 1 ) ] ;
208- return id ;
209- }
210- break ;
211- case "up" :
212- if ( index && siblings [ index ] && siblings [ prevIndex ] )
213- [ siblings [ prevIndex ] , siblings [ index ] ] = [
214- siblings [ index ] ,
215- siblings [ prevIndex ] ,
216- ] ;
217- break ;
218- }
241+ break ;
242+ case "up" :
243+ if ( index && siblings [ index ] && siblings [ prevIndex ] )
244+ [ siblings [ prevIndex ] , siblings [ index ] ] = [
245+ siblings [ index ] ,
246+ siblings [ prevIndex ] ,
247+ ] ;
248+ break ;
219249 }
220- return ;
221- } ;
250+ }
251+ return ;
252+ } ;
222253
223254 return {
224255 /**
0 commit comments