squarified 0.4.3 → 0.6.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.
package/dist/index.js CHANGED
@@ -1,116 +1,1987 @@
1
1
  'use strict';
2
2
 
3
- var domEvent = require('./dom-event-CeVZ44nB.js');
3
+ const DEG_TO_RAD = Math.PI / 180;
4
+ class Matrix2D {
5
+ a;
6
+ b;
7
+ c;
8
+ d;
9
+ e;
10
+ f;
11
+ constructor(loc = {}){
12
+ this.a = loc.a || 1;
13
+ this.b = loc.b || 0;
14
+ this.c = loc.c || 0;
15
+ this.d = loc.d || 1;
16
+ this.e = loc.e || 0;
17
+ this.f = loc.f || 0;
18
+ }
19
+ create(loc) {
20
+ Object.assign(this, loc);
21
+ return this;
22
+ }
23
+ transform(x, y, scaleX, scaleY, rotation, skewX, skewY) {
24
+ this.scale(scaleX, scaleY).translation(x, y);
25
+ if (skewX || skewY) {
26
+ this.skew(skewX, skewY);
27
+ } else {
28
+ this.roate(rotation);
29
+ }
30
+ return this;
31
+ }
32
+ translation(x, y) {
33
+ this.e += x;
34
+ this.f += y;
35
+ return this;
36
+ }
37
+ scale(a, d) {
38
+ this.a *= a;
39
+ this.d *= d;
40
+ return this;
41
+ }
42
+ skew(x, y) {
43
+ const tanX = Math.tan(x * DEG_TO_RAD);
44
+ const tanY = Math.tan(y * DEG_TO_RAD);
45
+ const a = this.a + this.b * tanX;
46
+ const b = this.b + this.a * tanY;
47
+ const c = this.c + this.d * tanX;
48
+ const d = this.d + this.c * tanY;
49
+ this.a = a;
50
+ this.b = b;
51
+ this.c = c;
52
+ this.d = d;
53
+ return this;
54
+ }
55
+ roate(rotation) {
56
+ if (rotation > 0) {
57
+ const rad = rotation * DEG_TO_RAD;
58
+ const cosTheta = Math.cos(rad);
59
+ const sinTheta = Math.sin(rad);
60
+ const a = this.a * cosTheta - this.b * sinTheta;
61
+ const b = this.a * sinTheta + this.b * cosTheta;
62
+ const c = this.c * cosTheta - this.d * sinTheta;
63
+ const d = this.c * sinTheta + this.d * cosTheta;
64
+ this.a = a;
65
+ this.b = b;
66
+ this.c = c;
67
+ this.d = d;
68
+ }
69
+ return this;
70
+ }
71
+ }
4
72
 
5
- function createTreemap(// @ts-expect-error todo fix
6
- options) {
7
- const { plugins = [], graphic = {} } = options || {};
8
- let root = null;
9
- let installed = false;
10
- let domEvent$1 = null;
11
- let component = null;
12
- const exposedEvent = new domEvent.Event();
13
- if (!Array.isArray(plugins)) {
14
- domEvent.logger.panic('Plugins should be an array');
73
+ const SELF_ID = {
74
+ id: 0,
75
+ get () {
76
+ return this.id++;
77
+ }
78
+ };
79
+ var DisplayType = /*#__PURE__*/ function(DisplayType) {
80
+ DisplayType["Graph"] = "Graph";
81
+ DisplayType["Box"] = "Box";
82
+ DisplayType["Text"] = "Text";
83
+ DisplayType["RoundRect"] = "RoundRect";
84
+ DisplayType["Bitmap"] = "Bitmap";
85
+ return DisplayType;
86
+ }({});
87
+ class Display {
88
+ parent;
89
+ id;
90
+ matrix;
91
+ constructor(){
92
+ this.parent = null;
93
+ this.id = SELF_ID.get();
94
+ this.matrix = new Matrix2D();
95
+ }
96
+ destory() {
97
+ //
98
+ }
99
+ }
100
+ const ASSIGN_MAPPINGS = {
101
+ fillStyle: 0o1,
102
+ strokeStyle: 0o2,
103
+ font: 0o4,
104
+ lineWidth: 0o10,
105
+ textAlign: 0o20,
106
+ textBaseline: 0o40
107
+ };
108
+ const ASSIGN_MAPPINGS_MODE = ASSIGN_MAPPINGS.fillStyle | ASSIGN_MAPPINGS.strokeStyle | ASSIGN_MAPPINGS.font | ASSIGN_MAPPINGS.lineWidth | ASSIGN_MAPPINGS.textAlign | ASSIGN_MAPPINGS.textBaseline;
109
+ const CALL_MAPPINGS_MODE = 0o0;
110
+ function createInstruction() {
111
+ return {
112
+ mods: [],
113
+ fillStyle (...args) {
114
+ this.mods.push({
115
+ mod: [
116
+ 'fillStyle',
117
+ args
118
+ ],
119
+ type: ASSIGN_MAPPINGS.fillStyle
120
+ });
121
+ },
122
+ fillRect (...args) {
123
+ this.mods.push({
124
+ mod: [
125
+ 'fillRect',
126
+ args
127
+ ],
128
+ type: CALL_MAPPINGS_MODE
129
+ });
130
+ },
131
+ strokeStyle (...args) {
132
+ this.mods.push({
133
+ mod: [
134
+ 'strokeStyle',
135
+ args
136
+ ],
137
+ type: ASSIGN_MAPPINGS.strokeStyle
138
+ });
139
+ },
140
+ lineWidth (...args) {
141
+ this.mods.push({
142
+ mod: [
143
+ 'lineWidth',
144
+ args
145
+ ],
146
+ type: ASSIGN_MAPPINGS.lineWidth
147
+ });
148
+ },
149
+ strokeRect (...args) {
150
+ this.mods.push({
151
+ mod: [
152
+ 'strokeRect',
153
+ args
154
+ ],
155
+ type: CALL_MAPPINGS_MODE
156
+ });
157
+ },
158
+ fillText (...args) {
159
+ this.mods.push({
160
+ mod: [
161
+ 'fillText',
162
+ args
163
+ ],
164
+ type: CALL_MAPPINGS_MODE
165
+ });
166
+ },
167
+ font (...args) {
168
+ this.mods.push({
169
+ mod: [
170
+ 'font',
171
+ args
172
+ ],
173
+ type: ASSIGN_MAPPINGS.font
174
+ });
175
+ },
176
+ textBaseline (...args) {
177
+ this.mods.push({
178
+ mod: [
179
+ 'textBaseline',
180
+ args
181
+ ],
182
+ type: ASSIGN_MAPPINGS.textBaseline
183
+ });
184
+ },
185
+ textAlign (...args) {
186
+ this.mods.push({
187
+ mod: [
188
+ 'textAlign',
189
+ args
190
+ ],
191
+ type: ASSIGN_MAPPINGS.textAlign
192
+ });
193
+ },
194
+ beginPath () {
195
+ this.mods.push({
196
+ mod: [
197
+ 'beginPath',
198
+ []
199
+ ],
200
+ type: CALL_MAPPINGS_MODE
201
+ });
202
+ },
203
+ moveTo (...args) {
204
+ this.mods.push({
205
+ mod: [
206
+ 'moveTo',
207
+ args
208
+ ],
209
+ type: CALL_MAPPINGS_MODE
210
+ });
211
+ },
212
+ arcTo (...args) {
213
+ this.mods.push({
214
+ mod: [
215
+ 'arcTo',
216
+ args
217
+ ],
218
+ type: CALL_MAPPINGS_MODE
219
+ });
220
+ },
221
+ closePath () {
222
+ this.mods.push({
223
+ mod: [
224
+ 'closePath',
225
+ []
226
+ ],
227
+ type: CALL_MAPPINGS_MODE
228
+ });
229
+ },
230
+ fill () {
231
+ this.mods.push({
232
+ mod: [
233
+ 'fill',
234
+ []
235
+ ],
236
+ type: CALL_MAPPINGS_MODE
237
+ });
238
+ },
239
+ stroke () {
240
+ this.mods.push({
241
+ mod: [
242
+ 'stroke',
243
+ []
244
+ ],
245
+ type: CALL_MAPPINGS_MODE
246
+ });
247
+ },
248
+ drawImage (...args) {
249
+ // @ts-expect-error safe
250
+ this.mods.push({
251
+ mod: [
252
+ 'drawImage',
253
+ args
254
+ ],
255
+ type: CALL_MAPPINGS_MODE
256
+ });
257
+ }
258
+ };
259
+ }
260
+ class S extends Display {
261
+ width;
262
+ height;
263
+ x;
264
+ y;
265
+ scaleX;
266
+ scaleY;
267
+ rotation;
268
+ skewX;
269
+ skewY;
270
+ constructor(options = {}){
271
+ super();
272
+ this.width = options.width || 0;
273
+ this.height = options.height || 0;
274
+ this.x = options.x || 0;
275
+ this.y = options.y || 0;
276
+ this.scaleX = options.scaleX || 1;
277
+ this.scaleY = options.scaleY || 1;
278
+ this.rotation = options.rotation || 0;
279
+ this.skewX = options.skewX || 0;
280
+ this.skewY = options.skewY || 0;
281
+ }
282
+ }
283
+ // For performance. we need impl AABB Check for render.
284
+ class Graph extends S {
285
+ instruction;
286
+ __options__;
287
+ __widget__;
288
+ constructor(options = {}){
289
+ super(options);
290
+ this.instruction = createInstruction();
291
+ this.__options__ = options;
292
+ this.__widget__ = null;
293
+ }
294
+ render(ctx) {
295
+ this.create();
296
+ const cap = this.instruction.mods.length;
297
+ for(let i = 0; i < cap; i++){
298
+ const { mod, type } = this.instruction.mods[i];
299
+ const [direct, ...args] = mod;
300
+ if (type & ASSIGN_MAPPINGS_MODE) {
301
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
302
+ // @ts-expect-error
303
+ ctx[direct] = args[0];
304
+ continue;
305
+ }
306
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
307
+ // @ts-expect-error
308
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
309
+ ctx[direct].apply(ctx, ...args);
310
+ }
311
+ }
312
+ get __instanceOf__() {
313
+ return "Graph";
314
+ }
315
+ }
316
+
317
+ function isGraph(display) {
318
+ return display.__instanceOf__ === DisplayType.Graph;
319
+ }
320
+ function isBox(display) {
321
+ return display.__instanceOf__ === DisplayType.Box;
322
+ }
323
+ function isRoundRect(display) {
324
+ return isGraph(display) && display.__shape__ === DisplayType.RoundRect;
325
+ }
326
+ function isText(display) {
327
+ return isGraph(display) && display.__shape__ === DisplayType.Text;
328
+ }
329
+ function isBitmap(display) {
330
+ return isGraph(display) && display.__shape__ === DisplayType.Bitmap;
331
+ }
332
+ const asserts = {
333
+ isGraph,
334
+ isBox,
335
+ isText,
336
+ isRoundRect,
337
+ isBitmap
338
+ };
339
+
340
+ class C extends Display {
341
+ elements;
342
+ constructor(){
343
+ super();
344
+ this.elements = [];
345
+ }
346
+ add(...elements) {
347
+ const cap = elements.length;
348
+ for(let i = 0; i < cap; i++){
349
+ const element = elements[i];
350
+ if (element.parent) ;
351
+ this.elements.push(element);
352
+ element.parent = this;
353
+ }
354
+ }
355
+ remove(...elements) {
356
+ const cap = elements.length;
357
+ for(let i = 0; i < cap; i++){
358
+ for(let j = this.elements.length - 1; j >= 0; j--){
359
+ const element = this.elements[j];
360
+ if (element.id === elements[i].id) {
361
+ this.elements.splice(j, 1);
362
+ element.parent = null;
363
+ }
364
+ }
365
+ }
366
+ }
367
+ destory() {
368
+ this.elements.forEach((element)=>element.parent = null);
369
+ this.elements.length = 0;
370
+ }
371
+ }
372
+ class Box extends C {
373
+ elements;
374
+ constructor(){
375
+ super();
376
+ this.elements = [];
377
+ }
378
+ add(...elements) {
379
+ const cap = elements.length;
380
+ for(let i = 0; i < cap; i++){
381
+ const element = elements[i];
382
+ if (element.parent) ;
383
+ this.elements.push(element);
384
+ element.parent = this;
385
+ }
386
+ }
387
+ remove(...elements) {
388
+ const cap = elements.length;
389
+ for(let i = 0; i < cap; i++){
390
+ for(let j = this.elements.length - 1; j >= 0; j--){
391
+ const element = this.elements[j];
392
+ if (element.id === elements[i].id) {
393
+ this.elements.splice(j, 1);
394
+ element.parent = null;
395
+ }
396
+ }
397
+ }
398
+ }
399
+ destory() {
400
+ this.elements.forEach((element)=>element.parent = null);
401
+ this.elements.length = 0;
402
+ }
403
+ get __instanceOf__() {
404
+ return DisplayType.Box;
405
+ }
406
+ clone() {
407
+ const box = new Box();
408
+ if (this.elements.length) {
409
+ const stack = [
410
+ {
411
+ elements: this.elements,
412
+ parent: box
413
+ }
414
+ ];
415
+ while(stack.length > 0){
416
+ const { elements, parent } = stack.pop();
417
+ const cap = elements.length;
418
+ for(let i = 0; i < cap; i++){
419
+ const element = elements[i];
420
+ if (asserts.isBox(element)) {
421
+ const newBox = new Box();
422
+ newBox.parent = parent;
423
+ parent.add(newBox);
424
+ stack.push({
425
+ elements: element.elements,
426
+ parent: newBox
427
+ });
428
+ } else if (asserts.isGraph(element)) {
429
+ const el = element.clone();
430
+ el.parent = parent;
431
+ parent.add(el);
432
+ }
433
+ }
434
+ }
435
+ }
436
+ return box;
437
+ }
438
+ }
439
+
440
+ class Bitmap extends Graph {
441
+ bitmap;
442
+ style;
443
+ dpi;
444
+ constructor(options = {}){
445
+ super(options);
446
+ this.bitmap = options.bitmap || null;
447
+ this.style = options.style || Object.create(null);
448
+ this.dpi = options.dpi || 1;
449
+ }
450
+ create() {
451
+ if (this.bitmap) {
452
+ this.instruction.drawImage(this.bitmap, 0, 0, this.bitmap.width / this.dpi, this.bitmap.height / this.dpi);
453
+ }
454
+ }
455
+ clone() {
456
+ return new Bitmap({
457
+ ...this.style,
458
+ ...this.__options__
459
+ });
460
+ }
461
+ get __shape__() {
462
+ return DisplayType.Bitmap;
463
+ }
464
+ }
465
+
466
+ // Runtime is designed for graph element
467
+ function decodeHLS(meta) {
468
+ const { h, l, s, a } = meta;
469
+ if ('a' in meta) {
470
+ return `hsla(${h}deg, ${s}%, ${l}%, ${a})`;
471
+ }
472
+ return `hsl(${h}deg, ${s}%, ${l}%)`;
473
+ }
474
+ function decodeRGB(meta) {
475
+ const { r, g, b, a } = meta;
476
+ if ('a' in meta) {
477
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
478
+ }
479
+ return `rgb(${r}, ${g}, ${b})`;
480
+ }
481
+ function decodeColor(meta) {
482
+ return meta.mode === 'rgb' ? decodeRGB(meta.desc) : decodeHLS(meta.desc);
483
+ }
484
+ function evaluateFillStyle(primitive, opacity = 1) {
485
+ const descibe = {
486
+ mode: primitive.mode,
487
+ desc: {
488
+ ...primitive.desc,
489
+ a: opacity
490
+ }
491
+ };
492
+ return decodeColor(descibe);
493
+ }
494
+ const runtime = {
495
+ evaluateFillStyle
496
+ };
497
+
498
+ class RoundRect extends Graph {
499
+ style;
500
+ constructor(options = {}){
501
+ super(options);
502
+ this.style = options.style || Object.create(null);
503
+ }
504
+ get __shape__() {
505
+ return DisplayType.RoundRect;
506
+ }
507
+ create() {
508
+ const padding = this.style.padding;
509
+ const x = 0;
510
+ const y = 0;
511
+ const width = this.width - padding * 2;
512
+ const height = this.height - padding * 2;
513
+ const radius = this.style.radius || 0;
514
+ this.instruction.beginPath();
515
+ this.instruction.moveTo(x + radius, y);
516
+ this.instruction.arcTo(x + width, y, x + width, y + height, radius);
517
+ this.instruction.arcTo(x + width, y + height, x, y + height, radius);
518
+ this.instruction.arcTo(x, y + height, x, y, radius);
519
+ this.instruction.arcTo(x, y, x + width, y, radius);
520
+ this.instruction.closePath();
521
+ if (this.style.fill) {
522
+ this.instruction.closePath();
523
+ this.instruction.fillStyle(runtime.evaluateFillStyle(this.style.fill, this.style.opacity));
524
+ this.instruction.fill();
525
+ }
526
+ if (this.style.stroke) {
527
+ if (typeof this.style.lineWidth === 'number') {
528
+ this.instruction.lineWidth(this.style.lineWidth);
529
+ }
530
+ this.instruction.strokeStyle(this.style.stroke);
531
+ this.instruction.stroke();
532
+ }
533
+ }
534
+ clone() {
535
+ return new RoundRect({
536
+ ...this.style,
537
+ ...this.__options__
538
+ });
539
+ }
540
+ }
541
+
542
+ class Text extends Graph {
543
+ text;
544
+ style;
545
+ constructor(options = {}){
546
+ super(options);
547
+ this.text = options.text || '';
548
+ this.style = options.style || Object.create(null);
549
+ }
550
+ create() {
551
+ if (this.style.fill) {
552
+ this.instruction.font(this.style.font);
553
+ this.instruction.lineWidth(this.style.lineWidth);
554
+ this.instruction.textBaseline(this.style.baseline);
555
+ this.instruction.fillStyle(this.style.fill);
556
+ this.instruction.fillText(this.text, 0, 0);
557
+ }
558
+ }
559
+ clone() {
560
+ return new Text({
561
+ ...this.style,
562
+ ...this.__options__
563
+ });
564
+ }
565
+ get __shape__() {
566
+ return DisplayType.Text;
567
+ }
568
+ }
569
+
570
+ function traverse(graphs, handler) {
571
+ const len = graphs.length;
572
+ for(let i = 0; i < len; i++){
573
+ const graph = graphs[i];
574
+ if (asserts.isGraph(graph)) {
575
+ handler(graph);
576
+ } else if (asserts.isBox(graph)) {
577
+ traverse(graph.elements, handler);
578
+ }
579
+ }
580
+ }
581
+ // https://jhildenbiddle.github.io/canvas-size/#/?id=maxheight
582
+ function getCanvasBoundarySize() {
583
+ const ua = navigator.userAgent;
584
+ let size = 16384;
585
+ if (/Firefox\/(\d+)/.test(ua)) {
586
+ const version = parseInt(RegExp.$1, 10);
587
+ if (version >= 122) {
588
+ size = 23168;
589
+ } else {
590
+ size = 11180;
591
+ }
592
+ }
593
+ return {
594
+ size
595
+ };
596
+ }
597
+ const canvasBoundarySize = getCanvasBoundarySize();
598
+
599
+ // Currently, etoile is an internal module, so we won't need too much easing functions.
600
+ // And the animation logic is implemented by user code.
601
+ const easing = {
602
+ linear: (k)=>k,
603
+ quadraticIn: (k)=>k * k,
604
+ quadraticOut: (k)=>k * (2 - k),
605
+ quadraticInOut: (k)=>{
606
+ if ((k *= 2) < 1) {
607
+ return 0.5 * k * k;
608
+ }
609
+ return -0.5 * (--k * (k - 2) - 1);
610
+ },
611
+ cubicIn: (k)=>k * k * k,
612
+ cubicOut: (k)=>{
613
+ if ((k *= 2) < 1) {
614
+ return 0.5 * k * k * k;
615
+ }
616
+ return 0.5 * ((k -= 2) * k * k + 2);
617
+ },
618
+ cubicInOut: (k)=>{
619
+ if ((k *= 2) < 1) {
620
+ return 0.5 * k * k * k;
621
+ }
622
+ return 0.5 * ((k -= 2) * k * k + 2);
623
+ }
624
+ };
625
+
626
+ class Event {
627
+ eventCollections;
628
+ constructor(){
629
+ this.eventCollections = Object.create(null);
630
+ }
631
+ on(evt, handler, c) {
632
+ if (!(evt in this.eventCollections)) {
633
+ this.eventCollections[evt] = [];
634
+ }
635
+ const data = {
636
+ name: evt,
637
+ handler,
638
+ ctx: c || this,
639
+ silent: false
640
+ };
641
+ this.eventCollections[evt].push(data);
642
+ }
643
+ off(evt, handler) {
644
+ if (evt in this.eventCollections) {
645
+ if (!handler) {
646
+ this.eventCollections[evt] = [];
647
+ return;
648
+ }
649
+ this.eventCollections[evt] = this.eventCollections[evt].filter((d)=>d.handler !== handler);
650
+ }
651
+ }
652
+ silent(evt, handler) {
653
+ if (!(evt in this.eventCollections)) {
654
+ return;
655
+ }
656
+ this.eventCollections[evt].forEach((d)=>{
657
+ if (!handler || d.handler === handler) {
658
+ d.silent = true;
659
+ }
660
+ });
661
+ }
662
+ active(evt, handler) {
663
+ if (!(evt in this.eventCollections)) {
664
+ return;
665
+ }
666
+ this.eventCollections[evt].forEach((d)=>{
667
+ if (!handler || d.handler === handler) {
668
+ d.silent = false;
669
+ }
670
+ });
671
+ }
672
+ emit(evt, ...args) {
673
+ if (!this.eventCollections[evt]) {
674
+ return;
675
+ }
676
+ const handlers = this.eventCollections[evt];
677
+ if (handlers.length) {
678
+ handlers.forEach((d)=>{
679
+ if (d.silent) {
680
+ return;
681
+ }
682
+ d.handler.call(d.ctx, ...args);
683
+ });
684
+ }
685
+ }
686
+ bindWithContext(c) {
687
+ return (evt, handler)=>this.on(evt, handler, c);
688
+ }
689
+ }
690
+
691
+ function hashCode(str) {
692
+ let hash = 0;
693
+ for(let i = 0; i < str.length; i++){
694
+ const code = str.charCodeAt(i);
695
+ hash = (hash << 5) - hash + code;
696
+ hash = hash & hash;
697
+ }
698
+ return hash;
699
+ }
700
+ // For strings we only check the first character to determine if it's a number (I think it's enough)
701
+ function perferNumeric(s) {
702
+ if (typeof s === 'number') {
703
+ return true;
704
+ }
705
+ return s.charCodeAt(0) >= 48 && s.charCodeAt(0) <= 57;
706
+ }
707
+ function createRoundBlock(x, y, width, height, style) {
708
+ return new RoundRect({
709
+ width,
710
+ height,
711
+ x,
712
+ y,
713
+ style: {
714
+ ...style
715
+ }
716
+ });
717
+ }
718
+ function createTitleText(text, x, y, font, color) {
719
+ return new Text({
720
+ text,
721
+ x,
722
+ y,
723
+ style: {
724
+ fill: color,
725
+ textAlign: 'center',
726
+ baseline: 'middle',
727
+ font,
728
+ lineWidth: 1
729
+ }
730
+ });
731
+ }
732
+ const raf = window.requestAnimationFrame;
733
+ function createCanvasElement() {
734
+ return document.createElement('canvas');
735
+ }
736
+ function applyCanvasTransform(ctx, matrix, dpr) {
737
+ ctx.setTransform(matrix.a * dpr, matrix.b * dpr, matrix.c * dpr, matrix.d * dpr, matrix.e * dpr, matrix.f * dpr);
738
+ }
739
+ function mixin(app, methods) {
740
+ methods.forEach(({ name, fn })=>{
741
+ Object.defineProperty(app, name, {
742
+ value: fn(app),
743
+ writable: false
744
+ });
745
+ });
746
+ }
747
+ function prettyStrJoin(...s) {
748
+ return s.join('');
749
+ }
750
+
751
+ const NAME_SPACE = 'etoile';
752
+ const log = {
753
+ error: (message)=>{
754
+ return `[${NAME_SPACE}] ${message}`;
755
+ }
756
+ };
757
+
758
+ function writeBoundingRectForCanvas(c, w, h, dpr) {
759
+ c.width = w * dpr;
760
+ c.height = h * dpr;
761
+ c.style.cssText = `width: ${w}px; height: ${h}px`;
762
+ }
763
+ class Canvas {
764
+ canvas;
765
+ ctx;
766
+ constructor(options){
767
+ this.canvas = createCanvasElement();
768
+ this.setOptions(options);
769
+ this.ctx = this.canvas.getContext('2d');
770
+ }
771
+ setOptions(options) {
772
+ writeBoundingRectForCanvas(this.canvas, options.width, options.height, options.devicePixelRatio);
773
+ }
774
+ }
775
+ class Render {
776
+ options;
777
+ container;
778
+ constructor(to, options){
779
+ this.container = new Canvas(options);
780
+ this.options = options;
781
+ this.initOptions(options);
782
+ if (!options.shaow) {
783
+ to.appendChild(this.container.canvas);
784
+ }
785
+ }
786
+ clear(width, height) {
787
+ this.ctx.clearRect(0, 0, width, height);
788
+ }
789
+ get canvas() {
790
+ return this.container.canvas;
791
+ }
792
+ get ctx() {
793
+ return this.container.ctx;
794
+ }
795
+ initOptions(userOptions = {}) {
796
+ Object.assign(this.options, userOptions);
797
+ this.container.setOptions(this.options);
798
+ }
799
+ destory() {}
800
+ }
801
+
802
+ // First cleanup canvas
803
+ function drawGraphIntoCanvas(graph, opts) {
804
+ const { ctx, dpr } = opts;
805
+ ctx.save();
806
+ if (asserts.isBox(graph)) {
807
+ const elements = graph.elements;
808
+ const cap = elements.length;
809
+ for(let i = 0; i < cap; i++){
810
+ const element = elements[i];
811
+ drawGraphIntoCanvas(element, opts);
812
+ }
813
+ }
814
+ if (asserts.isGraph(graph)) {
815
+ const matrix = graph.matrix.create({
816
+ a: 1,
817
+ b: 0,
818
+ c: 0,
819
+ d: 1,
820
+ e: 0,
821
+ f: 0
822
+ });
823
+ matrix.transform(graph.x, graph.y, graph.scaleX, graph.scaleY, graph.rotation, graph.skewX, graph.skewY);
824
+ if (asserts.isRoundRect(graph)) {
825
+ const effectiveWidth = graph.width - graph.style.padding * 2;
826
+ const effectiveHeight = graph.height - graph.style.padding * 2;
827
+ if (effectiveWidth <= 0 || effectiveHeight <= 0) {
828
+ ctx.restore();
829
+ return;
830
+ }
831
+ if (graph.style.radius >= effectiveHeight / 2 || graph.style.radius >= effectiveWidth / 2) {
832
+ ctx.restore();
833
+ return;
834
+ }
835
+ }
836
+ applyCanvasTransform(ctx, matrix, dpr);
837
+ graph.render(ctx);
838
+ }
839
+ ctx.restore();
840
+ }
841
+ class Schedule extends Box {
842
+ render;
843
+ to;
844
+ event;
845
+ constructor(to, renderOptions = {}){
846
+ super();
847
+ this.to = typeof to === 'string' ? document.querySelector(to) : to;
848
+ if (!this.to) {
849
+ throw new Error(log.error('The element to bind is not found.'));
850
+ }
851
+ const { width, height } = this.to.getBoundingClientRect();
852
+ Object.assign(renderOptions, {
853
+ width,
854
+ height
855
+ }, {
856
+ devicePixelRatio: window.devicePixelRatio || 1
857
+ });
858
+ this.event = new Event();
859
+ this.render = new Render(this.to, renderOptions);
860
+ }
861
+ update() {
862
+ this.render.clear(this.render.options.width, this.render.options.height);
863
+ this.execute(this.render, this);
864
+ const matrix = this.matrix.create({
865
+ a: 1,
866
+ b: 0,
867
+ c: 0,
868
+ d: 1,
869
+ e: 0,
870
+ f: 0
871
+ });
872
+ applyCanvasTransform(this.render.ctx, matrix, this.render.options.devicePixelRatio);
873
+ }
874
+ // execute all graph elements
875
+ execute(render, graph = this) {
876
+ drawGraphIntoCanvas(graph, {
877
+ c: render.canvas,
878
+ ctx: render.ctx,
879
+ dpr: render.options.devicePixelRatio
880
+ });
881
+ }
882
+ }
883
+
884
+ class RenderCache extends Canvas {
885
+ key;
886
+ $memory;
887
+ constructor(opts){
888
+ super(opts);
889
+ this.key = 'render-cache';
890
+ this.$memory = false;
891
+ }
892
+ get state() {
893
+ return this.$memory;
894
+ }
895
+ flush(treemap, matrix = new Matrix2D()) {
896
+ const { devicePixelRatio, width, height } = treemap.render.options;
897
+ const { a, d } = matrix;
898
+ const { size } = canvasBoundarySize;
899
+ // Check outof boundary
900
+ if (width * a >= size || height * d >= size) {
901
+ return;
902
+ }
903
+ if (width * a * height * d >= size * size) {
904
+ return;
905
+ }
906
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
907
+ this.setOptions({
908
+ width: width * a,
909
+ height: height * d,
910
+ devicePixelRatio
911
+ });
912
+ resetLayout(treemap, width * a, height * d);
913
+ drawGraphIntoCanvas(treemap, {
914
+ c: this.canvas,
915
+ ctx: this.ctx,
916
+ dpr: devicePixelRatio
917
+ });
918
+ this.$memory = true;
15
919
  }
16
- const api = {
17
- zoom: domEvent.noop
920
+ destroy() {
921
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
922
+ this.$memory = false;
923
+ }
924
+ }
925
+ class FontCache {
926
+ key;
927
+ fonts;
928
+ ellispsis;
929
+ constructor(){
930
+ this.key = 'font-cache';
931
+ this.fonts = {};
932
+ this.ellispsis = {};
933
+ }
934
+ get state() {
935
+ return true;
936
+ }
937
+ flush(treemap, matrix = new Matrix2D()) {
938
+ const { width, height } = treemap.render.options;
939
+ const { a, d } = matrix;
940
+ const zoomedWidth = width * a;
941
+ const zoomedHeight = height * d;
942
+ if (zoomedWidth <= width || zoomedHeight <= height) {
943
+ return;
944
+ }
945
+ traverse([
946
+ treemap.elements[0]
947
+ ], (graph)=>{
948
+ if (asserts.isRoundRect(graph)) {
949
+ const { x, y, height: graphHeight, width: graphWidth } = graph;
950
+ if (!graphHeight || !graphWidth) {
951
+ return;
952
+ }
953
+ if (x >= 0 && y >= 0 && x + graphWidth <= width && y + graphHeight <= height) {
954
+ if (graph.__widget__) {
955
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
956
+ const node = graph.__widget__.node;
957
+ delete this.fonts[node.id];
958
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
959
+ delete this.ellispsis[node.label];
960
+ }
961
+ }
962
+ }
963
+ });
964
+ }
965
+ destroy() {
966
+ this.fonts = {};
967
+ this.ellispsis = {};
968
+ }
969
+ queryFontById(id, byDefault) {
970
+ if (!(id in this.fonts)) {
971
+ this.fonts[id] = byDefault();
972
+ }
973
+ return this.fonts[id];
974
+ }
975
+ }
976
+
977
+ // primitive types
978
+ const DOM_EVENTS = [
979
+ 'click',
980
+ 'mousedown',
981
+ 'mousemove',
982
+ 'mouseup',
983
+ 'mouseover',
984
+ 'mouseout',
985
+ 'wheel'
986
+ ];
987
+ function getOffset(el) {
988
+ let e = 0;
989
+ let f = 0;
990
+ if (document.documentElement.getBoundingClientRect && el.getBoundingClientRect) {
991
+ const { top, left } = el.getBoundingClientRect();
992
+ e = top;
993
+ f = left;
994
+ } else {
995
+ for(let elt = el; elt; elt = el.offsetParent){
996
+ e += el.offsetLeft;
997
+ f += el.offsetTop;
998
+ }
999
+ }
1000
+ return [
1001
+ e + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
1002
+ f + Math.max(document.documentElement.scrollTop, document.body.scrollTop)
1003
+ ];
1004
+ }
1005
+ function captureBoxXY(c, evt, a, d, translateX, translateY) {
1006
+ const boundingClientRect = c.getBoundingClientRect();
1007
+ if (evt instanceof MouseEvent) {
1008
+ const [e, f] = getOffset(c);
1009
+ return {
1010
+ x: (evt.clientX - boundingClientRect.left - e - translateX) / a,
1011
+ y: (evt.clientY - boundingClientRect.top - f - translateY) / d
1012
+ };
1013
+ }
1014
+ return {
1015
+ x: 0,
1016
+ y: 0
1017
+ };
1018
+ }
1019
+ function createEffectRun(c) {
1020
+ return (fn)=>{
1021
+ const effect = ()=>{
1022
+ const done = fn();
1023
+ if (!done) {
1024
+ c.animationFrameID = raf(effect);
1025
+ }
1026
+ };
1027
+ if (!c.animationFrameID) {
1028
+ c.animationFrameID = raf(effect);
1029
+ }
18
1030
  };
19
- const ctx = {
1031
+ }
1032
+ function createEffectStop(c) {
1033
+ return ()=>{
1034
+ if (c.animationFrameID) {
1035
+ window.cancelAnimationFrame(c.animationFrameID);
1036
+ c.animationFrameID = null;
1037
+ }
1038
+ };
1039
+ }
1040
+ function createSmoothFrame() {
1041
+ const c = {
1042
+ animationFrameID: null
1043
+ };
1044
+ const run = createEffectRun(c);
1045
+ const stop = createEffectStop(c);
1046
+ return {
1047
+ run,
1048
+ stop
1049
+ };
1050
+ }
1051
+ // Some thoughts DOMEvent was designed this way intentionally. I don't have any idea of splitting the general libray yet.
1052
+ // The follow captureBoxXy matrix a and d be 1 is because of the scaled canvas (without zoomed) is with a new layout.
1053
+ function bindDOMEvent(el, evt, dom) {
1054
+ const handler = (e)=>{
1055
+ const { x, y } = captureBoxXY(el, e, 1, 1, dom.matrix.e, dom.matrix.f);
1056
+ // @ts-expect-error safe
1057
+ dom.emit(evt, {
1058
+ native: e,
1059
+ loc: {
1060
+ x,
1061
+ y
1062
+ }
1063
+ });
1064
+ };
1065
+ el.addEventListener(evt, handler);
1066
+ return handler;
1067
+ }
1068
+ class DOMEvent extends Event {
1069
+ el;
1070
+ events;
1071
+ matrix;
1072
+ constructor(el){
1073
+ super();
1074
+ this.el = el;
1075
+ this.matrix = new Matrix2D();
1076
+ this.events = DOM_EVENTS.map((evt)=>bindDOMEvent(this.el, evt, this));
1077
+ }
1078
+ }
1079
+
1080
+ // Note: I won't decide to support mobile devices.
1081
+ // So don't create any reporting issues about mobile devices.
1082
+ // Notte: this only work at wheel event.
1083
+ function useMagicTrackPad(event) {
1084
+ if (event.cancelable !== false) {
1085
+ event.preventDefault();
1086
+ }
1087
+ !event.ctrlKey;
1088
+ }
1089
+
1090
+ function sortChildrenByKey(data, ...keys) {
1091
+ return data.sort((a, b)=>{
1092
+ for (const key of keys){
1093
+ const v = a[key];
1094
+ const v2 = b[key];
1095
+ if (perferNumeric(v) && perferNumeric(v2)) {
1096
+ if (v2 > v) {
1097
+ return 1;
1098
+ }
1099
+ if (v2 < v) {
1100
+ return -1;
1101
+ }
1102
+ continue;
1103
+ }
1104
+ // Not numeric, compare as string
1105
+ const comparison = ('' + v).localeCompare('' + v2);
1106
+ if (comparison !== 0) {
1107
+ return comparison;
1108
+ }
1109
+ }
1110
+ return 0;
1111
+ });
1112
+ }
1113
+ function c2m(data, key, modifier) {
1114
+ if (Array.isArray(data.groups)) {
1115
+ data.groups = sortChildrenByKey(data.groups.map((d)=>c2m(d, key, modifier)), 'weight');
1116
+ }
1117
+ const obj = {
1118
+ ...data,
1119
+ weight: data[key]
1120
+ };
1121
+ if (modifier) {
1122
+ return modifier(obj);
1123
+ }
1124
+ return obj;
1125
+ }
1126
+ function flatten(data) {
1127
+ const result = [];
1128
+ for(let i = 0; i < data.length; i++){
1129
+ const { groups, ...rest } = data[i];
1130
+ result.push(rest);
1131
+ if (groups) {
1132
+ result.push(...flatten(groups));
1133
+ }
1134
+ }
1135
+ return result;
1136
+ }
1137
+ function bindParentForModule(modules, parent) {
1138
+ return modules.map((module)=>{
1139
+ const next = {
1140
+ ...module
1141
+ };
1142
+ next.parent = parent;
1143
+ if (next.groups && Array.isArray(next.groups)) {
1144
+ next.groups = bindParentForModule(next.groups, next);
1145
+ }
1146
+ return next;
1147
+ });
1148
+ }
1149
+ function getNodeDepth(node) {
1150
+ let depth = 0;
1151
+ while(node.parent){
1152
+ node = node.parent;
1153
+ depth++;
1154
+ }
1155
+ return depth;
1156
+ }
1157
+ function visit(data, fn) {
1158
+ if (!data) {
1159
+ return null;
1160
+ }
1161
+ for (const d of data){
1162
+ if (d.children) {
1163
+ const result = visit(d.children, fn);
1164
+ if (result) {
1165
+ return result;
1166
+ }
1167
+ }
1168
+ const stop = fn(d);
1169
+ if (stop) {
1170
+ return d;
1171
+ }
1172
+ }
1173
+ return null;
1174
+ }
1175
+ function findRelativeNode(p, layoutNodes) {
1176
+ return visit(layoutNodes, (node)=>{
1177
+ const [x, y, w, h] = node.layout;
1178
+ if (p.x >= x && p.y >= y && p.x < x + w && p.y < y + h) {
1179
+ return true;
1180
+ }
1181
+ });
1182
+ }
1183
+ function findRelativeNodeById(id, layoutNodes) {
1184
+ return visit(layoutNodes, (node)=>{
1185
+ if (node.node.id === id) {
1186
+ return true;
1187
+ }
1188
+ });
1189
+ }
1190
+
1191
+ // Because of the cache system. scale factor is not needed to apply.
1192
+ // Eg. Only use scale factor when the layout is changed.
1193
+ function createTreemapEventState() {
1194
+ return {
1195
+ isDragging: false,
1196
+ isWheeling: false,
1197
+ isZooming: false,
1198
+ currentNode: null,
1199
+ forceDestroy: false,
1200
+ dragX: 0,
1201
+ dragY: 0
1202
+ };
1203
+ }
1204
+ const INTERNAL_EVENT_MAPPINGS = {
1205
+ ON_ZOOM: 0o1,
1206
+ ON_CLEANUP: 0o3
1207
+ };
1208
+ const ANIMATION_DURATION = 300;
1209
+ const fill = {
1210
+ desc: {
1211
+ r: 255,
1212
+ g: 255,
1213
+ b: 255
1214
+ },
1215
+ mode: 'rgb'
1216
+ };
1217
+ function smoothFrame(callback, opts) {
1218
+ const frame = createSmoothFrame();
1219
+ const startTime = Date.now();
1220
+ const condtion = (process)=>{
1221
+ if (Array.isArray(opts.deps)) {
1222
+ return opts.deps.some((dep)=>dep());
1223
+ }
1224
+ return process >= 1;
1225
+ };
1226
+ frame.run(()=>{
1227
+ const elapsed = Date.now() - startTime;
1228
+ const progress = Math.min(elapsed / opts.duration, 1);
1229
+ if (condtion(progress)) {
1230
+ frame.stop();
1231
+ if (opts.onStop) {
1232
+ opts.onStop();
1233
+ }
1234
+ return true;
1235
+ }
1236
+ return callback(progress, frame.stop);
1237
+ });
1238
+ }
1239
+ const HIGH_LIGHT_OPACITY = 0.3;
1240
+ function drawHighlight(treemap, evt) {
1241
+ const { highlight } = treemap;
1242
+ const { currentNode } = evt.state;
1243
+ if (currentNode) {
1244
+ const [x, y, w, h] = currentNode.layout;
1245
+ smoothFrame((_, cleanup)=>{
1246
+ cleanup();
1247
+ highlight.reset();
1248
+ const mask = createRoundBlock(x, y, w, h, {
1249
+ fill,
1250
+ opacity: HIGH_LIGHT_OPACITY,
1251
+ radius: 4,
1252
+ padding: 2
1253
+ });
1254
+ highlight.add(mask);
1255
+ highlight.setZIndexForHighlight('1');
1256
+ stackMatrixTransform(mask, evt.matrix.e, evt.matrix.f, 1);
1257
+ highlight.update();
1258
+ if (!evt.state.currentNode) {
1259
+ return true;
1260
+ }
1261
+ }, {
1262
+ duration: ANIMATION_DURATION,
1263
+ deps: [
1264
+ ()=>evt.state.isDragging,
1265
+ ()=>evt.state.isWheeling,
1266
+ ()=>evt.state.isZooming
1267
+ ]
1268
+ });
1269
+ } else {
1270
+ highlight.reset();
1271
+ highlight.setZIndexForHighlight();
1272
+ }
1273
+ }
1274
+ // TODO: Do we need turn off internal events?
1275
+ class TreemapEvent extends DOMEvent {
1276
+ exposedEvent;
1277
+ state;
1278
+ zoom;
1279
+ constructor(app, treemap){
1280
+ super(treemap.render.canvas);
1281
+ this.exposedEvent = new Event();
1282
+ this.state = createTreemapEventState();
1283
+ const exposedMethods = [
1284
+ {
1285
+ name: 'on',
1286
+ fn: ()=>this.exposedEvent.bindWithContext(treemap.api)
1287
+ },
1288
+ {
1289
+ name: 'off',
1290
+ fn: ()=>this.exposedEvent.off.bind(this.exposedEvent)
1291
+ }
1292
+ ];
1293
+ DOM_EVENTS.forEach((evt)=>{
1294
+ this.on(evt, (metadata)=>{
1295
+ // if (evt === 'wheel' && macOS) {
1296
+ // this.dispatch({ type: 'macOSWheel', treemap }, metadata)
1297
+ // return
1298
+ // }
1299
+ this.dispatch({
1300
+ type: evt,
1301
+ treemap
1302
+ }, metadata);
1303
+ });
1304
+ });
1305
+ mixin(app, exposedMethods);
1306
+ treemap.event.on(INTERNAL_EVENT_MAPPINGS.ON_CLEANUP, ()=>{
1307
+ this.matrix.create({
1308
+ a: 1,
1309
+ b: 0,
1310
+ c: 0,
1311
+ d: 1,
1312
+ e: 0,
1313
+ f: 0
1314
+ });
1315
+ this.state = createTreemapEventState();
1316
+ });
1317
+ this.zoom = createOnZoom(treemap, this);
1318
+ treemap.event.on(INTERNAL_EVENT_MAPPINGS.ON_ZOOM, this.zoom);
1319
+ }
1320
+ dispatch(ctx, metadata) {
1321
+ const node = findRelativeNode(metadata.loc, ctx.treemap.layoutNodes);
1322
+ const fn = prettyStrJoin('on', ctx.type);
1323
+ // @ts-expect-error safe
1324
+ if (typeof this[fn] === 'function') {
1325
+ // @ts-expect-error safe
1326
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
1327
+ this[fn](ctx, metadata, node);
1328
+ }
1329
+ // note: onmouseup event will trigger click event together
1330
+ if (ctx.type === 'mousemove') {
1331
+ if (this.state.isDragging) {
1332
+ this.exposedEvent.silent('click');
1333
+ } else {
1334
+ this.exposedEvent.active('click');
1335
+ }
1336
+ }
1337
+ // For macOS
1338
+ this.exposedEvent.emit(ctx.type === 'macOSWheel' ? 'wheel' : ctx.type, {
1339
+ native: metadata.native,
1340
+ module: node
1341
+ });
1342
+ }
1343
+ onmousemove(ctx, metadata, node) {
1344
+ if (!this.state.isDragging) {
1345
+ if (this.state.currentNode !== node || !node) {
1346
+ this.state.currentNode = node;
1347
+ }
1348
+ drawHighlight(ctx.treemap, this);
1349
+ } else {
1350
+ // for drag
1351
+ const { treemap } = ctx;
1352
+ smoothFrame((_, cleanup)=>{
1353
+ cleanup();
1354
+ const { offsetX: x, offsetY: y } = metadata.native;
1355
+ const { dragX: lastX, dragY: lastY } = this.state;
1356
+ const drawX = x - lastX;
1357
+ const drawY = y - lastY;
1358
+ treemap.highlight.reset();
1359
+ treemap.highlight.setZIndexForHighlight();
1360
+ treemap.reset();
1361
+ this.matrix.translation(drawX, drawY);
1362
+ Object.assign(this.state, {
1363
+ isDragging: true,
1364
+ dragX: x,
1365
+ dragY: y
1366
+ });
1367
+ stackMatrixTransformWithGraphAndLayer(treemap.elements, this.matrix.e, this.matrix.f, 1);
1368
+ treemap.update();
1369
+ return true;
1370
+ }, {
1371
+ duration: ANIMATION_DURATION,
1372
+ deps: [
1373
+ ()=>this.state.forceDestroy
1374
+ ],
1375
+ onStop: ()=>{
1376
+ this.state.isDragging = false;
1377
+ }
1378
+ });
1379
+ }
1380
+ }
1381
+ onmouseout(ctx) {
1382
+ this.state.currentNode = null;
1383
+ drawHighlight(ctx.treemap, this);
1384
+ }
1385
+ onmousedown(ctx, metadata) {
1386
+ if (isScrollWheelOrRightButtonOnMouseupAndDown(metadata.native)) {
1387
+ return;
1388
+ }
1389
+ this.state.isDragging = true;
1390
+ this.state.dragX = metadata.native.offsetX;
1391
+ this.state.dragY = metadata.native.offsetY;
1392
+ this.state.forceDestroy = false;
1393
+ if (!ctx.treemap.renderCache.state) {
1394
+ this.exposedEvent.silent('mousemove');
1395
+ this.silent('mousemove');
1396
+ ctx.treemap.renderCache.flush(ctx.treemap, this.matrix);
1397
+ this.active('mousemove');
1398
+ this.exposedEvent.active('mousemove');
1399
+ }
1400
+ }
1401
+ onmouseup(ctx) {
1402
+ if (!this.state.isDragging) {
1403
+ return;
1404
+ }
1405
+ this.state.forceDestroy = true;
1406
+ this.state.isDragging = false;
1407
+ const { treemap } = ctx;
1408
+ treemap.highlight.reset();
1409
+ treemap.highlight.setZIndexForHighlight();
1410
+ }
1411
+ onwheel(ctx, metadata) {
1412
+ ctx.treemap.renderCache.destroy();
1413
+ ctx.treemap.event.silent(INTERNAL_EVENT_MAPPINGS.ON_ZOOM);
1414
+ const { native } = metadata;
1415
+ const { treemap } = ctx;
1416
+ // @ts-expect-error safe
1417
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1418
+ const wheelDelta = native.wheelDelta;
1419
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1420
+ const absWheelDelta = Math.abs(wheelDelta);
1421
+ const offsetX = native.offsetX;
1422
+ const offsetY = native.offsetY;
1423
+ if (wheelDelta === 0) {
1424
+ return;
1425
+ }
1426
+ this.state.forceDestroy = true;
1427
+ const factor = absWheelDelta > 3 ? 1.4 : absWheelDelta > 1 ? 1.2 : 1.1;
1428
+ const delta = wheelDelta > 0 ? factor : 1 / factor;
1429
+ const targetScaleRatio = this.matrix.a * delta;
1430
+ const translateX = offsetX - (offsetX - this.matrix.e) * delta;
1431
+ const translateY = offsetY - (offsetY - this.matrix.f) * delta;
1432
+ smoothFrame((progress, cleanup)=>{
1433
+ this.exposedEvent.silent('mousemove');
1434
+ this.silent('mousemove');
1435
+ this.silent('click');
1436
+ this.exposedEvent.silent('click');
1437
+ treemap.highlight.reset();
1438
+ treemap.highlight.setZIndexForHighlight();
1439
+ treemap.fontCache.flush(treemap, this.matrix);
1440
+ this.state.isWheeling = true;
1441
+ const easedProgress = easing.cubicInOut(progress);
1442
+ const scale = (targetScaleRatio - this.matrix.a) * easedProgress;
1443
+ this.matrix.a += scale;
1444
+ this.matrix.d += scale;
1445
+ this.matrix.translation((translateX - this.matrix.e) * easedProgress, (translateY - this.matrix.f) * easedProgress);
1446
+ // Each shape will scale by schedule phase. (According the dpi)
1447
+ // So that if the DPI is more than 1. we need to shrink first. ( w / dpi , h / dpi )
1448
+ // Schedule will scale it back to the original size.
1449
+ resetLayout(treemap, treemap.render.canvas.width * this.matrix.a / treemap.render.options.devicePixelRatio, treemap.render.canvas.height * this.matrix.d / treemap.render.options.devicePixelRatio);
1450
+ stackMatrixTransformWithGraphAndLayer(treemap.elements, this.matrix.e, this.matrix.f, 1);
1451
+ treemap.update();
1452
+ cleanup();
1453
+ }, {
1454
+ duration: ANIMATION_DURATION,
1455
+ onStop: ()=>{
1456
+ this.state.forceDestroy = false;
1457
+ this.state.isWheeling = false;
1458
+ // this.active('mousedown')
1459
+ this.active('mousemove');
1460
+ this.exposedEvent.active('mousemove');
1461
+ this.active('click');
1462
+ this.exposedEvent.active('click');
1463
+ treemap.event.active(INTERNAL_EVENT_MAPPINGS.ON_ZOOM);
1464
+ }
1465
+ });
1466
+ }
1467
+ onmacOSWheel(ctx, metadata) {
1468
+ useMagicTrackPad(metadata.native);
1469
+ }
1470
+ }
1471
+ function stackMatrixTransform(graph, e, f, scale) {
1472
+ graph.x = graph.x * scale + e;
1473
+ graph.y = graph.y * scale + f;
1474
+ graph.scaleX = scale;
1475
+ graph.scaleY = scale;
1476
+ }
1477
+ function stackMatrixTransformWithGraphAndLayer(graphs, e, f, scale) {
1478
+ traverse(graphs, (graph)=>stackMatrixTransform(graph, e, f, scale));
1479
+ }
1480
+ // Only works for mouseup and mousedown events
1481
+ function isScrollWheelOrRightButtonOnMouseupAndDown(e) {
1482
+ return e.which === 2 || e.which === 3;
1483
+ }
1484
+ function createOnZoom(treemap, evt) {
1485
+ return (node)=>{
1486
+ treemap.renderCache.destroy();
1487
+ evt.state.isZooming = true;
1488
+ const c = treemap.render.canvas;
1489
+ const boundingClientRect = c.getBoundingClientRect();
1490
+ if (node) {
1491
+ const [mx, my, mw, mh] = node.layout;
1492
+ const factor = Math.min(boundingClientRect.width / mw, boundingClientRect.height / mh);
1493
+ const targetScale = factor * evt.matrix.a;
1494
+ const translateX = boundingClientRect.width / 2 - (mx + mw / 2) * factor;
1495
+ const translateY = boundingClientRect.height / 2 - (my + mh / 2) * factor;
1496
+ smoothFrame((progress, cleanup)=>{
1497
+ cleanup();
1498
+ evt.silent('mousemove');
1499
+ evt.exposedEvent.silent('mousemove');
1500
+ treemap.fontCache.flush(treemap, evt.matrix);
1501
+ const easedProgress = easing.cubicInOut(progress);
1502
+ const scale = (targetScale - evt.matrix.a) * easedProgress;
1503
+ evt.matrix.a += scale;
1504
+ evt.matrix.d += scale;
1505
+ evt.matrix.translation((translateX - evt.matrix.e) * easedProgress, (translateY - evt.matrix.f) * easedProgress);
1506
+ resetLayout(treemap, treemap.render.canvas.width * evt.matrix.a / treemap.render.options.devicePixelRatio, treemap.render.canvas.height * evt.matrix.d / treemap.render.options.devicePixelRatio);
1507
+ stackMatrixTransformWithGraphAndLayer(treemap.elements, evt.matrix.e, evt.matrix.f, 1);
1508
+ treemap.update();
1509
+ }, {
1510
+ duration: ANIMATION_DURATION,
1511
+ onStop: ()=>{
1512
+ evt.state.isZooming = false;
1513
+ evt.active('mousemove');
1514
+ evt.exposedEvent.active('mousemove');
1515
+ }
1516
+ });
1517
+ }
1518
+ };
1519
+ }
1520
+
1521
+ function register(Mod) {
1522
+ return (app, treemap)=>{
1523
+ new Mod(app, treemap);
1524
+ };
1525
+ }
1526
+
1527
+ function squarify(data, rect, layoutDecorator) {
1528
+ const result = [];
1529
+ if (!data.length) {
1530
+ return result;
1531
+ }
1532
+ const worst = (start, end, shortestSide, totalWeight, aspectRatio)=>{
1533
+ const max = data[start].weight * aspectRatio;
1534
+ const min = data[end].weight * aspectRatio;
1535
+ return Math.max(shortestSide * shortestSide * max / (totalWeight * totalWeight), totalWeight * totalWeight / (shortestSide * shortestSide * min));
1536
+ };
1537
+ const recursion = (start, rect)=>{
1538
+ while(start < data.length){
1539
+ let totalWeight = 0;
1540
+ for(let i = start; i < data.length; i++){
1541
+ totalWeight += data[i].weight;
1542
+ }
1543
+ const shortestSide = Math.min(rect.w, rect.h);
1544
+ const aspectRatio = rect.w * rect.h / totalWeight;
1545
+ let end = start;
1546
+ let areaInRun = 0;
1547
+ let oldWorst = 0;
1548
+ // find the best split
1549
+ while(end < data.length){
1550
+ const area = data[end].weight * aspectRatio;
1551
+ const newWorst = worst(start, end, shortestSide, areaInRun + area, aspectRatio);
1552
+ if (end > start && oldWorst < newWorst) {
1553
+ break;
1554
+ }
1555
+ areaInRun += area;
1556
+ oldWorst = newWorst;
1557
+ end++;
1558
+ }
1559
+ const splited = Math.round(areaInRun / shortestSide);
1560
+ let areaInLayout = 0;
1561
+ for(let i = start; i < end; i++){
1562
+ const children = data[i];
1563
+ const area = children.weight * aspectRatio;
1564
+ const lower = Math.round(shortestSide * areaInLayout / areaInRun);
1565
+ const upper = Math.round(shortestSide * (areaInLayout + area) / areaInRun);
1566
+ const [x, y, w, h] = rect.w >= rect.h ? [
1567
+ rect.x,
1568
+ rect.y + lower,
1569
+ splited,
1570
+ upper - lower
1571
+ ] : [
1572
+ rect.x + lower,
1573
+ rect.y,
1574
+ upper - lower,
1575
+ splited
1576
+ ];
1577
+ const depth = getNodeDepth(children) || 1;
1578
+ const { titleAreaHeight, rectGap } = layoutDecorator;
1579
+ const diff = titleAreaHeight.max / depth;
1580
+ const hh = diff < titleAreaHeight.min ? titleAreaHeight.min : diff;
1581
+ result.push({
1582
+ layout: [
1583
+ x,
1584
+ y,
1585
+ w,
1586
+ h
1587
+ ],
1588
+ node: children,
1589
+ decorator: {
1590
+ ...layoutDecorator,
1591
+ titleHeight: hh
1592
+ },
1593
+ children: w > rectGap * 2 && h > hh + rectGap ? squarify(children.groups || [], {
1594
+ x: x + rectGap,
1595
+ y: y + hh,
1596
+ w: w - rectGap * 2,
1597
+ h: h - hh - rectGap
1598
+ }, layoutDecorator) : []
1599
+ });
1600
+ areaInLayout += area;
1601
+ }
1602
+ start = end;
1603
+ if (rect.w >= rect.h) {
1604
+ rect.x += splited;
1605
+ rect.w -= splited;
1606
+ } else {
1607
+ rect.y += splited;
1608
+ rect.h -= splited;
1609
+ }
1610
+ }
1611
+ };
1612
+ recursion(0, rect);
1613
+ return result;
1614
+ }
1615
+
1616
+ function measureTextWidth(c, text) {
1617
+ return c.measureText(text).width;
1618
+ }
1619
+ function evaluateOptimalFontSize(c, text, font, desiredW, desiredH) {
1620
+ desiredW = Math.floor(desiredW);
1621
+ desiredH = Math.floor(desiredH);
1622
+ const { range, family } = font;
1623
+ let min = range.min;
1624
+ let max = range.max;
1625
+ const cache = new Map();
1626
+ while(max - min >= 1){
1627
+ const current = min + (max - min) / 2;
1628
+ if (!cache.has(current)) {
1629
+ c.font = `${current}px ${family}`;
1630
+ const metrics = c.measureText(text);
1631
+ const width = metrics.width;
1632
+ const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
1633
+ cache.set(current, {
1634
+ width,
1635
+ height
1636
+ });
1637
+ }
1638
+ const { width, height } = cache.get(current);
1639
+ if (width > desiredW || height > desiredH) {
1640
+ max = current;
1641
+ } else {
1642
+ min = current;
1643
+ }
1644
+ }
1645
+ return Math.floor(min);
1646
+ }
1647
+ function getSafeText(c, text, width, cache) {
1648
+ let ellipsisWidth = 0;
1649
+ if (text in cache) {
1650
+ ellipsisWidth = cache[text];
1651
+ } else {
1652
+ ellipsisWidth = measureTextWidth(c, '...');
1653
+ cache[text] = ellipsisWidth;
1654
+ }
1655
+ if (width < ellipsisWidth) {
1656
+ return false;
1657
+ }
1658
+ const textWidth = measureTextWidth(c, text);
1659
+ if (textWidth < width) {
1660
+ return {
1661
+ text,
1662
+ width: textWidth
1663
+ };
1664
+ }
1665
+ return {
1666
+ text: '...',
1667
+ width: ellipsisWidth
1668
+ };
1669
+ }
1670
+ function resetLayout(treemap, w, h) {
1671
+ treemap.layoutNodes = squarify(treemap.data, {
1672
+ w,
1673
+ h,
1674
+ x: 0,
1675
+ y: 0
1676
+ }, treemap.decorator.layout);
1677
+ treemap.reset(true);
1678
+ }
1679
+ class Highlight extends Schedule {
1680
+ reset() {
1681
+ this.destory();
1682
+ this.update();
1683
+ }
1684
+ get canvas() {
1685
+ return this.render.canvas;
1686
+ }
1687
+ setZIndexForHighlight(zIndex = '-1') {
1688
+ this.canvas.style.zIndex = zIndex;
1689
+ }
1690
+ init() {
1691
+ this.setZIndexForHighlight();
1692
+ this.canvas.style.position = 'absolute';
1693
+ this.canvas.style.pointerEvents = 'none';
1694
+ }
1695
+ }
1696
+ class TreemapLayout extends Schedule {
1697
+ data;
1698
+ layoutNodes;
1699
+ decorator;
1700
+ bgBox;
1701
+ fgBox;
1702
+ highlight;
1703
+ renderCache;
1704
+ fontCache;
1705
+ constructor(...args){
1706
+ super(...args);
1707
+ this.data = [];
1708
+ this.layoutNodes = [];
1709
+ this.bgBox = new Box();
1710
+ this.fgBox = new Box();
1711
+ this.decorator = Object.create(null);
1712
+ this.highlight = new Highlight(this.to, {
1713
+ width: this.render.options.width,
1714
+ height: this.render.options.height
1715
+ });
1716
+ this.renderCache = new RenderCache(this.render.options);
1717
+ this.fontCache = new FontCache();
1718
+ }
1719
+ drawBackgroundNode(node) {
1720
+ const [x, y, w, h] = node.layout;
1721
+ const padding = 2;
1722
+ if (w - padding * 2 <= 0 || h - padding * 2 <= 0) {
1723
+ return;
1724
+ }
1725
+ const fill = this.decorator.color.mappings[node.node.id];
1726
+ const rect = createRoundBlock(x, y, w, h, {
1727
+ fill,
1728
+ padding,
1729
+ radius: 4
1730
+ });
1731
+ rect.__widget__ = node;
1732
+ this.bgBox.add(rect);
1733
+ for (const child of node.children){
1734
+ this.drawBackgroundNode(child);
1735
+ }
1736
+ }
1737
+ drawForegroundNode(node) {
1738
+ const [x, y, w, h] = node.layout;
1739
+ if (!w || !h) {
1740
+ return;
1741
+ }
1742
+ const { titleHeight, rectGap } = node.decorator;
1743
+ const { fontSize, fontFamily, color } = this.decorator.font;
1744
+ const optimalFontSize = this.fontCache.queryFontById(node.node.id, ()=>evaluateOptimalFontSize(this.render.ctx, // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1745
+ node.node.label, {
1746
+ range: fontSize,
1747
+ family: fontFamily
1748
+ }, w - rectGap * 2, node.children.length ? Math.round(titleHeight / 2) + rectGap : h));
1749
+ this.render.ctx.font = `${optimalFontSize}px ${fontFamily}`;
1750
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1751
+ const result = getSafeText(this.render.ctx, node.node.label, w - rectGap * 2, this.fontCache.ellispsis);
1752
+ if (!result) {
1753
+ return;
1754
+ }
1755
+ if (result.width >= w || optimalFontSize >= h) {
1756
+ return;
1757
+ }
1758
+ const { text, width } = result;
1759
+ const textX = x + Math.round((w - width) / 2);
1760
+ const textY = y + (node.children.length ? Math.round(titleHeight / 2) : Math.round(h / 2));
1761
+ this.fgBox.add(createTitleText(text, textX, textY, `${optimalFontSize}px ${fontFamily}`, color));
1762
+ for (const child of node.children){
1763
+ this.drawForegroundNode(child);
1764
+ }
1765
+ }
1766
+ reset(refresh = false) {
1767
+ this.remove(this.bgBox, this.fgBox);
1768
+ this.bgBox.destory();
1769
+ if (this.renderCache.state) {
1770
+ this.fgBox.destory();
1771
+ this.bgBox.add(new Bitmap({
1772
+ bitmap: this.renderCache.canvas,
1773
+ dpi: this.render.options.devicePixelRatio
1774
+ }));
1775
+ } else {
1776
+ for (const node of this.layoutNodes){
1777
+ this.drawBackgroundNode(node);
1778
+ }
1779
+ if (!this.fgBox.elements.length || refresh) {
1780
+ this.render.ctx.textBaseline = 'middle';
1781
+ this.fgBox.destory();
1782
+ for (const node of this.layoutNodes){
1783
+ this.drawForegroundNode(node);
1784
+ }
1785
+ } else {
1786
+ this.fgBox = this.fgBox.clone();
1787
+ }
1788
+ }
1789
+ this.add(this.bgBox, this.fgBox);
1790
+ }
1791
+ get api() {
1792
+ return {
1793
+ zoom: (node)=>{
1794
+ if (!node) {
1795
+ return;
1796
+ }
1797
+ this.event.emit(INTERNAL_EVENT_MAPPINGS.ON_ZOOM, node);
1798
+ }
1799
+ };
1800
+ }
1801
+ }
1802
+ function createTreemap() {
1803
+ let treemap = null;
1804
+ let root = null;
1805
+ let installed = false;
1806
+ const uses = [];
1807
+ const context = {
20
1808
  init,
21
1809
  dispose,
1810
+ setOptions,
22
1811
  resize,
23
- setOptions
1812
+ use,
1813
+ zoom
24
1814
  };
25
1815
  function init(el) {
26
- component = new domEvent.Component(graphic, el);
27
- domEvent$1 = new domEvent.DOMEvent(component);
1816
+ treemap = new TreemapLayout(el);
28
1817
  root = el;
29
1818
  root.style.position = 'relative';
30
1819
  if (!installed) {
31
- plugins.forEach((plugin)=>component?.pluginDriver.use(plugin));
1820
+ register(TreemapEvent)(context, treemap);
32
1821
  installed = true;
33
- component.pluginDriver.runHook('onLoad', ctx, domEvent$1);
34
1822
  }
35
- domEvent$1.on('__exposed__', (type, args)=>exposedEvent.emit(type, args));
36
1823
  }
37
1824
  function dispose() {
38
- if (root && component && domEvent$1) {
39
- domEvent$1.destory();
40
- component.destory();
1825
+ if (root && treemap) {
1826
+ treemap.destory();
41
1827
  root.removeChild(root.firstChild);
42
- for(const evt in exposedEvent.eventCollections){
43
- exposedEvent.off(evt);
44
- }
45
- component.pluginDriver.runHook('onDispose');
46
1828
  root = null;
47
- component = null;
48
- domEvent$1 = null;
1829
+ treemap = null;
49
1830
  }
50
1831
  }
51
1832
  function resize() {
52
- if (!component || !root) {
1833
+ if (!treemap || !root) {
53
1834
  return;
54
1835
  }
1836
+ treemap.renderCache.destroy();
1837
+ treemap.fontCache.destroy();
55
1838
  const { width, height } = root.getBoundingClientRect();
56
- component.render.initOptions({
1839
+ treemap.render.initOptions({
57
1840
  height,
58
1841
  width,
59
1842
  devicePixelRatio: window.devicePixelRatio
60
1843
  });
61
- component.render.canvas.style.position = 'absolute';
62
- if (domEvent$1) {
63
- component.pluginDriver.runHook('onResize', domEvent$1);
64
- }
65
- component.cleanup();
66
- component.draw();
1844
+ treemap.render.canvas.style.position = 'absolute';
1845
+ treemap.event.emit(INTERNAL_EVENT_MAPPINGS.ON_CLEANUP);
1846
+ treemap.highlight.render.initOptions({
1847
+ height,
1848
+ width,
1849
+ devicePixelRatio: window.devicePixelRatio
1850
+ });
1851
+ treemap.highlight.reset();
1852
+ treemap.highlight.init();
1853
+ resetLayout(treemap, width, height);
1854
+ treemap.update();
1855
+ treemap.renderCache.flush(treemap, treemap.matrix);
67
1856
  }
68
1857
  function setOptions(options) {
69
- domEvent.assertExists(component, domEvent.logger, 'Treemap not initialized. Please call `init()` before setOptions.');
70
- component.data = domEvent.bindParentForModule(options.data);
1858
+ if (!treemap) {
1859
+ throw new Error(log.error('Treemap not initialized'));
1860
+ }
1861
+ treemap.data = bindParentForModule(options.data || []);
1862
+ for (const use of uses){
1863
+ use(treemap);
1864
+ }
71
1865
  resize();
72
1866
  }
73
- const base = domEvent.mixin(ctx, [
74
- {
75
- name: 'on',
76
- fn: ()=>exposedEvent.bindWithContext(api)
77
- },
78
- {
79
- name: 'off',
80
- fn: ()=>exposedEvent.off.bind(exposedEvent)
81
- }
82
- ]);
83
- return base;
84
- }
85
-
86
- exports.DefaultMap = domEvent.DefaultMap;
87
- exports.applyCanvasTransform = domEvent.applyCanvasTransform;
88
- exports.c2m = domEvent.c2m;
89
- exports.createCanvasElement = domEvent.createCanvasElement;
90
- exports.createRoundBlock = domEvent.createRoundBlock;
91
- exports.createTitleText = domEvent.createTitleText;
92
- exports.definePlugin = domEvent.definePlugin;
93
- exports.findRelativeNode = domEvent.findRelativeNode;
94
- exports.findRelativeNodeById = domEvent.findRelativeNodeById;
95
- exports.flattenModule = domEvent.flatten;
96
- exports.getNodeDepth = domEvent.getNodeDepth;
97
- exports.hashCode = domEvent.hashCode;
98
- exports.isClickEvent = domEvent.isClickEvent;
99
- exports.isContextMenuEvent = domEvent.isContextMenuEvent;
100
- exports.isMacOS = domEvent.isMacOS;
101
- exports.isMouseEvent = domEvent.isMouseEvent;
102
- exports.isScrollWheelOrRightButtonOnMouseupAndDown = domEvent.isScrollWheelOrRightButtonOnMouseupAndDown;
103
- exports.isWheelEvent = domEvent.isWheelEvent;
104
- exports.mixin = domEvent.mixin;
105
- exports.mixinWithParams = domEvent.mixinWithParams;
106
- exports.noop = domEvent.noop;
107
- exports.perferNumeric = domEvent.perferNumeric;
108
- exports.prettyStrJoin = domEvent.prettyStrJoin;
109
- exports.raf = domEvent.raf;
110
- exports.smoothFrame = domEvent.smoothFrame;
111
- exports.sortChildrenByKey = domEvent.sortChildrenByKey;
112
- exports.stackMatrixTransform = domEvent.stackMatrixTransform;
113
- exports.stackMatrixTransformWithGraphAndLayer = domEvent.stackMatrixTransformWithGraphAndLayer;
114
- exports.typedForIn = domEvent.typedForIn;
115
- exports.visit = domEvent.visit;
1867
+ function use(key, register) {
1868
+ switch(key){
1869
+ case 'decorator':
1870
+ uses.push((treemap)=>register(treemap));
1871
+ break;
1872
+ }
1873
+ }
1874
+ function zoom(id) {
1875
+ if (!treemap) {
1876
+ throw new Error(log.error("treemap don't init."));
1877
+ }
1878
+ const node = findRelativeNodeById(id, treemap.layoutNodes);
1879
+ if (node) {
1880
+ treemap.api.zoom(node);
1881
+ }
1882
+ }
1883
+ return context;
1884
+ }
1885
+
1886
+ const defaultLayoutOptions = {
1887
+ titleAreaHeight: {
1888
+ max: 60,
1889
+ min: 30
1890
+ },
1891
+ rectGap: 5,
1892
+ rectBorderRadius: 0.5,
1893
+ rectBorderWidth: 1.5
1894
+ };
1895
+ const defaultFontOptions = {
1896
+ color: '#000',
1897
+ fontSize: {
1898
+ max: 70,
1899
+ min: 0
1900
+ },
1901
+ fontFamily: 'sans-serif'
1902
+ };
1903
+ function presetDecorator(app) {
1904
+ Object.assign(app.decorator, {
1905
+ layout: defaultLayoutOptions,
1906
+ font: defaultFontOptions,
1907
+ color: {
1908
+ mappings: evaluateColorMappings(app.data)
1909
+ }
1910
+ });
1911
+ }
1912
+ function evaluateColorMappings(data) {
1913
+ const colorMappings = {};
1914
+ const hashToHue = (id)=>{
1915
+ const hash = Math.abs(hashCode(id));
1916
+ return hash % 360;
1917
+ };
1918
+ const lightScale = (depth)=>50 - depth * 5;
1919
+ const baseSaturation = 80;
1920
+ const siblingHueShift = 30;
1921
+ const rc = 0.2126;
1922
+ const gc = 0.7152;
1923
+ const bc = 0.0722;
1924
+ const hslToRgb = (h, s, l)=>{
1925
+ const a = s * Math.min(l, 1 - l);
1926
+ const f = (n)=>{
1927
+ const k = (n + h / 30) % 12;
1928
+ return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
1929
+ };
1930
+ return {
1931
+ r: f(0),
1932
+ g: f(8),
1933
+ b: f(4)
1934
+ };
1935
+ };
1936
+ const calculateLuminance = (r, g, b)=>{
1937
+ return rc * r + gc * g + bc * b;
1938
+ };
1939
+ const calculateColor = (module, depth, parentHue, siblingIndex, totalSiblings)=>{
1940
+ const nodeHue = hashToHue(module.id);
1941
+ const hue = parentHue !== null ? (parentHue + siblingHueShift * siblingIndex / totalSiblings) % 360 : nodeHue;
1942
+ const lightness = lightScale(depth);
1943
+ const hslColor = {
1944
+ h: hue,
1945
+ s: baseSaturation,
1946
+ l: lightness / 100
1947
+ };
1948
+ const { r, g, b } = hslToRgb(hslColor.h, hslColor.s / 100, hslColor.l);
1949
+ const luminance = calculateLuminance(r, g, b);
1950
+ if (luminance < 0.6) {
1951
+ hslColor.l += 0.2;
1952
+ } else if (luminance > 0.8) {
1953
+ hslColor.l -= 0.1;
1954
+ }
1955
+ hslColor.l *= 100;
1956
+ colorMappings[module.id] = {
1957
+ mode: 'hsl',
1958
+ desc: hslColor
1959
+ };
1960
+ if (module.groups && Array.isArray(module.groups)) {
1961
+ const totalChildren = module.groups.length;
1962
+ for(let i = 0; i < totalChildren; i++){
1963
+ const child = module.groups[i];
1964
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1965
+ calculateColor(child, depth + 1, hue, i, totalChildren);
1966
+ }
1967
+ }
1968
+ };
1969
+ for(let i = 0; i < data.length; i++){
1970
+ const module = data[i];
1971
+ calculateColor(module, 0, null, i, data.length);
1972
+ }
1973
+ return colorMappings;
1974
+ }
1975
+
1976
+ exports.TreemapLayout = TreemapLayout;
1977
+ exports.c2m = c2m;
116
1978
  exports.createTreemap = createTreemap;
1979
+ exports.defaultFontOptions = defaultFontOptions;
1980
+ exports.defaultLayoutOptions = defaultLayoutOptions;
1981
+ exports.findRelativeNode = findRelativeNode;
1982
+ exports.findRelativeNodeById = findRelativeNodeById;
1983
+ exports.flattenModule = flatten;
1984
+ exports.getNodeDepth = getNodeDepth;
1985
+ exports.presetDecorator = presetDecorator;
1986
+ exports.sortChildrenByKey = sortChildrenByKey;
1987
+ exports.visit = visit;