squarified 0.6.0 → 0.6.2
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/dom-event-ClwTQnot.js +1743 -0
- package/dist/dom-event-DrYYfglv.mjs +1706 -0
- package/dist/index-Bks7tGiV.d.ts +518 -0
- package/dist/index.d.mts +1 -352
- package/dist/index.d.ts +1 -352
- package/dist/index.js +76 -1953
- package/dist/index.mjs +48 -1940
- package/dist/plugin.d.mts +103 -0
- package/dist/plugin.d.ts +103 -0
- package/dist/plugin.js +662 -0
- package/dist/plugin.mjs +655 -0
- package/package.json +1 -1
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
import { h as definePlugin, B as smoothFrame, r as createRoundBlock, z as stackMatrixTransform, S as Schedule, H as DEFAULT_MATRIX_LOC, F as isScrollWheelOrRightButtonOnMouseupAndDown, A as stackMatrixTransformWithGraphAndLayer, j as isContextMenuEvent, o as hashCode, P as PI_2, n as isWheelEvent, m as mixin } from './dom-event-DrYYfglv.mjs';
|
|
2
|
+
|
|
3
|
+
// Currently, etoile is an internal module, so we won't need too much easing functions.
|
|
4
|
+
// And the animation logic is implemented by user code.
|
|
5
|
+
const easing = {
|
|
6
|
+
linear: (k)=>k,
|
|
7
|
+
quadraticIn: (k)=>k * k,
|
|
8
|
+
quadraticOut: (k)=>k * (2 - k),
|
|
9
|
+
quadraticInOut: (k)=>{
|
|
10
|
+
if ((k *= 2) < 1) {
|
|
11
|
+
return 0.5 * k * k;
|
|
12
|
+
}
|
|
13
|
+
return -0.5 * (--k * (k - 2) - 1);
|
|
14
|
+
},
|
|
15
|
+
cubicIn: (k)=>k * k * k,
|
|
16
|
+
cubicOut: (k)=>{
|
|
17
|
+
if ((k *= 2) < 1) {
|
|
18
|
+
return 0.5 * k * k * k;
|
|
19
|
+
}
|
|
20
|
+
return 0.5 * ((k -= 2) * k * k + 2);
|
|
21
|
+
},
|
|
22
|
+
cubicInOut: (k)=>{
|
|
23
|
+
if ((k *= 2) < 1) {
|
|
24
|
+
return 0.5 * k * k * k;
|
|
25
|
+
}
|
|
26
|
+
return 0.5 * ((k -= 2) * k * k + 2);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
class Highlight extends Schedule {
|
|
31
|
+
reset() {
|
|
32
|
+
this.destory();
|
|
33
|
+
this.update();
|
|
34
|
+
}
|
|
35
|
+
get canvas() {
|
|
36
|
+
return this.render.canvas;
|
|
37
|
+
}
|
|
38
|
+
setZIndexForHighlight(zIndex = '-1') {
|
|
39
|
+
this.canvas.style.zIndex = zIndex;
|
|
40
|
+
}
|
|
41
|
+
init() {
|
|
42
|
+
this.setZIndexForHighlight();
|
|
43
|
+
this.canvas.style.position = 'absolute';
|
|
44
|
+
this.canvas.style.pointerEvents = 'none';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const ANIMATION_DURATION = 300;
|
|
48
|
+
const HIGH_LIGHT_OPACITY = 0.3;
|
|
49
|
+
const fill = {
|
|
50
|
+
desc: {
|
|
51
|
+
r: 255,
|
|
52
|
+
g: 255,
|
|
53
|
+
b: 255
|
|
54
|
+
},
|
|
55
|
+
mode: 'rgb'
|
|
56
|
+
};
|
|
57
|
+
const presetHighlightPlugin = definePlugin({
|
|
58
|
+
name: 'treemap:preset-highlight',
|
|
59
|
+
onLoad () {
|
|
60
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
61
|
+
if (!meta) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!meta.highlight) {
|
|
65
|
+
meta.highlight = new Highlight(this.instance.to);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
onDOMEventTriggered (name, _, module, { stateManager: state, matrix }) {
|
|
69
|
+
if (name === 'mousemove') {
|
|
70
|
+
if (state.canTransition('MOVE')) {
|
|
71
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
72
|
+
if (!module) {
|
|
73
|
+
meta.highlight?.reset();
|
|
74
|
+
meta.highlight?.update();
|
|
75
|
+
meta.highlight?.setZIndexForHighlight();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const [x, y, w, h] = module.layout;
|
|
79
|
+
const effectiveRadius = Math.min(module.config.rectRadius, w / 4, h / 4);
|
|
80
|
+
smoothFrame((_, cleanup)=>{
|
|
81
|
+
cleanup();
|
|
82
|
+
meta.highlight?.reset();
|
|
83
|
+
const mask = createRoundBlock(x, y, w, h, {
|
|
84
|
+
fill,
|
|
85
|
+
opacity: HIGH_LIGHT_OPACITY,
|
|
86
|
+
radius: effectiveRadius,
|
|
87
|
+
padding: 0
|
|
88
|
+
});
|
|
89
|
+
meta.highlight?.add(mask);
|
|
90
|
+
meta.highlight?.setZIndexForHighlight('1');
|
|
91
|
+
stackMatrixTransform(mask, matrix.e, matrix.f, 1);
|
|
92
|
+
meta.highlight?.update();
|
|
93
|
+
}, {
|
|
94
|
+
duration: ANIMATION_DURATION
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
onResize () {
|
|
100
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
101
|
+
if (!meta) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
meta.highlight?.render.initOptions({
|
|
105
|
+
...this.instance.render.options
|
|
106
|
+
});
|
|
107
|
+
meta.highlight?.reset();
|
|
108
|
+
meta.highlight?.init();
|
|
109
|
+
},
|
|
110
|
+
onDispose () {
|
|
111
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
112
|
+
if (meta && meta.highlight) {
|
|
113
|
+
meta.highlight.destory();
|
|
114
|
+
meta.highlight = null;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
meta: {
|
|
118
|
+
highlight: null
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const presetDragElementPlugin = definePlugin({
|
|
123
|
+
name: 'treemap:preset-drag-element',
|
|
124
|
+
onDOMEventTriggered (name, event, module, domEvent) {
|
|
125
|
+
const { stateManager: state, matrix, component } = domEvent;
|
|
126
|
+
switch(name){
|
|
127
|
+
case 'mousemove':
|
|
128
|
+
{
|
|
129
|
+
if (state.isInState('DRAGGING')) {
|
|
130
|
+
domEvent.silent('click');
|
|
131
|
+
} else {
|
|
132
|
+
domEvent.active('click');
|
|
133
|
+
}
|
|
134
|
+
const meta = getDragOptions.call(this);
|
|
135
|
+
if (!meta) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (meta.dragOptions.x === 0 && meta.dragOptions.y === 0) {
|
|
139
|
+
state.transition('IDLE');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
state.transition('DRAGGING');
|
|
143
|
+
if (state.isInState('DRAGGING')) {
|
|
144
|
+
const highlight = getHighlightInstance.call(this);
|
|
145
|
+
smoothFrame((_, cleanup)=>{
|
|
146
|
+
cleanup();
|
|
147
|
+
const { offsetX, offsetY } = event.native;
|
|
148
|
+
const drawX = offsetX - meta.dragOptions.x;
|
|
149
|
+
const drawY = offsetY - meta.dragOptions.y;
|
|
150
|
+
const lastX = meta.dragOptions.x;
|
|
151
|
+
const lastY = meta.dragOptions.y;
|
|
152
|
+
if (highlight?.highlight) {
|
|
153
|
+
highlight.highlight.reset();
|
|
154
|
+
highlight.highlight.setZIndexForHighlight();
|
|
155
|
+
}
|
|
156
|
+
matrix.translation(drawX, drawY);
|
|
157
|
+
meta.dragOptions.x = offsetX;
|
|
158
|
+
meta.dragOptions.y = offsetY;
|
|
159
|
+
meta.dragOptions.lastX = lastX;
|
|
160
|
+
meta.dragOptions.lastY = lastY;
|
|
161
|
+
component.cleanup();
|
|
162
|
+
component.draw(false, false);
|
|
163
|
+
stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, 1);
|
|
164
|
+
component.update();
|
|
165
|
+
return true;
|
|
166
|
+
}, {
|
|
167
|
+
duration: ANIMATION_DURATION,
|
|
168
|
+
deps: [
|
|
169
|
+
()=>state.isInState('IDLE')
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case 'mouseup':
|
|
176
|
+
{
|
|
177
|
+
if (state.isInState('PRESSED')) {
|
|
178
|
+
const meta = getDragOptions.call(this);
|
|
179
|
+
if (meta && meta.dragOptions) {
|
|
180
|
+
if (meta.dragOptions.x === meta.dragOptions.lastX && meta.dragOptions.y === meta.dragOptions.lastY) {
|
|
181
|
+
state.transition('IDLE');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (state.isInState('DRAGGING') && state.canTransition('IDLE')) {
|
|
187
|
+
const highlight = getHighlightInstance.call(this);
|
|
188
|
+
if (highlight && highlight.highlight) {
|
|
189
|
+
highlight.highlight.reset();
|
|
190
|
+
highlight.highlight.setZIndexForHighlight();
|
|
191
|
+
}
|
|
192
|
+
const meta = getDragOptions.call(this);
|
|
193
|
+
if (meta && meta.dragOptions) {
|
|
194
|
+
meta.dragOptions.x = 0;
|
|
195
|
+
meta.dragOptions.y = 0;
|
|
196
|
+
meta.dragOptions.lastX = 0;
|
|
197
|
+
meta.dragOptions.lastY = 0;
|
|
198
|
+
state.transition('IDLE');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 'mousedown':
|
|
204
|
+
{
|
|
205
|
+
if (isScrollWheelOrRightButtonOnMouseupAndDown(event.native)) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const meta = getDragOptions.call(this);
|
|
209
|
+
if (!meta) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
meta.dragOptions.x = event.native.offsetX;
|
|
213
|
+
meta.dragOptions.y = event.native.offsetY;
|
|
214
|
+
meta.dragOptions.lastX = event.native.offsetX;
|
|
215
|
+
meta.dragOptions.lastY = event.native.offsetY;
|
|
216
|
+
state.transition('PRESSED');
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
meta: {
|
|
222
|
+
dragOptions: {
|
|
223
|
+
x: 0,
|
|
224
|
+
y: 0,
|
|
225
|
+
lastX: 0,
|
|
226
|
+
lastY: 0
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
onResize ({ matrix, stateManager: state }) {
|
|
230
|
+
matrix.create(DEFAULT_MATRIX_LOC);
|
|
231
|
+
state.reset();
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
function getHighlightInstance() {
|
|
235
|
+
return this.getPluginMetadata('treemap:preset-highlight');
|
|
236
|
+
}
|
|
237
|
+
function getDragOptions() {
|
|
238
|
+
const meta = this.getPluginMetadata('treemap:preset-drag-element');
|
|
239
|
+
return meta;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function presetMenuPlugin(options) {
|
|
243
|
+
let menu = null;
|
|
244
|
+
let domEvent = null;
|
|
245
|
+
const handleMenuClick = (e)=>{
|
|
246
|
+
if (!domEvent) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!menu) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const target = e.target;
|
|
253
|
+
if (target.parentNode) {
|
|
254
|
+
const parent = target.parentNode;
|
|
255
|
+
const action = parent.getAttribute('data-action');
|
|
256
|
+
if (!action) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (options?.onClick) {
|
|
260
|
+
options.onClick(action, domEvent.findRelativeNode({
|
|
261
|
+
native: e,
|
|
262
|
+
kind: undefined
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
menu.style.display = 'none';
|
|
267
|
+
};
|
|
268
|
+
return definePlugin({
|
|
269
|
+
name: 'treemap:preset-menu',
|
|
270
|
+
onDOMEventTriggered (_, event, __, DOMEvent) {
|
|
271
|
+
if (isContextMenuEvent(event)) {
|
|
272
|
+
event.native.stopPropagation();
|
|
273
|
+
event.native.preventDefault();
|
|
274
|
+
if (!menu) {
|
|
275
|
+
menu = document.createElement('div');
|
|
276
|
+
domEvent = DOMEvent;
|
|
277
|
+
Object.assign(menu.style, {
|
|
278
|
+
backgroundColor: '#fff',
|
|
279
|
+
...options?.style,
|
|
280
|
+
position: 'absolute',
|
|
281
|
+
zIndex: '9999'
|
|
282
|
+
});
|
|
283
|
+
menu.addEventListener('click', handleMenuClick);
|
|
284
|
+
if (menu && options?.render) {
|
|
285
|
+
const result = options.render(menu);
|
|
286
|
+
menu.innerHTML = result.map((item)=>{
|
|
287
|
+
return `<div data-action='${item.action}'>${item.html}</div>`;
|
|
288
|
+
}).join('');
|
|
289
|
+
}
|
|
290
|
+
document.body.append(menu);
|
|
291
|
+
}
|
|
292
|
+
menu.style.left = event.native.clientX + 'px';
|
|
293
|
+
menu.style.top = event.native.clientY + 'px';
|
|
294
|
+
menu.style.display = 'initial';
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
onDispose () {
|
|
298
|
+
if (!menu) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
menu.removeEventListener('click', handleMenuClick);
|
|
302
|
+
menu = null;
|
|
303
|
+
domEvent = null;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const presetColorPlugin = definePlugin({
|
|
309
|
+
name: 'treemap:preset-color',
|
|
310
|
+
onModuleInit (modules) {
|
|
311
|
+
const colorMappings = {};
|
|
312
|
+
for(let i = 0; i < modules.length; i++){
|
|
313
|
+
const module = modules[i];
|
|
314
|
+
assignColorMappings(colorMappings, module, Math.abs(hashCode(module.node.id)) % PI_2, 0);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
colorMappings
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
function assignColorMappings(colorMappings, module, ancestorHue, depth) {
|
|
322
|
+
const hueOffset = Math.abs(hashCode(module.node.id)) % 60 - 30;
|
|
323
|
+
const hue = (ancestorHue + hueOffset) % 360;
|
|
324
|
+
const saturation = Math.max(75 - depth * 5, 40);
|
|
325
|
+
const baseLightness = 55 - depth * 3;
|
|
326
|
+
const color = adjustColorToComfortableForHumanEye(hue, saturation, baseLightness);
|
|
327
|
+
colorMappings[module.node.id] = color;
|
|
328
|
+
if (module.node.isCombinedNode && module.node.originalNodes) {
|
|
329
|
+
for (const combined of module.node.originalNodes){
|
|
330
|
+
colorMappings[combined.id] = color;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (module.children && module.children.length) {
|
|
334
|
+
const childCount = module.children.length;
|
|
335
|
+
for(let i = 0; i < childCount; i++){
|
|
336
|
+
const childHueOffset = 40 * i / childCount;
|
|
337
|
+
const childHue = (hue + childHueOffset) % 360;
|
|
338
|
+
assignColorMappings(colorMappings, module.children[i], childHue, depth + 1);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function adjustColorToComfortableForHumanEye(hue, saturation, lightness) {
|
|
343
|
+
hue = (hue % 360 + 360) % 360;
|
|
344
|
+
saturation = Math.min(Math.max(saturation, 40), 85);
|
|
345
|
+
lightness = Math.min(Math.max(lightness, 35), 75);
|
|
346
|
+
if (hue >= 60 && hue <= 180) {
|
|
347
|
+
saturation = Math.max(saturation - 10, 40);
|
|
348
|
+
lightness = Math.min(lightness + 5, 75);
|
|
349
|
+
} else if (hue >= 200 && hue <= 280) {
|
|
350
|
+
lightness = Math.min(lightness + 8, 75);
|
|
351
|
+
} else if (hue >= 0 && hue <= 30) {
|
|
352
|
+
saturation = Math.max(saturation - 5, 40);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
mode: 'hsl',
|
|
356
|
+
desc: {
|
|
357
|
+
h: hue,
|
|
358
|
+
s: saturation,
|
|
359
|
+
l: lightness
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function presetScalePlugin(options) {
|
|
365
|
+
return definePlugin({
|
|
366
|
+
name: 'treemap:preset-scale',
|
|
367
|
+
onDOMEventTriggered (_, event, module, evt) {
|
|
368
|
+
if (isWheelEvent(event)) {
|
|
369
|
+
onWheel(this, event, evt);
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
meta: {
|
|
373
|
+
scaleOptions: {
|
|
374
|
+
scale: 1,
|
|
375
|
+
minScale: options?.min || 0.1,
|
|
376
|
+
maxScale: options?.max || Infinity,
|
|
377
|
+
scaleFactor: 0.05
|
|
378
|
+
},
|
|
379
|
+
gestureState: {
|
|
380
|
+
isTrackingGesture: false,
|
|
381
|
+
lastEventTime: 0,
|
|
382
|
+
eventCount: 0,
|
|
383
|
+
totalDeltaY: 0,
|
|
384
|
+
totalDeltaX: 0,
|
|
385
|
+
consecutivePinchEvents: 0,
|
|
386
|
+
gestureType: 'unknown',
|
|
387
|
+
lockGestureType: false
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
onResize ({ matrix, stateManager: state }) {
|
|
391
|
+
const meta = getScaleOptions.call(this);
|
|
392
|
+
if (meta) {
|
|
393
|
+
meta.scaleOptions.scale = 1;
|
|
394
|
+
}
|
|
395
|
+
matrix.create(DEFAULT_MATRIX_LOC);
|
|
396
|
+
state.reset();
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function getScaleOptions() {
|
|
401
|
+
const meta = this.getPluginMetadata('treemap:preset-scale');
|
|
402
|
+
return meta;
|
|
403
|
+
}
|
|
404
|
+
function determineGestureType(event, gestureState) {
|
|
405
|
+
const now = Date.now();
|
|
406
|
+
const timeDiff = now - gestureState.lastEventTime;
|
|
407
|
+
if (timeDiff > 150) {
|
|
408
|
+
Object.assign(gestureState, {
|
|
409
|
+
isTrackingGesture: false,
|
|
410
|
+
lastEventTime: now,
|
|
411
|
+
eventCount: 1,
|
|
412
|
+
totalDeltaY: Math.abs(event.deltaY),
|
|
413
|
+
totalDeltaX: Math.abs(event.deltaX),
|
|
414
|
+
consecutivePinchEvents: 0,
|
|
415
|
+
gestureType: 'unknown',
|
|
416
|
+
lockGestureType: false
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
gestureState.eventCount++;
|
|
420
|
+
gestureState.totalDeltaY += Math.abs(event.deltaY);
|
|
421
|
+
gestureState.totalDeltaX += Math.abs(event.deltaX);
|
|
422
|
+
gestureState.lastEventTime = now;
|
|
423
|
+
}
|
|
424
|
+
if (event.ctrlKey) {
|
|
425
|
+
gestureState.gestureType = 'zoom';
|
|
426
|
+
gestureState.lockGestureType = true;
|
|
427
|
+
return 'zoom';
|
|
428
|
+
}
|
|
429
|
+
// windows/macos mouse wheel
|
|
430
|
+
// Usually the dettaY is large and deltaX maybe 0 or small number.
|
|
431
|
+
const isMouseWheel = Math.abs(event.deltaX) >= 100 && Math.abs(event.deltaX) <= 10 || Math.abs(event.deltaY) > 50 && Math.abs(event.deltaX) < Math.abs(event.deltaY) * 0.1;
|
|
432
|
+
if (isMouseWheel) {
|
|
433
|
+
gestureState.gestureType = 'zoom';
|
|
434
|
+
gestureState.lockGestureType = true;
|
|
435
|
+
return 'zoom';
|
|
436
|
+
}
|
|
437
|
+
if (gestureState.lockGestureType && gestureState.gestureType !== 'unknown') {
|
|
438
|
+
return gestureState.gestureType;
|
|
439
|
+
}
|
|
440
|
+
// Magic Trackpad or Precision Touchpad
|
|
441
|
+
if (gestureState.eventCount >= 3) {
|
|
442
|
+
const avgDeltaY = gestureState.totalDeltaY / gestureState.eventCount;
|
|
443
|
+
const avgDeltaX = gestureState.totalDeltaX / gestureState.eventCount;
|
|
444
|
+
const ratio = avgDeltaX / (avgDeltaY + 0.1);
|
|
445
|
+
const isZoomGesture = avgDeltaY > 8 && ratio < 0.3 && Math.abs(event.deltaY) > 5;
|
|
446
|
+
if (isZoomGesture) {
|
|
447
|
+
gestureState.gestureType = 'zoom';
|
|
448
|
+
gestureState.lockGestureType = true;
|
|
449
|
+
return 'zoom';
|
|
450
|
+
} else {
|
|
451
|
+
gestureState.gestureType = 'pan';
|
|
452
|
+
gestureState.lockGestureType = true;
|
|
453
|
+
return 'pan';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return 'pan';
|
|
457
|
+
}
|
|
458
|
+
function onWheel(pluginContext, event, domEvent) {
|
|
459
|
+
event.native.preventDefault();
|
|
460
|
+
const meta = getScaleOptions.call(pluginContext);
|
|
461
|
+
if (!meta) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const gestureType = determineGestureType(event.native, meta.gestureState);
|
|
465
|
+
if (gestureType === 'zoom') {
|
|
466
|
+
handleZoom(pluginContext, event, domEvent);
|
|
467
|
+
} else {
|
|
468
|
+
handlePan(pluginContext, event, domEvent);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function updateViewport(pluginContext, { stateManager: state, component, matrix }, useAnimation = false) {
|
|
472
|
+
const highlight = getHighlightInstance.apply(pluginContext);
|
|
473
|
+
const doUpdate = ()=>{
|
|
474
|
+
if (highlight && highlight.highlight) {
|
|
475
|
+
highlight.highlight.reset();
|
|
476
|
+
highlight.highlight.setZIndexForHighlight();
|
|
477
|
+
}
|
|
478
|
+
component.cleanup();
|
|
479
|
+
const { width, height } = component.render.options;
|
|
480
|
+
component.layoutNodes = component.calculateLayoutNodes(component.data, {
|
|
481
|
+
w: width * matrix.a,
|
|
482
|
+
h: height * matrix.d,
|
|
483
|
+
x: 0,
|
|
484
|
+
y: 0
|
|
485
|
+
}, 1);
|
|
486
|
+
component.draw(true, false);
|
|
487
|
+
stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, 1);
|
|
488
|
+
component.update();
|
|
489
|
+
if (state.canTransition('IDLE')) {
|
|
490
|
+
state.transition('IDLE');
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
if (useAnimation) {
|
|
494
|
+
smoothFrame((_, cleanup)=>{
|
|
495
|
+
cleanup();
|
|
496
|
+
doUpdate();
|
|
497
|
+
return true;
|
|
498
|
+
}, {
|
|
499
|
+
duration: ANIMATION_DURATION
|
|
500
|
+
});
|
|
501
|
+
} else {
|
|
502
|
+
doUpdate();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function handleZoom(pluginContext, event, domEvent) {
|
|
506
|
+
const { stateManager: state, matrix, component } = domEvent;
|
|
507
|
+
const meta = getScaleOptions.call(pluginContext);
|
|
508
|
+
if (!meta) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const { scale, minScale, maxScale, scaleFactor } = meta.scaleOptions;
|
|
512
|
+
const oldMatrix = {
|
|
513
|
+
e: matrix.e,
|
|
514
|
+
f: matrix.f
|
|
515
|
+
};
|
|
516
|
+
const dynamicScaleFactor = Math.max(scaleFactor, scale * 0.1);
|
|
517
|
+
const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
|
|
518
|
+
const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
|
|
519
|
+
if (newScale === scale) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
state.transition('SCALING');
|
|
523
|
+
const mouseX = event.native.offsetX;
|
|
524
|
+
const mouseY = event.native.offsetY;
|
|
525
|
+
const scaleDiff = newScale / scale;
|
|
526
|
+
meta.scaleOptions.scale = newScale;
|
|
527
|
+
matrix.scale(scaleDiff, scaleDiff);
|
|
528
|
+
matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
|
|
529
|
+
matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
|
|
530
|
+
const newMatrix = {
|
|
531
|
+
e: matrix.e,
|
|
532
|
+
f: matrix.f
|
|
533
|
+
};
|
|
534
|
+
component.handleTransformCacheInvalidation(oldMatrix, newMatrix);
|
|
535
|
+
updateViewport(pluginContext, domEvent, false);
|
|
536
|
+
}
|
|
537
|
+
function handlePan(pluginContext, event, domEvent) {
|
|
538
|
+
const { stateManager: state, matrix } = domEvent;
|
|
539
|
+
const panSpeed = 0.8;
|
|
540
|
+
const deltaX = event.native.deltaX * panSpeed;
|
|
541
|
+
const deltaY = event.native.deltaY * panSpeed;
|
|
542
|
+
state.transition('PANNING');
|
|
543
|
+
matrix.e -= deltaX;
|
|
544
|
+
matrix.f -= deltaY;
|
|
545
|
+
updateViewport(pluginContext, domEvent, true);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const MAX_SCALE_MULTIPLIER = 2.0;
|
|
549
|
+
const ZOOM_PADDING_RATIO = 0.85;
|
|
550
|
+
const presetZoomablePlugin = definePlugin({
|
|
551
|
+
name: 'treemap:preset-zoomable',
|
|
552
|
+
onLoad (treemap, { stateManager: state, matrix }) {
|
|
553
|
+
return mixin(treemap, [
|
|
554
|
+
{
|
|
555
|
+
name: 'zoom',
|
|
556
|
+
fn: ()=>(id)=>{
|
|
557
|
+
const meta = this.getPluginMetadata('treemap:preset-zoomable');
|
|
558
|
+
if (!meta || state.isInState('ZOOMING')) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const targetModule = this.resolveModuleById(id);
|
|
562
|
+
if (!targetModule) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const oldMatrix = {
|
|
566
|
+
e: matrix.e,
|
|
567
|
+
f: matrix.f,
|
|
568
|
+
a: matrix.a
|
|
569
|
+
};
|
|
570
|
+
meta.previousMatrixState = {
|
|
571
|
+
e: matrix.e,
|
|
572
|
+
f: matrix.f,
|
|
573
|
+
a: matrix.a,
|
|
574
|
+
d: matrix.d
|
|
575
|
+
};
|
|
576
|
+
const component = this.instance;
|
|
577
|
+
state.transition('ZOOMING');
|
|
578
|
+
const [nodeX, nodeY, nodeW, nodeH] = targetModule.layout;
|
|
579
|
+
const { width, height } = component.render.options;
|
|
580
|
+
const currentScale = matrix.a;
|
|
581
|
+
// To prevent unlimited scale factor growth.
|
|
582
|
+
const scaleX = width * ZOOM_PADDING_RATIO / nodeW;
|
|
583
|
+
const scaleY = height * ZOOM_PADDING_RATIO / nodeH;
|
|
584
|
+
const idleScale = Math.min(scaleX, scaleY);
|
|
585
|
+
const maxAllowedScale = currentScale * MAX_SCALE_MULTIPLIER;
|
|
586
|
+
const targetScale = Math.max(currentScale, Math.min(idleScale, maxAllowedScale));
|
|
587
|
+
// Real world args
|
|
588
|
+
const viewportCenterX = width / 2;
|
|
589
|
+
const viewportCenterY = height / 2;
|
|
590
|
+
const originalNodeCenterX = (nodeX + nodeW / 2) / currentScale;
|
|
591
|
+
const originalNodeCenterY = (nodeY + nodeH / 2) / currentScale;
|
|
592
|
+
const targetE = viewportCenterX - originalNodeCenterX * targetScale;
|
|
593
|
+
const targetF = viewportCenterY - originalNodeCenterY * targetScale;
|
|
594
|
+
const scaleMeta = getScaleOptions.call(this);
|
|
595
|
+
if (scaleMeta) {
|
|
596
|
+
scaleMeta.scaleOptions.scale = targetScale;
|
|
597
|
+
}
|
|
598
|
+
const highlight = getHighlightInstance.call(this);
|
|
599
|
+
const dragMeta = getDragOptions.call(this);
|
|
600
|
+
if (dragMeta) {
|
|
601
|
+
Object.assign(dragMeta.dragOptions, {
|
|
602
|
+
x: 0,
|
|
603
|
+
y: 0,
|
|
604
|
+
lastX: 0,
|
|
605
|
+
lastY: 0
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
const startMatrix = {
|
|
609
|
+
e: matrix.e,
|
|
610
|
+
f: matrix.f,
|
|
611
|
+
a: matrix.a,
|
|
612
|
+
d: matrix.d
|
|
613
|
+
};
|
|
614
|
+
const finalMatrix = {
|
|
615
|
+
e: targetE,
|
|
616
|
+
f: targetF
|
|
617
|
+
};
|
|
618
|
+
component.handleTransformCacheInvalidation(oldMatrix, finalMatrix);
|
|
619
|
+
smoothFrame((progress)=>{
|
|
620
|
+
const easedProgress = easing.cubicInOut(progress);
|
|
621
|
+
matrix.create(DEFAULT_MATRIX_LOC);
|
|
622
|
+
matrix.e = startMatrix.e + (targetE - startMatrix.e) * easedProgress;
|
|
623
|
+
matrix.f = startMatrix.f + (targetF - startMatrix.f) * easedProgress;
|
|
624
|
+
matrix.a = startMatrix.a + (targetScale - startMatrix.a) * easedProgress;
|
|
625
|
+
matrix.d = startMatrix.d + (targetScale - startMatrix.d) * easedProgress;
|
|
626
|
+
if (highlight?.highlight) {
|
|
627
|
+
highlight.highlight.reset();
|
|
628
|
+
highlight.highlight.setZIndexForHighlight();
|
|
629
|
+
}
|
|
630
|
+
component.cleanup();
|
|
631
|
+
component.layoutNodes = component.calculateLayoutNodes(component.data, {
|
|
632
|
+
w: width * matrix.a,
|
|
633
|
+
h: height * matrix.d,
|
|
634
|
+
x: 0,
|
|
635
|
+
y: 0
|
|
636
|
+
}, 1);
|
|
637
|
+
component.draw(true, false);
|
|
638
|
+
stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, 1);
|
|
639
|
+
component.update();
|
|
640
|
+
}, {
|
|
641
|
+
duration: ANIMATION_DURATION,
|
|
642
|
+
onStop: ()=>{
|
|
643
|
+
state.reset();
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
]);
|
|
649
|
+
},
|
|
650
|
+
meta: {
|
|
651
|
+
isZooming: false
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
export { presetColorPlugin, presetDragElementPlugin, presetHighlightPlugin, presetMenuPlugin, presetScalePlugin, presetZoomablePlugin };
|