cedro 0.1.0

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.
@@ -0,0 +1,832 @@
1
+ import { IWidget, WUIEvent, WUICallback } from "../interfaces/widget.interface";
2
+ import { Vector2D } from "../types/vector2d.type";
3
+
4
+ export enum WidgetTypes {
5
+ FILL = 1,
6
+ CUSTOM = 2,
7
+ FREE = 3,
8
+ }
9
+
10
+ export enum WidgetAlignTypes {
11
+ HORIZONTAL = 1,
12
+ VERTICAL = 2,
13
+ }
14
+
15
+ export class Widget implements IWidget {
16
+ readonly id: string;
17
+
18
+ subscribers: Map<string, WUICallback>;
19
+
20
+ padding: number;
21
+
22
+ left: number;
23
+ top: number;
24
+ width: number;
25
+ height: number;
26
+
27
+ initialWidth: number;
28
+ initialHeight: number;
29
+
30
+ overflow: boolean;
31
+
32
+ fixedSize: number | null;
33
+
34
+ type: WidgetTypes;
35
+ align: WidgetAlignTypes;
36
+
37
+ visible: boolean;
38
+ enabled: boolean;
39
+
40
+ parent: IWidget | null;
41
+ childs: IWidget[];
42
+ bodyTagName: string;
43
+ body: HTMLElement;
44
+
45
+ constructor(
46
+ id: string,
47
+ bodyTagName: string = "div",
48
+ parent: IWidget | null = null
49
+ ) {
50
+ this.id = id;
51
+
52
+ this.overflow = false;
53
+
54
+ this.subscribers = new Map<string, WUICallback>();
55
+
56
+ this.padding = 0;
57
+
58
+ this.left = 0;
59
+ this.top = 0;
60
+ this.width = 0;
61
+ this.height = 0;
62
+ this.initialWidth = 0;
63
+ this.initialHeight = 0;
64
+ this.fixedSize = null;
65
+
66
+ this.type = WidgetTypes.FREE;
67
+ this.align = WidgetAlignTypes.VERTICAL;
68
+
69
+ this.visible = true;
70
+ this.enabled = true;
71
+
72
+ this.parent = parent;
73
+ this.childs = [];
74
+
75
+ this.bodyTagName = bodyTagName;
76
+ this.body = document.createElement(this.bodyTagName);
77
+ this.body.id = `WUI.${id}.body`;
78
+
79
+ if (parent) {
80
+ //this.setType(WidgetTypes.CUSTOM);
81
+ parent.addChild(this);
82
+ parent.getBody().appendChild(this.body);
83
+ } else {
84
+ this.setType(WidgetTypes.FREE);
85
+ document.body.appendChild(this.body);
86
+ }
87
+
88
+ this.body.addEventListener("click", (e) => {
89
+ this.subscribers.forEach((callback) => {
90
+ if (callback.event == "click") {
91
+ callback.then(e, this);
92
+ }
93
+ });
94
+ });
95
+
96
+ this.body.addEventListener("mousedown", (e) => {
97
+ this.subscribers.forEach((callback) => {
98
+ if (callback.event == "mousedown") {
99
+ callback.then(e, this);
100
+ }
101
+ });
102
+ });
103
+
104
+ this.body.addEventListener("mouseup", (e) => {
105
+ this.subscribers.forEach((callback) => {
106
+ if (callback.event == "mouseup") {
107
+ callback.then(e, this);
108
+ }
109
+ });
110
+ });
111
+
112
+ this.body.addEventListener("mousemove", (e) => {
113
+ this.subscribers.forEach((callback) => {
114
+ if (callback.event == "mousemove") {
115
+ callback.then(e, this);
116
+ }
117
+ });
118
+ });
119
+
120
+ this.init();
121
+
122
+ this.getMaxZIndex();
123
+ }
124
+
125
+ public subscribe(cb: WUICallback) {
126
+ const randomId =
127
+ Math.random().toString(36).substring(2, 15) +
128
+ Math.random().toString(36).substring(2, 15);
129
+
130
+ this.subscribers.set(`${randomId}.${cb.event}`, cb);
131
+ }
132
+
133
+ public unsubscribe(event: WUIEvent) {
134
+ this.subscribers.delete(`${event}`);
135
+ }
136
+
137
+ /**
138
+ * Sets the padding value for the object.
139
+ *
140
+ * @param {number} p - The padding value to set.
141
+ * @return {void} This function does not return a value.
142
+ */
143
+ public setPadding(p: number): void {
144
+ this.padding = p;
145
+ }
146
+
147
+ /**
148
+ * Sets the value of x and updates the left position of the element.
149
+ *
150
+ * @param {number} x - The new value of x.
151
+ * @return {void} This function does not return anything.
152
+ */
153
+ public setX(x: number): void {
154
+ this.left = x;
155
+ this.body.style.left = `${x}px`;
156
+ }
157
+
158
+ /**
159
+ * Sets the value of the 'y' property and updates the 'top' and 'body.style.top' properties accordingly.
160
+ *
161
+ * @param {number} y - The new value for the 'y' property.
162
+ * @return {void} This function does not return a value.
163
+ */
164
+ public setY(y: number): void {
165
+ this.top = y;
166
+ this.body.style.top = `${y}px`;
167
+ }
168
+
169
+ /**
170
+ * Sets the width of the element and updates the corresponding inline style.
171
+ *
172
+ * @param {number} w - The new width value.
173
+ * @return {void} This function does not return a value.
174
+ */
175
+ public setW(w: number): void {
176
+ //this.width = w;
177
+ this.body.style.width = `${w}px`;
178
+ }
179
+
180
+ /**
181
+ * Sets the initial value of the width property.
182
+ *
183
+ * @param {number} w - The initial width value.
184
+ * @return {void} This function does not return a value.
185
+ */
186
+ public setInitialW(w: number): void {
187
+ this.initialWidth = w;
188
+ }
189
+
190
+ /**
191
+ * Sets the value of the height property and updates the height of the element.
192
+ *
193
+ * @param {number} h - The new height value.
194
+ * @return {void} This function does not return any value.
195
+ */
196
+ public setH(h: number): void {
197
+ //this.height = h;
198
+ this.body.style.height = `${h}px`;
199
+ }
200
+
201
+ /**
202
+ * Sets the initial height of the object.
203
+ *
204
+ * @param {number} h - The initial height value to set.
205
+ * @return {void} This function does not return a value.
206
+ */
207
+ public setInitialH(h: number): void {
208
+ this.initialHeight = h;
209
+ }
210
+
211
+ /**
212
+ * Sets the width and height of the element.
213
+ *
214
+ * @param {number} w - The width to set.
215
+ * @param {number} h - The height to set.
216
+ * @return {void} This function does not return anything.
217
+ */
218
+ public setWH(w: number, h: number): void {
219
+ this.width = w;
220
+ this.height = h;
221
+ this.body.style.width = `${w}px`;
222
+ this.body.style.height = `${h}px`;
223
+
224
+ //ejecutar los subscriptores
225
+ this.subscribers.forEach((callback) => {
226
+ if (callback.event == "resize") {
227
+ callback.then(new Event("resize"), this);
228
+ }
229
+ });
230
+ }
231
+
232
+ public setFixedSize(s: number): void {
233
+ this.fixedSize = s;
234
+ }
235
+
236
+ /**
237
+ * Sets the type of the widget.
238
+ *
239
+ * @param {WidgetTypes} type - The type of widget to set.
240
+ * @return {void} This function does not return anything.
241
+ */
242
+ public setType(type: WidgetTypes): void {
243
+ this.type = type;
244
+
245
+ let freeStyle = false;
246
+
247
+ if (this.type === WidgetTypes.FREE) {
248
+ freeStyle = true;
249
+ }
250
+
251
+ const parent = this.getParent();
252
+
253
+ if (parent) {
254
+ if (parent.type === WidgetTypes.FREE) {
255
+ freeStyle = true;
256
+ }
257
+ }
258
+
259
+ if (freeStyle) {
260
+ //this.body.style.position = "relative";
261
+ this.addClass("WUIFixPosition");
262
+ //this.body.style.overflow = "auto";
263
+ this.body.style.left = "";
264
+ this.body.style.top = "";
265
+ this.body.style.width = "";
266
+ this.body.style.height = "";
267
+
268
+ this.body.style.bottom = "";
269
+ this.body.style.right = "";
270
+ } else {
271
+ if (
272
+ this.type === WidgetTypes.CUSTOM ||
273
+ this.type === WidgetTypes.FILL
274
+ ) {
275
+ this.body.style.position = "absolute";
276
+ this.body.style.overflow = "hidden";
277
+ }
278
+ }
279
+
280
+ if (this.initialWidth != 0) {
281
+ this.body.style.width = `${this.initialWidth}px`;
282
+ }
283
+ if (this.initialHeight != 0) {
284
+ this.body.style.height = `${this.initialHeight}px`;
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Sets the alignment of the widget.
290
+ *
291
+ * @param {WidgetAlignTypes} align - The alignment to set.
292
+ * @return {void}
293
+ */
294
+ public setAlign(align: WidgetAlignTypes): void {
295
+ this.align = align;
296
+ }
297
+
298
+ /**
299
+ * Sets the visibility of the element.
300
+ *
301
+ * @param {boolean} visible - The visibility state of the element.
302
+ * @return {void}
303
+ */
304
+ public setVisible(visible: boolean): void {
305
+ this.visible = visible;
306
+ this.body.style.display = visible ? "" : "none";
307
+ }
308
+
309
+ /**
310
+ * Sets the enabled status of the object.
311
+ *
312
+ * @param {boolean} enabled - The new enabled status.
313
+ * @return {void} This function does not return a value.
314
+ */
315
+ public setEnabled(enabled: boolean): void {
316
+ this.enabled = enabled;
317
+ }
318
+
319
+ /**
320
+ * Sets the parent of the widget.
321
+ *
322
+ * @param {IWidget | null} parent - The parent widget or null if there is no parent.
323
+ * @return {void} This function does not return anything.
324
+ */
325
+ public setParent(parent: IWidget | null): void {
326
+ if (this.parent) {
327
+ if (this.body) {
328
+ if (this.body.parentNode) {
329
+ this.body.parentNode.removeChild(this.body);
330
+ }
331
+ }
332
+ }
333
+ this.parent = parent;
334
+ this.parent?.body.appendChild(this.body);
335
+
336
+ //this.parent?.addChild(this);
337
+ }
338
+
339
+ /**
340
+ * Sets the body of the element.
341
+ *
342
+ * @param {HTMLElement} body - The new body element.
343
+ * @return {void} This function does not return anything.
344
+ */
345
+ public setBody(body: HTMLElement): void {
346
+ if (this.body.parentNode) {
347
+ this.body.parentNode.removeChild(this.body);
348
+ }
349
+
350
+ this.body = body;
351
+ this.parent?.getBody().appendChild(this.body);
352
+ }
353
+
354
+ /**
355
+ * Sets the array of child widgets for this widget.
356
+ *
357
+ * @param {IWidget[]} childs - The array of child widgets to set.
358
+ * @return {void}
359
+ */
360
+ public setChilds(childs: IWidget[]): void {
361
+ this.childs = childs;
362
+ }
363
+
364
+ /**
365
+ * Sets the overflow value.
366
+ *
367
+ * @param {boolean} overflow - The new value for the overflow.
368
+ * @return {void} This function does not return a value.
369
+ */
370
+ setOverflow(overflow: boolean): void {
371
+ this.overflow = overflow;
372
+ this.getBody().style.overflow = this.overflow ? "auto" : "hidden";
373
+ }
374
+
375
+ /**
376
+ * Adds a CSS class to the list of CSS classes.
377
+ *
378
+ * @param {string} cssClass - The CSS class to add.
379
+ * @return {void}
380
+ */
381
+ public addClass(cssClass: string): void {
382
+ this.body.classList.add(cssClass);
383
+ }
384
+
385
+ /**
386
+ * Deletes a CSS class from the list of CSS classes.
387
+ *
388
+ * @param {string} cssClass - The CSS class to be deleted.
389
+ * @return {void} This function does not return a value.
390
+ */
391
+ public deleteClass(cssClass: string): void {
392
+ this.body.classList.remove(cssClass);
393
+ }
394
+
395
+ public deleteAllClasses(): void {
396
+ this.body.classList.forEach((cssClass) => {
397
+ this.deleteClass(cssClass);
398
+ });
399
+ }
400
+
401
+ /**
402
+ * Retrieves the padding value.
403
+ *
404
+ * @return {number} The padding value.
405
+ */
406
+ getPadding(): number {
407
+ return this.padding;
408
+ }
409
+
410
+ /**
411
+ * Retrieves the value of the 'left' property.
412
+ *
413
+ * @return {number} The value of the 'left' property.
414
+ */
415
+ public getX(): number {
416
+ return this.left;
417
+ }
418
+
419
+ /**
420
+ * Retrieves the value of Y.
421
+ *
422
+ * @return {number} The value of Y.
423
+ */
424
+ public getY(): number {
425
+ return this.top;
426
+ }
427
+
428
+ /**
429
+ * Get the width of the object.
430
+ *
431
+ * @return {number} The width of the object.
432
+ */
433
+ public getW(): number {
434
+ //return this.width;
435
+ return this.getBody().clientWidth;
436
+ }
437
+
438
+ /**
439
+ * Returns the value of the height property.
440
+ *
441
+ * @return {number} The value of the height property.
442
+ */
443
+ public getH(): number {
444
+ //return this.height;
445
+ return this.getBody().clientHeight;
446
+ }
447
+
448
+ /**
449
+ * Returns the position of the element relative to the viewport.
450
+ *
451
+ * @param {boolean} scroll - Indicates whether to account for scrolling in the position calculation. Defaults to false.
452
+ * @return {Vector2D} An object with x and y properties representing the position of the element.
453
+ */
454
+ public getPosition(scroll: boolean = false): Vector2D {
455
+ var box = this.getBody().getBoundingClientRect();
456
+ var body = document.body;
457
+ var docElem = document.documentElement;
458
+ var scrollTop = scroll ? docElem.scrollTop || body.scrollTop : 0;
459
+ var scrollLeft = scroll ? docElem.scrollLeft || body.scrollLeft : 0;
460
+ var clientTop = docElem.clientTop || body.clientTop || 0;
461
+ var clientLeft = docElem.clientLeft || body.clientLeft || 0;
462
+ var top = box.top + scrollTop - clientTop;
463
+ var left = box.left + scrollLeft - clientLeft;
464
+
465
+ return { x: Math.round(left), y: Math.round(top) };
466
+ }
467
+
468
+ public getFixedSize(): number | null {
469
+ return this.fixedSize;
470
+ }
471
+
472
+ /**
473
+ * Returns the type of the widget.
474
+ *
475
+ * @return {WidgetTypes} The type of the widget.
476
+ */
477
+ public getType(): WidgetTypes {
478
+ return this.type;
479
+ }
480
+
481
+ /**
482
+ * Retrieves the alignment of the widget.
483
+ *
484
+ * @return {WidgetAlignTypes} The alignment of the widget.
485
+ */
486
+ public getAlign(): WidgetAlignTypes {
487
+ return this.align;
488
+ }
489
+
490
+ /**
491
+ * Retrieves the value of the enabled flag.
492
+ *
493
+ * @return {boolean} The value of the enabled flag.
494
+ */
495
+ public getEnabled(): boolean {
496
+ return this.enabled;
497
+ }
498
+
499
+ /**
500
+ * Returns the visibility status of the element.
501
+ *
502
+ * @return {boolean} The visibility status of the element.
503
+ */
504
+ public getVisible(): boolean {
505
+ return this.visible;
506
+ }
507
+
508
+ /**
509
+ * Retrieves the parent widget of this widget.
510
+ *
511
+ * @return {IWidget | null} The parent widget or null if there is no parent.
512
+ */
513
+ public getParent(): IWidget | null {
514
+ return this.parent;
515
+ }
516
+
517
+ /**
518
+ * Returns the body element of the object.
519
+ *
520
+ * @return {HTMLElement} The body element.
521
+ */
522
+ public getBody(): HTMLElement {
523
+ return this.body;
524
+ }
525
+
526
+ /**
527
+ * Retrieves the array of child widgets.
528
+ *
529
+ * @return {IWidget[]} The array of child widgets.
530
+ */
531
+ public getChilds(): IWidget[] {
532
+ return this.childs;
533
+ }
534
+
535
+ /**
536
+ * Returns the value of the 'overflow' property.
537
+ *
538
+ * @return {boolean} The value of the 'overflow' property.
539
+ */
540
+ public getOverflow(): boolean {
541
+ return this.overflow;
542
+ }
543
+
544
+ /**
545
+ * Adds a child widget to the current widget.
546
+ *
547
+ * @param {IWidget} child - The widget to be added.
548
+ * @return {void} - This function does not return anything.
549
+ */
550
+ public addChild(child: IWidget | null = null): void {
551
+ if (!child) return;
552
+ this.childs.push(child);
553
+ child.setParent(this);
554
+
555
+ child.init();
556
+ /*child.resize();
557
+ child.render();*/
558
+ }
559
+
560
+ /**
561
+ * Disables the selection feature.
562
+ *
563
+ * @param {void} -
564
+ * @return {void} -
565
+ */
566
+ public disableSelection(): void {
567
+ console.log("disableSelection");
568
+ }
569
+
570
+ /**
571
+ * Enables selection.
572
+ *
573
+ */
574
+ public enableSelection(): void {
575
+ console.log("enableSelection");
576
+ }
577
+
578
+ /**
579
+ * Hides the element by setting its visibility to false.
580
+ *
581
+ * @param {void} -
582
+ * @return {void} -
583
+ */
584
+ public hide(): void {
585
+ this.setVisible(false);
586
+ }
587
+
588
+ /**
589
+ * Initializes the function.
590
+ *
591
+ * @param {type} paramName - description of parameter
592
+ * @return {type} description of return value
593
+ */
594
+ public init(): void {
595
+ if (this.type !== WidgetTypes.FREE) {
596
+ this.body.style.position = "absolute";
597
+ this.body.style.overflow = "hidden";
598
+ }
599
+
600
+ this.initPosition();
601
+ }
602
+
603
+ /**
604
+ * Initializes the position.
605
+ *
606
+ * @param {void} - No parameters required.
607
+ * @return {void} - No return value.
608
+ */
609
+ initPosition(): void {
610
+ if (this.type === WidgetTypes.FILL) {
611
+ if (!this.parent) {
612
+ this.setX(0);
613
+ this.setY(0);
614
+ this.setWH(window.innerWidth, window.innerHeight);
615
+ } else {
616
+ //Si tiene padre que lo controle el padre!
617
+ return;
618
+ }
619
+ }
620
+ }
621
+
622
+ /**
623
+ * Renders the component.
624
+ *
625
+ * @return {void}
626
+ */
627
+ public render(): void {
628
+ //this.initPosition();
629
+
630
+ const padding = this.padding;
631
+
632
+ const size =
633
+ this.align === WidgetAlignTypes.HORIZONTAL
634
+ ? this.width
635
+ : this.height;
636
+
637
+ let currentPosition = padding;
638
+
639
+ //Obtener el available size si hay elementos con fixed size
640
+ let sumFixedSizes = 0;
641
+
642
+ let totalFillElements = 0;
643
+ for (const child of this.childs) {
644
+ if (child.type === WidgetTypes.FILL) {
645
+ totalFillElements++;
646
+ }
647
+ }
648
+
649
+ let freeElements = totalFillElements;
650
+
651
+ for (const child of this.childs) {
652
+ if (child.getFixedSize() !== null) {
653
+ sumFixedSizes += child.getFixedSize() as number;
654
+ freeElements--;
655
+ }
656
+ }
657
+
658
+ let availableSize = size - sumFixedSizes;
659
+
660
+ /**
661
+ * Elementos que tienen un padre LIBRE
662
+ * ===================================
663
+ */
664
+
665
+ const myParent = this.getParent();
666
+ if (myParent) {
667
+ if (myParent.type === WidgetTypes.FREE) {
668
+ //this.getBody().style.position = "relative";
669
+ this.addClass("WUIFixPosition");
670
+ this.getBody().style.left = "";
671
+ this.getBody().style.top = "";
672
+ this.getBody().style.height = "";
673
+ this.getBody().style.width = "";
674
+ if (this.initialWidth != 0) {
675
+ this.getBody().style.width = `${this.initialWidth}px`;
676
+ }
677
+ if (this.initialHeight != 0) {
678
+ this.getBody().style.height = `${this.initialHeight}px`;
679
+ }
680
+ }
681
+ }
682
+ /************************************ */
683
+
684
+ for (const child of this.childs) {
685
+ if (child.type !== WidgetTypes.FILL) {
686
+ child.render();
687
+
688
+ continue;
689
+ }
690
+
691
+ let elementSize = (availableSize - padding) / freeElements;
692
+
693
+ if (child.getFixedSize() !== null) {
694
+ elementSize = child.getFixedSize() as number;
695
+ }
696
+
697
+ if (this.align === WidgetAlignTypes.HORIZONTAL) {
698
+ child.setY(padding);
699
+ child.setX(currentPosition);
700
+
701
+ if (child.type === WidgetTypes.FILL) {
702
+ child.setWH(
703
+ elementSize - padding,
704
+ this.getH() - padding * 2
705
+ );
706
+ } else {
707
+ child.setWH(
708
+ elementSize - padding,
709
+ this.getH() - padding * 2
710
+ );
711
+ }
712
+ } else if (this.align === WidgetAlignTypes.VERTICAL) {
713
+ child.setX(padding);
714
+ child.setY(currentPosition);
715
+ if (child.type === WidgetTypes.FILL) {
716
+ child.setWH(
717
+ this.getW() - padding * 2,
718
+ elementSize - padding
719
+ );
720
+ } else {
721
+ child.setWH(
722
+ this.getW() - padding * 2,
723
+ elementSize - padding
724
+ );
725
+ }
726
+ }
727
+ currentPosition += elementSize;
728
+ child.render();
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Resizes the element and its child elements.
734
+ */
735
+ public resize(): void {
736
+ // Initialize the position of the element
737
+ this.initPosition();
738
+ this.render();
739
+ }
740
+
741
+ /**
742
+ * Displays the element on the screen.
743
+ *
744
+ * @return {void} - Does not return a value.
745
+ */
746
+ public display(): void {
747
+ this.setVisible(true);
748
+ }
749
+
750
+ /**
751
+ * Toggles the visibility of something.
752
+ *
753
+ * @return {void} Nothing is returned.
754
+ */
755
+ public toggle(): void {
756
+ this.setVisible(!this.visible);
757
+ }
758
+
759
+ renderHTML(content: any): HTMLElement {
760
+ this.body.appendChild(content);
761
+ return content as HTMLElement;
762
+ }
763
+
764
+ private getMaxZIndex(
765
+ maxZindex: number = 0,
766
+ node: ChildNode | null = null
767
+ ): number {
768
+ const parent = node ? node : document.body;
769
+
770
+ if (parent instanceof HTMLElement) {
771
+ if (parseInt(window.getComputedStyle(parent).zIndex) > maxZindex) {
772
+ maxZindex = parseInt(window.getComputedStyle(parent).zIndex);
773
+ }
774
+ }
775
+
776
+ parent?.childNodes.forEach((child) => {
777
+ maxZindex = this.getMaxZIndex(maxZindex, child);
778
+ });
779
+
780
+ return maxZindex;
781
+ }
782
+
783
+ setZIndex(zIndex: number): void {
784
+ this.getBody().style.zIndex = `${zIndex}`;
785
+ }
786
+
787
+ raisteTop(): void {
788
+ this.setZIndex(this.getMaxZIndex() + 1);
789
+ }
790
+
791
+ raiseBottom(): void {
792
+ this.setZIndex(0);
793
+ }
794
+
795
+ /**
796
+ * Attaches a widget to the current widget and delete all pre-existents widgets
797
+ *
798
+ * @param {IWidget} guest - The widget to attach.
799
+ * @return {void} This function does not return anything.
800
+ */
801
+ attachWidget(guest: IWidget): void {
802
+ this.removeAllChilds();
803
+ this.addChild(guest);
804
+ guest.setParent(this);
805
+ guest.render();
806
+ this.resize();
807
+ this.render();
808
+ }
809
+
810
+ /**
811
+ * Removes all child nodes from the current element.
812
+ *
813
+ * @return {void} No return value.
814
+ */
815
+ removeAllChilds(): void {
816
+ while (this.getBody().childNodes.length > 0) {
817
+ const child = this.getBody().firstChild;
818
+ if (child) this.getBody().removeChild(child);
819
+ }
820
+
821
+ this.childs = [];
822
+ }
823
+
824
+ free(): void {
825
+ if (this.childs) {
826
+ for (const child of this.childs) {
827
+ child.free();
828
+ }
829
+ }
830
+ window.w.delete(this.id);
831
+ }
832
+ }