squarified 0.5.0 → 0.6.1

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/README.md CHANGED
@@ -1,25 +1,250 @@
1
1
  # Squarified
2
2
 
3
- `squarified` is a minimal and powerful treemap component.
3
+ A minimal and powerful treemap visualization library for creating interactive hierarchical data visualizations.
4
4
 
5
- ## Install
5
+ ## Features
6
+
7
+ - 🚀 **Lightweight**: Minimal bundle size with maximum performance
8
+ - 🎨 **Customizable**: Rich theming and styling options
9
+ - 🔌 **Plugin System**: Extensible architecture with built-in plugins
10
+ - 📱 **Responsive**: Automatic resizing
11
+ - ⚡ **Interactive**: Built-in zoom, drag, highlight, and menu interactions
12
+ - 🎯 **TypeScript**: Full type safety and excellent DX
13
+
14
+ ## Installation
6
15
 
7
16
  ```shell
8
- $ yarn add squarified
17
+ npm install squarified
18
+ # or
19
+ yarn add squarified
20
+ # or
21
+ pnpm add squarified
9
22
  ```
10
23
 
11
- ### Document
24
+ ## Quick Start
12
25
 
13
- nonzzz.github.io/squarified/
26
+ ```typescript
27
+ import { c2m, createTreemap, sortChildrenByKey } from 'squarified'
14
28
 
15
- ### Auth
29
+ // Create a treemap instance
30
+ const treemap = createTreemap({
31
+ plugins: [
32
+ // Add built-in plugins for interactions
33
+ ]
34
+ })
16
35
 
17
- Kanno
36
+ // Initialize with a DOM element
37
+ const container = document.querySelector('#treemap-container')
38
+ treemap.init(container)
39
+
40
+ // Set your data
41
+ treemap.setOptions({
42
+ data: [
43
+ {
44
+ id: 'root',
45
+ label: 'Root',
46
+ weight: 100,
47
+ groups: [
48
+ { id: 'child1', label: 'Child 1', weight: 60 },
49
+ { id: 'child2', label: 'Child 2', weight: 40 }
50
+ ]
51
+ }
52
+ ]
53
+ })
54
+ ```
55
+
56
+ ## Complete Example
57
+
58
+ ```typescript
59
+ import {
60
+ c2m,
61
+ createTreemap,
62
+ presetColorPlugin,
63
+ presetDragElementPlugin,
64
+ presetHighlightPlugin,
65
+ presetMenuPlugin,
66
+ presetScalePlugin,
67
+ presetZoomablePlugin,
68
+ sortChildrenByKey
69
+ } from 'squarified'
70
+
71
+ // Create treemap with plugins
72
+ const treemap = createTreemap({
73
+ plugins: [
74
+ presetColorPlugin,
75
+ presetZoomablePlugin,
76
+ presetHighlightPlugin,
77
+ presetDragElementPlugin,
78
+ presetScalePlugin(),
79
+ presetMenuPlugin({
80
+ style: {
81
+ borderRadius: '5px',
82
+ padding: '6px 12px',
83
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
84
+ color: 'white',
85
+ cursor: 'pointer',
86
+ userSelect: 'none'
87
+ },
88
+ render: () => [
89
+ { html: '<span>🔍 Zoom In</span>', action: 'zoom' },
90
+ { html: '<span>🔄 Reset</span>', action: 'reset' }
91
+ ],
92
+ onClick(action, module) {
93
+ switch (action) {
94
+ case 'zoom':
95
+ if (module?.node.id) {
96
+ treemap.zoom(module.node.id)
97
+ }
98
+ break
99
+ case 'reset':
100
+ treemap.resize()
101
+ break
102
+ }
103
+ }
104
+ })
105
+ ]
106
+ })
107
+
108
+ // Convert and prepare data
109
+ async function loadData() {
110
+ const response = await fetch('/api/data')
111
+ const rawData = await response.json()
112
+
113
+ // Transform data structure
114
+ const convertedData = rawData.map((item) => ({
115
+ ...item,
116
+ groups: item.children?.map((child) => convertChildrenToGroups(child))
117
+ }))
118
+
119
+ // Convert to treemap format and sort
120
+ const treemapData = sortChildrenByKey(
121
+ convertedData.map((item) =>
122
+ c2m(item, 'value', (d) => ({
123
+ ...d,
124
+ id: d.path,
125
+ label: d.name
126
+ }))
127
+ ),
128
+ 'weight'
129
+ )
130
+
131
+ return treemapData
132
+ }
133
+
134
+ // Initialize
135
+ const container = document.querySelector('#app')
136
+ treemap.init(container)
137
+
138
+ // Load and set data
139
+ loadData().then((data) => {
140
+ treemap.setOptions({ data })
141
+ })
142
+
143
+ // Handle events
144
+ treemap.on('click', (event, module) => {
145
+ console.log('Clicked:', module?.node)
146
+ })
147
+
148
+ // Auto-resize on container changes
149
+ new ResizeObserver(() => {
150
+ treemap.resize()
151
+ }).observe(container)
152
+ ```
153
+
154
+ ## API Reference
155
+
156
+ ### Creating a Treemap
157
+
158
+ #### `createTreemap(options?)`
159
+
160
+ Creates a new treemap instance.
161
+
162
+ ```typescript
163
+ interface CreateTreemapOptions {
164
+ plugins?: Plugin[]
165
+ }
166
+ ```
167
+
168
+ ### Instance Methods
169
+
170
+ #### `init(element: HTMLElement)`
171
+
172
+ Initialize the treemap with a DOM container.
173
+
174
+ #### `setOptions(options: TreemapOptions)`
18
175
 
19
- ### Credits
176
+ Update treemap configuration and data.
20
177
 
21
- Algorithm is ported from [esbuild Bundle Size Analyzer](https://esbuild.github.io/analyze/) by [Evan Wallace](https://github.com/evanw). Refactor algorithm to adjusted the layout.
178
+ #### `resize()`
22
179
 
23
- ### LICENSE
180
+ Manually trigger a resize recalculation.
181
+
182
+ #### `on(event: string, handler: Function)`
183
+
184
+ Subscribe to treemap events.
185
+
186
+ #### `dispose()`
187
+
188
+ Clean up the treemap instance.
189
+
190
+ ### Data Format
191
+
192
+ ```typescript
193
+ interface TreemapNode {
194
+ id: string
195
+ label: string
196
+ weight: number
197
+ groups?: TreemapNode[]
198
+ // Custom properties...
199
+ }
200
+ ```
201
+
202
+ ### Utility Functions
203
+
204
+ #### `c2m(data, weightKey, transform?)`
205
+
206
+ Convert hierarchical data to treemap format.
207
+
208
+ #### `sortChildrenByKey(data, key)`
209
+
210
+ Sort treemap nodes by a specific key.
211
+
212
+ ## Built-in Plugins
213
+
214
+ - **presetColorPlugin**: Automatic color assignment
215
+ - **presetZoomablePlugin**: Zoom interactions
216
+ - **presetHighlightPlugin**: Hover highlighting
217
+ - **presetDragElementPlugin**: Drag interactions
218
+ - **presetScalePlugin**: Scaling controls
219
+ - **presetMenuPlugin**: Context menus
220
+
221
+ ## Browser Support
222
+
223
+ - Chrome/Edge 80+
224
+ - Firefox 75+
225
+ - Safari 13+
226
+
227
+ ## Performance
228
+
229
+ Squarified is optimized for performance with:
230
+
231
+ - Canvas-based rendering
232
+ - Efficient layout algorithms
233
+ - Smart redraw optimizations
234
+ - Memory-conscious design
235
+
236
+ ## Contributing
237
+
238
+ Contributions are welcome! Please read our [contributing guide](CONTRIBUTING.md) for details.
239
+
240
+ ## License
24
241
 
25
242
  [MIT](./LICENSE)
243
+
244
+ ## Auth
245
+
246
+ Kanno
247
+
248
+ ## Credits
249
+
250
+ Algorithm ported from [esbuild Bundle Size Analyzer](https://esbuild.github.io/analyze/) by [Evan Wallace](https://github.com/evanw), refactored and optimized for general-purpose treemap visualization.
@@ -88,7 +88,6 @@ var DisplayType = /*#__PURE__*/ function(DisplayType) {
88
88
  DisplayType["Box"] = "Box";
89
89
  DisplayType["Text"] = "Text";
90
90
  DisplayType["RoundRect"] = "RoundRect";
91
- DisplayType["Bitmap"] = "Bitmap";
92
91
  return DisplayType;
93
92
  }({});
94
93
  class Display {
@@ -291,12 +290,10 @@ class S extends Display {
291
290
  class Graph extends S {
292
291
  instruction;
293
292
  __options__;
294
- __widget__;
295
293
  constructor(options = {}){
296
294
  super(options);
297
295
  this.instruction = createInstruction();
298
296
  this.__options__ = options;
299
- this.__widget__ = null;
300
297
  }
301
298
  render(ctx) {
302
299
  this.create();
@@ -333,15 +330,11 @@ function isRoundRect(display) {
333
330
  function isText(display) {
334
331
  return isGraph(display) && display.__shape__ === DisplayType.Text;
335
332
  }
336
- function isBitmap(display) {
337
- return isGraph(display) && display.__shape__ === DisplayType.Bitmap;
338
- }
339
333
  const asserts = {
340
334
  isGraph,
341
335
  isBox,
342
336
  isText,
343
- isRoundRect,
344
- isBitmap
337
+ isRoundRect
345
338
  };
346
339
 
347
340
  class C extends Display {
@@ -741,16 +734,6 @@ function applyCanvasTransform(ctx, matrix, dpr) {
741
734
  ctx.setTransform(matrix.a * dpr, matrix.b * dpr, matrix.c * dpr, matrix.d * dpr, matrix.e * dpr, matrix.f * dpr);
742
735
  }
743
736
  function mixin(app, methods) {
744
- methods.forEach(({ name, fn })=>{
745
- Object.defineProperty(app, name, {
746
- value: fn(app),
747
- writable: false
748
- });
749
- });
750
- // @ts-expect-error not
751
- return app;
752
- }
753
- function mixinWithParams(app, methods) {
754
737
  methods.forEach(({ name, fn })=>{
755
738
  Object.defineProperty(app, name, {
756
739
  value: fn(app),
@@ -760,12 +743,6 @@ function mixinWithParams(app, methods) {
760
743
  });
761
744
  return app;
762
745
  }
763
- function prettyStrJoin(...s) {
764
- return s.join('');
765
- }
766
- function isMacOS() {
767
- return /Mac OS X/.test(navigator.userAgent);
768
- }
769
746
  function typedForIn(obj, callback) {
770
747
  for(const key in obj){
771
748
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
@@ -1125,12 +1102,15 @@ function squarify(data, rect, config, scale = 1) {
1125
1102
  if (!processedData.length) {
1126
1103
  return result;
1127
1104
  }
1128
- rect = {
1129
- x: rect.x + scaledGap / 2,
1130
- y: rect.y + scaledGap / 2,
1131
- w: rect.w - scaledGap,
1132
- h: rect.h - scaledGap
1133
- };
1105
+ let workingRect = rect;
1106
+ if (scaledGap > 0) {
1107
+ workingRect = {
1108
+ x: rect.x + scaledGap / 2,
1109
+ y: rect.y + scaledGap / 2,
1110
+ w: Math.max(0, rect.w - scaledGap),
1111
+ h: Math.max(0, rect.h - scaledGap)
1112
+ };
1113
+ }
1134
1114
  const worst = (start, end, shortestSide, totalWeight, aspectRatio)=>{
1135
1115
  const max = processedData[start].weight * aspectRatio;
1136
1116
  const min = processedData[end].weight * aspectRatio;
@@ -1182,39 +1162,43 @@ function squarify(data, rect, config, scale = 1) {
1182
1162
  w = upper - lower;
1183
1163
  h = splited;
1184
1164
  }
1185
- const edgeGap = currentGap / 2;
1186
- if (!isFirst) {
1187
- if (isHorizontalLayout) {
1188
- y += edgeGap;
1189
- h -= edgeGap;
1190
- } else {
1191
- x += edgeGap;
1192
- w -= edgeGap;
1165
+ if (currentGap > 0) {
1166
+ const edgeGap = currentGap / 2;
1167
+ if (!isFirst) {
1168
+ if (isHorizontalLayout) {
1169
+ y += edgeGap;
1170
+ h = Math.max(0, h - edgeGap);
1171
+ } else {
1172
+ x += edgeGap;
1173
+ w = Math.max(0, w - edgeGap);
1174
+ }
1193
1175
  }
1194
- }
1195
- if (!isLast) {
1196
- if (isHorizontalLayout) {
1197
- h -= edgeGap;
1198
- } else {
1199
- w -= edgeGap;
1176
+ if (!isLast) {
1177
+ if (isHorizontalLayout) {
1178
+ h = Math.max(0, h - edgeGap);
1179
+ } else {
1180
+ w = Math.max(0, w - edgeGap);
1181
+ }
1200
1182
  }
1201
1183
  }
1202
1184
  const nodeDepth = getNodeDepth(children) || 1;
1203
1185
  const { titleAreaHeight } = config;
1204
1186
  const diff = titleAreaHeight.max / nodeDepth;
1205
1187
  const titleHeight = diff < titleAreaHeight.min ? titleAreaHeight.min : diff;
1206
- w = Math.max(2, w);
1207
- h = Math.max(2, h);
1188
+ w = Math.max(1, w);
1189
+ h = Math.max(1, h);
1208
1190
  let childrenLayout = [];
1209
1191
  const hasValidChildren = children.groups && children.groups.length > 0;
1210
1192
  if (hasValidChildren) {
1193
+ const childGapOffset = currentGap > 0 ? currentGap : 0;
1211
1194
  const childRect = {
1212
- x: x + currentGap,
1195
+ x: x + childGapOffset,
1213
1196
  y: y + titleHeight,
1214
- w: Math.max(0, w - currentGap * 2),
1215
- h: Math.max(0, h - titleHeight - currentGap)
1197
+ w: Math.max(0, w - childGapOffset * 2),
1198
+ h: Math.max(0, h - titleHeight - childGapOffset)
1216
1199
  };
1217
- if (childRect.w > currentRadius * 2 && childRect.h > currentRadius * 2) {
1200
+ const minChildSize = currentRadius > 0 ? currentRadius * 2 : 1;
1201
+ if (childRect.w >= minChildSize && childRect.h >= minChildSize) {
1218
1202
  childrenLayout = squarify(children.groups || [], childRect, {
1219
1203
  ...config,
1220
1204
  rectGap: currentGap,
@@ -1240,16 +1224,17 @@ function squarify(data, rect, config, scale = 1) {
1240
1224
  areaInLayout += area;
1241
1225
  }
1242
1226
  start = end;
1227
+ const rectGapOffset = currentGap > 0 ? currentGap : 0;
1243
1228
  if (isHorizontalLayout) {
1244
- rect.x += splited + currentGap;
1245
- rect.w -= splited + currentGap;
1229
+ rect.x += splited + rectGapOffset;
1230
+ rect.w = Math.max(0, rect.w - splited - rectGapOffset);
1246
1231
  } else {
1247
- rect.y += splited + currentGap;
1248
- rect.h -= splited + currentGap;
1232
+ rect.y += splited + rectGapOffset;
1233
+ rect.h = Math.max(0, rect.h - splited - rectGapOffset);
1249
1234
  }
1250
1235
  }
1251
1236
  };
1252
- recursion(0, rect);
1237
+ recursion(0, workingRect);
1253
1238
  return result;
1254
1239
  }
1255
1240
 
@@ -1296,6 +1281,7 @@ class PluginDriver {
1296
1281
  this.plugins.forEach((plugin)=>{
1297
1282
  const hook = plugin[hookName];
1298
1283
  if (hook) {
1284
+ // @ts-expect-error fixme
1299
1285
  const hookResult = hook.call(this.pluginContext, ...args);
1300
1286
  if (hookResult) {
1301
1287
  Object.assign(finalResult, hookResult);
@@ -1351,6 +1337,65 @@ class Component extends Schedule {
1351
1337
  this.caches = new DefaultMap(()=>14);
1352
1338
  this.layoutNodes = [];
1353
1339
  }
1340
+ clearFontCacheInAABB(aabb) {
1341
+ const affectedModules = this.getModulesInAABB(this.layoutNodes, aabb);
1342
+ for (const module of affectedModules){
1343
+ this.caches.delete(module.node.id);
1344
+ }
1345
+ }
1346
+ getModulesInAABB(modules, aabb) {
1347
+ const result = [];
1348
+ for (const module of modules){
1349
+ const [x, y, w, h] = module.layout;
1350
+ const moduleAABB = {
1351
+ x,
1352
+ y,
1353
+ width: w,
1354
+ height: h
1355
+ };
1356
+ if (this.isAABBIntersecting(moduleAABB, aabb)) {
1357
+ result.push(module);
1358
+ if (module.children && module.children.length > 0) {
1359
+ result.push(...this.getModulesInAABB(module.children, aabb));
1360
+ }
1361
+ }
1362
+ }
1363
+ return result;
1364
+ }
1365
+ getViewportAABB(matrixE, matrixF, scale) {
1366
+ const { width, height } = this.render.options;
1367
+ const worldX = -matrixE / scale;
1368
+ const worldY = -matrixF / scale;
1369
+ const worldWidth = width / scale;
1370
+ const worldHeight = height / scale;
1371
+ return {
1372
+ x: worldX,
1373
+ y: worldY,
1374
+ width: worldWidth,
1375
+ height: worldHeight
1376
+ };
1377
+ }
1378
+ getAABBUnion(a, b) {
1379
+ const minX = Math.min(a.x, b.x);
1380
+ const minY = Math.min(a.y, b.y);
1381
+ const maxX = Math.max(a.x + a.width, b.x + b.width);
1382
+ const maxY = Math.max(a.y + a.height, b.y + b.height);
1383
+ return {
1384
+ x: minX,
1385
+ y: minY,
1386
+ width: maxX - minX,
1387
+ height: maxY - minY
1388
+ };
1389
+ }
1390
+ handleTransformCacheInvalidation(oldMatrix, newMatrix) {
1391
+ const oldViewportAABB = this.getViewportAABB(oldMatrix.e, oldMatrix.f, oldMatrix.a);
1392
+ const newViewportAABB = this.getViewportAABB(newMatrix.e, newMatrix.f, newMatrix.a);
1393
+ const affectedAABB = this.getAABBUnion(oldViewportAABB, newViewportAABB);
1394
+ this.clearFontCacheInAABB(affectedAABB);
1395
+ }
1396
+ isAABBIntersecting(a, b) {
1397
+ return !(a.x + a.width < b.x || b.x + b.width < a.x || a.y + a.height < b.y || b.y + b.height < a.y);
1398
+ }
1354
1399
  drawBroundRect(node) {
1355
1400
  const [x, y, w, h] = node.layout;
1356
1401
  const { rectRadius } = node.config;
@@ -1361,7 +1406,6 @@ class Component extends Schedule {
1361
1406
  padding: 0,
1362
1407
  radius: effectiveRadius
1363
1408
  });
1364
- rect.__widget__ = node;
1365
1409
  this.rectLayer.add(rect);
1366
1410
  for (const child of node.children){
1367
1411
  this.drawBroundRect(child);
@@ -1435,11 +1479,16 @@ class Component extends Schedule {
1435
1479
  }
1436
1480
  calculateLayoutNodes(data, rect, scale = 1) {
1437
1481
  const config = {
1438
- titleAreaHeight: this.config.layout?.titleAreaHeight || DEFAULT_TITLE_AREA_HEIGHT,
1439
- rectRadius: this.config.layout?.rectRadius || DEFAULT_RECT_BORDER_RADIUS,
1440
- rectGap: this.config.layout?.rectGap || DEFAULT_RECT_GAP
1482
+ titleAreaHeight: this.config.layout?.titleAreaHeight ?? DEFAULT_TITLE_AREA_HEIGHT,
1483
+ rectRadius: this.config.layout?.rectRadius ?? DEFAULT_RECT_BORDER_RADIUS,
1484
+ rectGap: this.config.layout?.rectGap ?? DEFAULT_RECT_GAP
1441
1485
  };
1442
- return squarify(data, rect, config, scale);
1486
+ const layoutNodes = squarify(data, rect, config, scale);
1487
+ const result = this.pluginDriver.cascadeHook('onLayoutCalculated', layoutNodes, rect, config);
1488
+ if (result && result.layoutNodes?.length) {
1489
+ return result.layoutNodes;
1490
+ }
1491
+ return layoutNodes;
1443
1492
  }
1444
1493
  }
1445
1494
  function evaluateOptimalFontSize(c, text, config, desiredW, desiredH) {
@@ -1448,39 +1497,25 @@ function evaluateOptimalFontSize(c, text, config, desiredW, desiredH) {
1448
1497
  const { fontSize, family } = config;
1449
1498
  let min = fontSize.min;
1450
1499
  let max = fontSize.max;
1451
- const cache = new Map();
1452
1500
  while(max - min >= 1){
1453
1501
  const current = min + (max - min) / 2;
1454
- if (!cache.has(current)) {
1455
- c.font = `${current}px ${family}`;
1456
- const metrics = c.measureText(text);
1457
- const width = metrics.width;
1458
- const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
1459
- cache.set(current, {
1460
- width,
1461
- height
1462
- });
1463
- }
1464
- const { width, height } = cache.get(current);
1465
- if (width > desiredW || height > desiredH) {
1466
- max = current;
1467
- } else {
1502
+ c.font = `${current}px ${family}`;
1503
+ const textWidth = c.measureText(text).width;
1504
+ const metrics = c.measureText(text);
1505
+ const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
1506
+ if (textWidth <= desiredW && textHeight <= desiredH) {
1468
1507
  min = current;
1508
+ } else {
1509
+ max = current;
1469
1510
  }
1470
1511
  }
1471
1512
  return Math.floor(min);
1472
1513
  }
1473
1514
  function getTextLayout(c, text, width, height) {
1474
- const ellipsisWidth = measureTextWidth(c, '...');
1475
- if (width < ellipsisWidth) {
1476
- if (height > ellipsisWidth) {
1477
- return {
1478
- valid: true,
1479
- text: '...',
1480
- direction: 'vertical',
1481
- width: ellipsisWidth / 3
1482
- };
1483
- }
1515
+ const textWidth = c.measureText(text).width;
1516
+ const metrics = c.measureText(text);
1517
+ const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
1518
+ if (textHeight > height) {
1484
1519
  return {
1485
1520
  valid: false,
1486
1521
  text: '',
@@ -1488,8 +1523,7 @@ function getTextLayout(c, text, width, height) {
1488
1523
  width: 0
1489
1524
  };
1490
1525
  }
1491
- const textWidth = measureTextWidth(c, text);
1492
- if (textWidth < width) {
1526
+ if (textWidth <= width) {
1493
1527
  return {
1494
1528
  valid: true,
1495
1529
  text,
@@ -1497,16 +1531,41 @@ function getTextLayout(c, text, width, height) {
1497
1531
  width: textWidth
1498
1532
  };
1499
1533
  }
1500
- return {
1534
+ const ellipsisWidth = c.measureText('...').width;
1535
+ if (width <= ellipsisWidth) {
1536
+ return {
1537
+ valid: false,
1538
+ text: '',
1539
+ direction: 'horizontal',
1540
+ width: 0
1541
+ };
1542
+ }
1543
+ let left = 0;
1544
+ let right = text.length;
1545
+ let bestFit = '';
1546
+ while(left <= right){
1547
+ const mid = Math.floor((left + right) / 2);
1548
+ const substring = text.substring(0, mid);
1549
+ const subWidth = c.measureText(substring).width;
1550
+ if (subWidth + ellipsisWidth <= width) {
1551
+ bestFit = substring;
1552
+ left = mid + 1;
1553
+ } else {
1554
+ right = mid - 1;
1555
+ }
1556
+ }
1557
+ return bestFit.length > 0 ? {
1558
+ valid: true,
1559
+ text: bestFit + '...',
1560
+ direction: 'horizontal',
1561
+ width
1562
+ } : {
1501
1563
  valid: true,
1502
1564
  text: '...',
1503
1565
  direction: 'horizontal',
1504
1566
  width: ellipsisWidth
1505
1567
  };
1506
1568
  }
1507
- function measureTextWidth(c, text) {
1508
- return c.measureText(text).width;
1509
- }
1510
1569
 
1511
1570
  // I think those event is enough for user.
1512
1571
  const DOM_EVENTS = [
@@ -1645,4 +1704,4 @@ class DOMEvent extends Event {
1645
1704
  }
1646
1705
  }
1647
1706
 
1648
- export { isMacOS as A, typedForIn as B, Component as C, DOMEvent as D, Event as E, stackMatrixTransform as F, stackMatrixTransformWithGraphAndLayer as G, smoothFrame as H, isScrollWheelOrRightButtonOnMouseupAndDown as I, DefaultMap as J, DEFAULT_MATRIX_LOC as K, PI_2 as P, Schedule as S, assertExists as a, bindParentForModule as b, c2m as c, findRelativeNodeById as d, flatten as e, findRelativeNode as f, getNodeDepth as g, definePlugin as h, isClickEvent as i, isContextMenuEvent as j, isMouseEvent as k, logger as l, mixin as m, noop as n, isWheelEvent as o, hashCode as p, perferNumeric as q, createRoundBlock as r, sortChildrenByKey as s, createTitleText as t, raf as u, visit as v, createCanvasElement as w, applyCanvasTransform as x, mixinWithParams as y, prettyStrJoin as z };
1707
+ export { stackMatrixTransformWithGraphAndLayer as A, smoothFrame as B, Component as C, DOMEvent as D, Event as E, isScrollWheelOrRightButtonOnMouseupAndDown as F, DefaultMap as G, DEFAULT_MATRIX_LOC as H, PI_2 as P, Schedule as S, assertExists as a, bindParentForModule as b, c2m as c, findRelativeNodeById as d, flatten as e, findRelativeNode as f, getNodeDepth as g, definePlugin as h, isClickEvent as i, isContextMenuEvent as j, isMouseEvent as k, logger as l, mixin as m, isWheelEvent as n, hashCode as o, perferNumeric as p, noop as q, createRoundBlock as r, sortChildrenByKey as s, createTitleText as t, raf as u, visit as v, createCanvasElement as w, applyCanvasTransform as x, typedForIn as y, stackMatrixTransform as z };