1- import { toReactive } from "@vueuse/core" ;
21import { computed , isReactive , reactive } from "vue" ;
32
3+ /* -------------------------------------------------------------------------- */
4+ /* Тип произвольного объекта */
5+ /* -------------------------------------------------------------------------- */
6+
7+ export type unObject = Record < string , unknown > ;
8+
9+ /* -------------------------------------------------------------------------- */
10+ /* Композабл для работы с древовидным объектом */
11+ /* -------------------------------------------------------------------------- */
12+
413export default (
5- tree : Record < string , unknown > [ ] ,
14+ tree : unObject [ ] ,
615 {
716 branch : keyBranch = "branch" ,
817 children : keyChildren = "children" ,
@@ -14,186 +23,165 @@ export default (
1423 siblings : keySiblings = "siblings" ,
1524 } = { } ,
1625) => {
26+ /* -------------------------------------------------------------------------- */
27+ /* Расчетные свойства для работы с древовидным объектом */
28+ /* -------------------------------------------------------------------------- */
29+
1730 const properties : PropertyDescriptorMap = {
1831 [ keyBranch ] : {
19- get ( this : Record < string , unknown > ) {
32+ get ( this : unObject ) {
2033 const ret = [ this ] ;
21- while ( ret [ 0 ] ?. [ keyParent ] )
22- ret . unshift ( ret [ 0 ] [ keyParent ] as Record < string , unknown > ) ;
34+ while ( ret [ 0 ] ?. [ keyParent ] ) ret . unshift ( ret [ 0 ] [ keyParent ] as unObject ) ;
2335 return ret ;
2436 } ,
2537 } ,
2638 [ keyIndex ] : {
27- get ( this : Record < string , unknown > ) {
28- return ( this [ keySiblings ] as Record < string , unknown > [ ] ) . findIndex (
39+ get ( this : unObject ) {
40+ return ( this [ keySiblings ] as unObject [ ] ) . findIndex (
2941 ( sibling ) => this [ keyId ] === sibling [ keyId ] ,
3042 ) ;
3143 } ,
3244 } ,
3345 [ keyNext ] : {
34- get ( this : Record < string , unknown > ) {
35- return ( this [ keySiblings ] as Record < string , unknown > [ ] ) [
46+ get ( this : unObject ) {
47+ return ( this [ keySiblings ] as unObject [ ] ) [
3648 ( this [ keyIndex ] as number ) + 1
3749 ] ;
3850 } ,
3951 } ,
4052 [ keyPrev ] : {
41- get ( this : Record < string , unknown > ) {
42- return ( this [ keySiblings ] as Record < string , unknown > [ ] ) [
53+ get ( this : unObject ) {
54+ return ( this [ keySiblings ] as unObject [ ] ) [
4355 ( this [ keyIndex ] as number ) - 1
4456 ] ;
4557 } ,
4658 } ,
4759 } ;
48- const getLeaves = (
49- siblings : { configurable ?: boolean ; value : Record < string , unknown > [ ] } ,
50- parent = { } ,
51- ) =>
52- siblings . value . flatMap ( ( value ) : Record < string , unknown > [ ] => {
53- Object . defineProperties ( value , {
54- ...properties ,
55- [ keyParent ] : parent ,
56- [ keySiblings ] : siblings ,
57- } ) ;
58- return [
59- value ,
60- ...getLeaves (
61- {
62- configurable : true ,
63- value : ( value [ keyChildren ] ?? [ ] ) as Record < string , unknown > [ ] ,
64- } ,
65- { configurable : true , value } ,
66- ) ,
67- ] ;
68- } ) ,
69- leaves = computed ( ( ) =>
70- getLeaves ( { value : isReactive ( tree ) ? tree : reactive ( tree ) } ) ,
71- ) ,
72- objLeaves = toReactive (
73- computed ( ( ) =>
74- Object . fromEntries (
75- leaves . value . map ( ( leaf ) => [ leaf [ keyId ] as string , leaf ] ) ,
76- ) ,
77- ) ,
78- ) ;
79- return {
80- add : ( pId : string ) => {
81- const the = objLeaves [ pId ] ;
82- if ( the ) {
83- const children = the [ keyChildren ] as
84- | Record < string , unknown > [ ]
85- | undefined ,
86- url = URL . createObjectURL ( new Blob ( ) ) ,
87- id = url . split ( "/" ) . pop ( ) ?? crypto . randomUUID ( ) ,
88- index = the [ keyIndex ] as number ,
89- siblings = the [ keySiblings ] as Record < string , unknown > [ ] ;
90- URL . revokeObjectURL ( url ) ;
91- switch ( true ) {
92- case ! ! the [ keyParent ] :
93- siblings . splice ( index + 1 , 0 , { [ keyId ] : id } ) ;
94- break ;
95- case ! ! children :
96- children . unshift ( { [ keyId ] : id } ) ;
97- break ;
98- default :
99- siblings . splice ( index + 1 , 0 , { [ keyId ] : id } ) ;
100- break ;
101- }
102- return id ;
103- }
104- return undefined ;
105- } ,
106- arrLeaves : toReactive ( leaves ) ,
107- down : ( pId : string ) => {
108- const the = objLeaves [ pId ] ;
109- if ( the ) {
110- const index = the [ keyIndex ] as number ,
111- nextIndex = index + 1 ,
112- siblings = the [ keySiblings ] as Record < string , unknown > [ ] ;
113- if (
114- index < siblings . length - 1 &&
115- siblings [ index ] &&
116- siblings [ nextIndex ]
117- )
118- [ siblings [ index ] , siblings [ nextIndex ] ] = [
119- siblings [ nextIndex ] ,
120- siblings [ index ] ,
121- ] ;
122- }
123- } ,
124- leaves,
125- left : ( pId : string ) => {
126- const the = objLeaves [ pId ] ;
127- if ( the ) {
128- const parent = the [ keyParent ] as Record < string , unknown > | undefined ;
129- if ( parent ?. [ keyParent ] ) {
130- const children = ( parent [ keyChildren ] ?? [ ] ) as Record <
131- string ,
132- unknown
133- > [ ] ,
134- siblings = parent [ keySiblings ] as Record < string , unknown > [ ] ;
135- siblings . splice (
136- ( parent [ keyIndex ] as number ) + 1 ,
137- 0 ,
138- ...children . splice ( the [ keyIndex ] as number , 1 ) ,
60+
61+ /* -------------------------------------------------------------------------- */
62+ /* Формирование массива элементов дерева простого и ассоциативного */
63+ /* -------------------------------------------------------------------------- */
64+
65+ const getItems = ( nodes : unObject [ ] , node ?: unObject ) =>
66+ nodes . toReversed ( ) . map ( ( child ) => ( {
67+ node : child ,
68+ parent : { configurable : true , value : node } ,
69+ siblings : { configurable : true , value : nodes } ,
70+ } ) ) ,
71+ getNodes = function * ( nodes : unObject [ ] ) {
72+ const stack = getItems ( nodes ) ;
73+ while ( stack . length > 0 ) {
74+ const { node, parent, siblings } = stack . pop ( ) ?? { } ;
75+ if ( node && parent && siblings ) {
76+ Object . defineProperties ( node , {
77+ ...properties ,
78+ [ keyParent ] : parent ,
79+ [ keySiblings ] : siblings ,
80+ } ) ;
81+ yield node ;
82+ stack . push (
83+ ...getItems ( ( node [ keyChildren ] ?? [ ] ) as unObject [ ] , node ) ,
13984 ) ;
140- return parent [ keyId ] as string ;
141- }
142- }
143- return undefined ;
144- } ,
145- objLeaves,
146- remove : ( pId : string ) => {
147- const the = objLeaves [ pId ] ;
148- if ( the ) {
149- const parent = the [ keyParent ] as Record < string , unknown > | undefined ;
150- if ( parent ) {
151- const [ root ] = leaves . value ,
152- next = the [ keyNext ] as Record < string , unknown > | undefined ,
153- prev = the [ keyPrev ] as Record < string , unknown > | undefined ,
154- id = ( next ?. [ keyId ] ??
155- prev ?. [ keyId ] ??
156- parent [ keyId ] ??
157- root ?. [ keyId ] ) as string ,
158- siblings = the [ keySiblings ] as Record < string , unknown > [ ] ;
159- siblings . splice ( the [ keyIndex ] as number , 1 ) ;
160- return id ;
16185 }
16286 }
163- return undefined ;
16487 } ,
165- right : ( pId : string ) => {
166- const the = objLeaves [ pId ] ;
167- if ( the ) {
168- const prev = the [ keyPrev ] as Record < string , unknown > | undefined ;
169- if ( prev ) {
170- const children = ( prev [ keyChildren ] ?? [ ] ) as Record <
171- string ,
172- unknown
173- > [ ] ,
174- id = prev [ keyId ] as string ,
175- siblings = the [ keySiblings ] as Record < string , unknown > [ ] ;
176- prev [ keyChildren ] = [
177- ...children ,
178- ...siblings . splice ( the [ keyIndex ] as number , 1 ) ,
179- ] ;
88+ nodes = computed ( ( ) => [
89+ ...getNodes ( isReactive ( tree ) ? tree : reactive ( tree ) ) ,
90+ ] ) ,
91+ nodesMap = computed ( ( ) =>
92+ Object . fromEntries (
93+ nodes . value . map ( ( node ) => [ node [ keyId ] as string , node ] ) ,
94+ ) ,
95+ ) ;
96+
97+ /* -------------------------------------------------------------------------- */
98+ /* Служебная функция для выполнения действия над элементом дерева */
99+ /* -------------------------------------------------------------------------- */
100+
101+ const run = ( pId : string , action : string ) => {
102+ const the = nodesMap . value [ pId ] ;
103+ if ( the ) {
104+ const [ root ] = nodes . value ,
105+ children = the [ keyChildren ] as undefined | unObject [ ] ,
106+ index = the [ keyIndex ] as number ,
107+ next = the [ keyNext ] as undefined | unObject ,
108+ nextIndex = index + 1 ,
109+ parent = the [ keyParent ] as undefined | unObject ,
110+ prev = the [ keyPrev ] as undefined | unObject ,
111+ prevIndex = index - 1 ,
112+ siblings = the [ keySiblings ] as unObject [ ] ;
113+ switch ( action ) {
114+ case "add" : {
115+ const url = URL . createObjectURL ( new Blob ( ) ) ,
116+ id = url . split ( "/" ) . pop ( ) ;
117+ URL . revokeObjectURL ( url ) ;
118+ if ( parent && Array . isArray ( children ) )
119+ children . unshift ( { [ keyId ] : id } ) ;
120+ else siblings . splice ( index + 1 , 0 , { [ keyId ] : id } ) ;
180121 return id ;
181122 }
123+ case "down" :
124+ if (
125+ index < siblings . length - 1 &&
126+ siblings [ index ] &&
127+ siblings [ nextIndex ]
128+ )
129+ [ siblings [ index ] , siblings [ nextIndex ] ] = [
130+ siblings [ nextIndex ] ,
131+ siblings [ index ] ,
132+ ] ;
133+ break ;
134+ case "left" :
135+ if ( parent ?. [ keyParent ] ) {
136+ ( parent [ keySiblings ] as unObject [ ] ) . splice (
137+ ( parent [ keyIndex ] as number ) + 1 ,
138+ 0 ,
139+ ...siblings . splice ( index , 1 ) ,
140+ ) ;
141+ return parent [ keyId ] as string ;
142+ }
143+ break ;
144+ case "remove" :
145+ if ( parent ) {
146+ const id = ( next ?. [ keyId ] ??
147+ prev ?. [ keyId ] ??
148+ parent [ keyId ] ??
149+ root ?. [ keyId ] ) as string | undefined ;
150+ siblings . splice ( index , 1 ) ;
151+ return id ;
152+ }
153+ break ;
154+ case "right" :
155+ if ( prev ) {
156+ const children = ( prev [ keyChildren ] ?? [ ] ) as unObject [ ] ,
157+ id = prev [ keyId ] as string ;
158+ prev [ keyChildren ] = [ ...children , ...siblings . splice ( index , 1 ) ] ;
159+ return id ;
160+ }
161+ break ;
162+ case "up" :
163+ if ( index && siblings [ index ] && siblings [ prevIndex ] )
164+ [ siblings [ prevIndex ] , siblings [ index ] ] = [
165+ siblings [ index ] ,
166+ siblings [ prevIndex ] ,
167+ ] ;
168+ break ;
182169 }
183- return undefined ;
184- } ,
185- up : ( pId : string ) => {
186- const the = objLeaves [ pId ] ;
187- if ( the ) {
188- const index = the [ keyIndex ] as number ,
189- prevIndex = index - 1 ,
190- siblings = the [ keySiblings ] as Record < string , unknown > [ ] ;
191- if ( index && siblings [ index ] && siblings [ prevIndex ] )
192- [ siblings [ prevIndex ] , siblings [ index ] ] = [
193- siblings [ index ] ,
194- siblings [ prevIndex ] ,
195- ] ;
196- }
197- } ,
170+ }
171+ } ;
172+
173+ /* -------------------------------------------------------------------------- */
174+ /* Формирование возвращаемого объекта композабл функции */
175+ /* -------------------------------------------------------------------------- */
176+
177+ return {
178+ add : ( pId : string ) => run ( pId , "add" ) ,
179+ down : ( pId : string ) => run ( pId , "down" ) ,
180+ left : ( pId : string ) => run ( pId , "left" ) ,
181+ nodes,
182+ nodesMap,
183+ remove : ( pId : string ) => run ( pId , "remove" ) ,
184+ right : ( pId : string ) => run ( pId , "right" ) ,
185+ up : ( pId : string ) => run ( pId , "up" ) ,
198186 } ;
199187} ;
0 commit comments