squarified 0.4.3 → 0.5.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.
@@ -1529,7 +1529,7 @@ class StateManager {
1529
1529
  canTransition(to) {
1530
1530
  switch(this.current){
1531
1531
  case 'IDLE':
1532
- return to === 'PRESSED' || to === 'MOVE' || to === 'SCALING' || to === 'ZOOMING';
1532
+ return to === 'PRESSED' || to === 'MOVE' || to === 'SCALING' || to === 'ZOOMING' || to === 'PANNING';
1533
1533
  case 'PRESSED':
1534
1534
  return to === 'DRAGGING' || to === 'IDLE';
1535
1535
  case 'DRAGGING':
@@ -1540,6 +1540,8 @@ class StateManager {
1540
1540
  return to === 'IDLE';
1541
1541
  case 'ZOOMING':
1542
1542
  return to === 'IDLE';
1543
+ case 'PANNING':
1544
+ return to === 'IDLE';
1543
1545
  default:
1544
1546
  return false;
1545
1547
  }
@@ -1531,7 +1531,7 @@ class StateManager {
1531
1531
  canTransition(to) {
1532
1532
  switch(this.current){
1533
1533
  case 'IDLE':
1534
- return to === 'PRESSED' || to === 'MOVE' || to === 'SCALING' || to === 'ZOOMING';
1534
+ return to === 'PRESSED' || to === 'MOVE' || to === 'SCALING' || to === 'ZOOMING' || to === 'PANNING';
1535
1535
  case 'PRESSED':
1536
1536
  return to === 'DRAGGING' || to === 'IDLE';
1537
1537
  case 'DRAGGING':
@@ -1542,6 +1542,8 @@ class StateManager {
1542
1542
  return to === 'IDLE';
1543
1543
  case 'ZOOMING':
1544
1544
  return to === 'IDLE';
1545
+ case 'PANNING':
1546
+ return to === 'IDLE';
1545
1547
  default:
1546
1548
  return false;
1547
1549
  }
@@ -454,6 +454,7 @@ declare const STATE_TRANSITION: {
454
454
  readonly ZOOMING: "ZOOMING";
455
455
  readonly MOVE: "MOVE";
456
456
  readonly SCALING: "SCALING";
457
+ readonly PANNING: "PANNING";
457
458
  };
458
459
  type StateTransition = typeof STATE_TRANSITION[keyof typeof STATE_TRANSITION];
459
460
  declare class StateManager {
package/dist/index.d.mts CHANGED
@@ -1 +1 @@
1
- export { B as BasicTreemapInstance, c as CreateTreemapOptions, D as DOMEventType, a1 as DefaultMap, E as ExposedEventCallback, n as ExposedEventDefinition, o as ExposedEventMethods, x as GraphicConfig, w as GraphicFont, G as GraphicLayout, Q as InheritedCollections, U as InheritedCollectionsWithParamter, L as LayoutModule, M as Module, N as NativeModule, k as Plugin, P as PluginContext, l as PluginHooks, p as PrimitiveEventMetadata, S as Series, e as TreemapInstance, y as TreemapInstanceAPI, T as TreemapOptions, O as applyCanvasTransform, f as c2m, K as createCanvasElement, H as createRoundBlock, I as createTitleText, d as createTreemap, m as definePlugin, g as findRelativeNode, h as findRelativeNodeById, i as flattenModule, j as getNodeDepth, z as hashCode, q as isClickEvent, r as isContextMenuEvent, X as isMacOS, t as isMouseEvent, a0 as isScrollWheelOrRightButtonOnMouseupAndDown, u as isWheelEvent, R as mixin, V as mixinWithParams, F as noop, A as perferNumeric, W as prettyStrJoin, J as raf, $ as smoothFrame, s as sortChildrenByKey, Z as stackMatrixTransform, _ as stackMatrixTransformWithGraphAndLayer, Y as typedForIn, v as visit } from './index-BoUEaWVv.js';
1
+ export { B as BasicTreemapInstance, c as CreateTreemapOptions, D as DOMEventType, a1 as DefaultMap, E as ExposedEventCallback, n as ExposedEventDefinition, o as ExposedEventMethods, x as GraphicConfig, w as GraphicFont, G as GraphicLayout, Q as InheritedCollections, U as InheritedCollectionsWithParamter, L as LayoutModule, M as Module, N as NativeModule, k as Plugin, P as PluginContext, l as PluginHooks, p as PrimitiveEventMetadata, S as Series, e as TreemapInstance, y as TreemapInstanceAPI, T as TreemapOptions, O as applyCanvasTransform, f as c2m, K as createCanvasElement, H as createRoundBlock, I as createTitleText, d as createTreemap, m as definePlugin, g as findRelativeNode, h as findRelativeNodeById, i as flattenModule, j as getNodeDepth, z as hashCode, q as isClickEvent, r as isContextMenuEvent, X as isMacOS, t as isMouseEvent, a0 as isScrollWheelOrRightButtonOnMouseupAndDown, u as isWheelEvent, R as mixin, V as mixinWithParams, F as noop, A as perferNumeric, W as prettyStrJoin, J as raf, $ as smoothFrame, s as sortChildrenByKey, Z as stackMatrixTransform, _ as stackMatrixTransformWithGraphAndLayer, Y as typedForIn, v as visit } from './index-Dskgz6nc.js';
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { B as BasicTreemapInstance, c as CreateTreemapOptions, D as DOMEventType, a1 as DefaultMap, E as ExposedEventCallback, n as ExposedEventDefinition, o as ExposedEventMethods, x as GraphicConfig, w as GraphicFont, G as GraphicLayout, Q as InheritedCollections, U as InheritedCollectionsWithParamter, L as LayoutModule, M as Module, N as NativeModule, k as Plugin, P as PluginContext, l as PluginHooks, p as PrimitiveEventMetadata, S as Series, e as TreemapInstance, y as TreemapInstanceAPI, T as TreemapOptions, O as applyCanvasTransform, f as c2m, K as createCanvasElement, H as createRoundBlock, I as createTitleText, d as createTreemap, m as definePlugin, g as findRelativeNode, h as findRelativeNodeById, i as flattenModule, j as getNodeDepth, z as hashCode, q as isClickEvent, r as isContextMenuEvent, X as isMacOS, t as isMouseEvent, a0 as isScrollWheelOrRightButtonOnMouseupAndDown, u as isWheelEvent, R as mixin, V as mixinWithParams, F as noop, A as perferNumeric, W as prettyStrJoin, J as raf, $ as smoothFrame, s as sortChildrenByKey, Z as stackMatrixTransform, _ as stackMatrixTransformWithGraphAndLayer, Y as typedForIn, v as visit } from './index-BoUEaWVv.js';
1
+ export { B as BasicTreemapInstance, c as CreateTreemapOptions, D as DOMEventType, a1 as DefaultMap, E as ExposedEventCallback, n as ExposedEventDefinition, o as ExposedEventMethods, x as GraphicConfig, w as GraphicFont, G as GraphicLayout, Q as InheritedCollections, U as InheritedCollectionsWithParamter, L as LayoutModule, M as Module, N as NativeModule, k as Plugin, P as PluginContext, l as PluginHooks, p as PrimitiveEventMetadata, S as Series, e as TreemapInstance, y as TreemapInstanceAPI, T as TreemapOptions, O as applyCanvasTransform, f as c2m, K as createCanvasElement, H as createRoundBlock, I as createTitleText, d as createTreemap, m as definePlugin, g as findRelativeNode, h as findRelativeNodeById, i as flattenModule, j as getNodeDepth, z as hashCode, q as isClickEvent, r as isContextMenuEvent, X as isMacOS, t as isMouseEvent, a0 as isScrollWheelOrRightButtonOnMouseupAndDown, u as isWheelEvent, R as mixin, V as mixinWithParams, F as noop, A as perferNumeric, W as prettyStrJoin, J as raf, $ as smoothFrame, s as sortChildrenByKey, Z as stackMatrixTransform, _ as stackMatrixTransformWithGraphAndLayer, Y as typedForIn, v as visit } from './index-Dskgz6nc.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var domEvent = require('./dom-event-CeVZ44nB.js');
3
+ var domEvent = require('./dom-event-Dz0I7Z12.js');
4
4
 
5
5
  function createTreemap(// @ts-expect-error todo fix
6
6
  options) {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { l as logger, m as mixin, E as Event, n as noop, a as assertExists, b as bindParentForModule, C as Component, D as DOMEvent } from './dom-event-BLJt9knO.mjs';
2
- export { J as DefaultMap, x as applyCanvasTransform, c as c2m, w as createCanvasElement, r as createRoundBlock, t as createTitleText, h as definePlugin, f as findRelativeNode, d as findRelativeNodeById, e as flattenModule, g as getNodeDepth, p as hashCode, i as isClickEvent, j as isContextMenuEvent, A as isMacOS, k as isMouseEvent, I as isScrollWheelOrRightButtonOnMouseupAndDown, o as isWheelEvent, y as mixinWithParams, q as perferNumeric, z as prettyStrJoin, u as raf, H as smoothFrame, s as sortChildrenByKey, F as stackMatrixTransform, G as stackMatrixTransformWithGraphAndLayer, B as typedForIn, v as visit } from './dom-event-BLJt9knO.mjs';
1
+ import { l as logger, m as mixin, E as Event, n as noop, a as assertExists, b as bindParentForModule, C as Component, D as DOMEvent } from './dom-event-Bkw3ecGf.mjs';
2
+ export { J as DefaultMap, x as applyCanvasTransform, c as c2m, w as createCanvasElement, r as createRoundBlock, t as createTitleText, h as definePlugin, f as findRelativeNode, d as findRelativeNodeById, e as flattenModule, g as getNodeDepth, p as hashCode, i as isClickEvent, j as isContextMenuEvent, A as isMacOS, k as isMouseEvent, I as isScrollWheelOrRightButtonOnMouseupAndDown, o as isWheelEvent, y as mixinWithParams, q as perferNumeric, z as prettyStrJoin, u as raf, H as smoothFrame, s as sortChildrenByKey, F as stackMatrixTransform, G as stackMatrixTransformWithGraphAndLayer, B as typedForIn, v as visit } from './dom-event-Bkw3ecGf.mjs';
3
3
 
4
4
  function createTreemap(// @ts-expect-error todo fix
5
5
  options) {
package/dist/plugin.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { P as PluginContext, D as DOMEventType, a as DOMEventMetadata, L as LayoutModule, b as DOMEvent, C as ColorMappings, B as BasicTreemapInstance } from './index-BoUEaWVv.js';
1
+ import { P as PluginContext, D as DOMEventType, a as DOMEventMetadata, L as LayoutModule, b as DOMEvent, C as ColorMappings, B as BasicTreemapInstance } from './index-Dskgz6nc.js';
2
2
 
3
3
  declare const presetHighlightPlugin: {
4
4
  name: "treemap:preset-highlight";
@@ -69,6 +69,16 @@ declare function presetScalePlugin(options?: ScalePluginOptions): {
69
69
  maxScale: number;
70
70
  scaleFactor: number;
71
71
  };
72
+ gestureState: {
73
+ isTrackingGesture: false;
74
+ lastEventTime: number;
75
+ eventCount: number;
76
+ totalDeltaY: number;
77
+ totalDeltaX: number;
78
+ consecutivePinchEvents: number;
79
+ gestureType: "unknown";
80
+ lockGestureType: false;
81
+ };
72
82
  };
73
83
  onResize(this: PluginContext, { matrix, stateManager: state }: DOMEvent): void;
74
84
  };
package/dist/plugin.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { P as PluginContext, D as DOMEventType, a as DOMEventMetadata, L as LayoutModule, b as DOMEvent, C as ColorMappings, B as BasicTreemapInstance } from './index-BoUEaWVv.js';
1
+ import { P as PluginContext, D as DOMEventType, a as DOMEventMetadata, L as LayoutModule, b as DOMEvent, C as ColorMappings, B as BasicTreemapInstance } from './index-Dskgz6nc.js';
2
2
 
3
3
  declare const presetHighlightPlugin: {
4
4
  name: "treemap:preset-highlight";
@@ -69,6 +69,16 @@ declare function presetScalePlugin(options?: ScalePluginOptions): {
69
69
  maxScale: number;
70
70
  scaleFactor: number;
71
71
  };
72
+ gestureState: {
73
+ isTrackingGesture: false;
74
+ lastEventTime: number;
75
+ eventCount: number;
76
+ totalDeltaY: number;
77
+ totalDeltaX: number;
78
+ consecutivePinchEvents: number;
79
+ gestureType: "unknown";
80
+ lockGestureType: false;
81
+ };
72
82
  };
73
83
  onResize(this: PluginContext, { matrix, stateManager: state }: DOMEvent): void;
74
84
  };
package/dist/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var domEvent = require('./dom-event-CeVZ44nB.js');
3
+ var domEvent = require('./dom-event-Dz0I7Z12.js');
4
4
 
5
5
  // Currently, etoile is an internal module, so we won't need too much easing functions.
6
6
  // And the animation logic is implemented by user code.
@@ -377,6 +377,16 @@ function presetScalePlugin(options) {
377
377
  minScale: options?.min || 0.1,
378
378
  maxScale: options?.max || Infinity,
379
379
  scaleFactor: 0.05
380
+ },
381
+ gestureState: {
382
+ isTrackingGesture: false,
383
+ lastEventTime: 0,
384
+ eventCount: 0,
385
+ totalDeltaY: 0,
386
+ totalDeltaX: 0,
387
+ consecutivePinchEvents: 0,
388
+ gestureType: 'unknown',
389
+ lockGestureType: false
380
390
  }
381
391
  },
382
392
  onResize ({ matrix, stateManager: state }) {
@@ -393,34 +403,80 @@ function getScaleOptions() {
393
403
  const meta = this.getPluginMetadata('treemap:preset-scale');
394
404
  return meta;
395
405
  }
396
- function onWheel(pluginContext, event, { stateManager: state, component, matrix }) {
406
+ function determineGestureType(event, gestureState) {
407
+ const now = Date.now();
408
+ const timeDiff = now - gestureState.lastEventTime;
409
+ if (timeDiff > 150) {
410
+ Object.assign(gestureState, {
411
+ isTrackingGesture: false,
412
+ lastEventTime: now,
413
+ eventCount: 1,
414
+ totalDeltaY: Math.abs(event.deltaY),
415
+ totalDeltaX: Math.abs(event.deltaX),
416
+ consecutivePinchEvents: 0,
417
+ gestureType: 'unknown',
418
+ lockGestureType: false
419
+ });
420
+ } else {
421
+ gestureState.eventCount++;
422
+ gestureState.totalDeltaY += Math.abs(event.deltaY);
423
+ gestureState.totalDeltaX += Math.abs(event.deltaX);
424
+ gestureState.lastEventTime = now;
425
+ }
426
+ if (event.ctrlKey) {
427
+ gestureState.gestureType = 'zoom';
428
+ gestureState.lockGestureType = true;
429
+ return 'zoom';
430
+ }
431
+ // windows/macos mouse wheel
432
+ // Usually the dettaY is large and deltaX maybe 0 or small number.
433
+ 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;
434
+ if (isMouseWheel) {
435
+ gestureState.gestureType = 'zoom';
436
+ gestureState.lockGestureType = true;
437
+ return 'zoom';
438
+ }
439
+ if (gestureState.lockGestureType && gestureState.gestureType !== 'unknown') {
440
+ return gestureState.gestureType;
441
+ }
442
+ // Magic trackpad
443
+ if (gestureState.eventCount >= 3) {
444
+ const avgDeltaY = gestureState.totalDeltaY / gestureState.eventCount;
445
+ const avgDeltaX = gestureState.totalDeltaX / gestureState.eventCount;
446
+ const ratio = avgDeltaX / (avgDeltaY + 0.1);
447
+ const isZoomGesture = avgDeltaY > 8 && ratio < 0.3 && Math.abs(event.deltaY) > 5;
448
+ if (isZoomGesture) {
449
+ gestureState.gestureType = 'zoom';
450
+ gestureState.lockGestureType = true;
451
+ return 'zoom';
452
+ } else {
453
+ gestureState.gestureType = 'pan';
454
+ gestureState.lockGestureType = true;
455
+ return 'pan';
456
+ }
457
+ }
458
+ return 'pan';
459
+ }
460
+ function onWheel(pluginContext, event, domEvent) {
397
461
  event.native.preventDefault();
398
462
  const meta = getScaleOptions.call(pluginContext);
399
463
  if (!meta) {
400
464
  return;
401
465
  }
402
- const { scale, minScale, maxScale, scaleFactor } = meta.scaleOptions;
403
- const dynamicScaleFactor = Math.max(scaleFactor, scale * 0.1);
404
- const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
405
- const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
406
- if (newScale === scale) {
407
- return;
466
+ const gestureType = determineGestureType(event.native, meta.gestureState);
467
+ if (gestureType === 'zoom') {
468
+ handleZoom(pluginContext, event, domEvent);
469
+ } else {
470
+ handlePan(pluginContext, event, domEvent);
408
471
  }
409
- state.transition('SCALING');
410
- const mouseX = event.native.offsetX;
411
- const mouseY = event.native.offsetY;
412
- const scaleDiff = newScale / scale;
413
- meta.scaleOptions.scale = newScale;
472
+ }
473
+ function updateViewport(pluginContext, { stateManager: state, component, matrix }, useAnimation = false) {
414
474
  const highlight = getHighlightInstance.apply(pluginContext);
415
- domEvent.smoothFrame((_, cleanup)=>{
416
- cleanup();
475
+ const doUpdate = ()=>{
417
476
  if (highlight && highlight.highlight) {
418
477
  highlight.highlight.reset();
419
478
  highlight.highlight.setZIndexForHighlight();
420
479
  }
421
- matrix.scale(scaleDiff, scaleDiff);
422
- matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
423
- matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
424
480
  component.cleanup();
425
481
  const { width, height } = component.render.options;
426
482
  component.layoutNodes = component.calculateLayoutNodes(component.data, {
@@ -430,15 +486,56 @@ function onWheel(pluginContext, event, { stateManager: state, component, matrix
430
486
  y: 0
431
487
  }, matrix.a);
432
488
  component.draw(true, false);
433
- domEvent.stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, newScale);
489
+ domEvent.stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, matrix.a);
434
490
  component.update();
435
491
  if (state.canTransition('IDLE')) {
436
492
  state.transition('IDLE');
437
493
  }
438
- return true;
439
- }, {
440
- duration: ANIMATION_DURATION
441
- });
494
+ };
495
+ if (useAnimation) {
496
+ domEvent.smoothFrame((_, cleanup)=>{
497
+ cleanup();
498
+ doUpdate();
499
+ return true;
500
+ }, {
501
+ duration: ANIMATION_DURATION
502
+ });
503
+ } else {
504
+ doUpdate();
505
+ }
506
+ }
507
+ function handleZoom(pluginContext, event, domEvent) {
508
+ const { stateManager: state, matrix } = domEvent;
509
+ const meta = getScaleOptions.call(pluginContext);
510
+ if (!meta) {
511
+ return;
512
+ }
513
+ const { scale, minScale, maxScale, scaleFactor } = meta.scaleOptions;
514
+ const dynamicScaleFactor = Math.max(scaleFactor, scale * 0.1);
515
+ const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
516
+ const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
517
+ if (newScale === scale) {
518
+ return;
519
+ }
520
+ state.transition('SCALING');
521
+ const mouseX = event.native.offsetX;
522
+ const mouseY = event.native.offsetY;
523
+ const scaleDiff = newScale / scale;
524
+ meta.scaleOptions.scale = newScale;
525
+ matrix.scale(scaleDiff, scaleDiff);
526
+ matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
527
+ matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
528
+ updateViewport(pluginContext, domEvent, false);
529
+ }
530
+ function handlePan(pluginContext, event, domEvent) {
531
+ const { stateManager: state, matrix } = domEvent;
532
+ const panSpeed = 0.8;
533
+ const deltaX = event.native.deltaX * panSpeed;
534
+ const deltaY = event.native.deltaY * panSpeed;
535
+ state.transition('PANNING');
536
+ matrix.e -= deltaX;
537
+ matrix.f -= deltaY;
538
+ updateViewport(pluginContext, domEvent, true);
442
539
  }
443
540
 
444
541
  const MAX_SCALE_MULTIPLIER = 2.0;
package/dist/plugin.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { h as definePlugin, H as smoothFrame, r as createRoundBlock, F as stackMatrixTransform, S as Schedule, K as DEFAULT_MATRIX_LOC, I as isScrollWheelOrRightButtonOnMouseupAndDown, G as stackMatrixTransformWithGraphAndLayer, j as isContextMenuEvent, p as hashCode, P as PI_2, o as isWheelEvent, y as mixinWithParams } from './dom-event-BLJt9knO.mjs';
1
+ import { h as definePlugin, H as smoothFrame, r as createRoundBlock, F as stackMatrixTransform, S as Schedule, K as DEFAULT_MATRIX_LOC, I as isScrollWheelOrRightButtonOnMouseupAndDown, G as stackMatrixTransformWithGraphAndLayer, j as isContextMenuEvent, p as hashCode, P as PI_2, o as isWheelEvent, y as mixinWithParams } from './dom-event-Bkw3ecGf.mjs';
2
2
 
3
3
  // Currently, etoile is an internal module, so we won't need too much easing functions.
4
4
  // And the animation logic is implemented by user code.
@@ -375,6 +375,16 @@ function presetScalePlugin(options) {
375
375
  minScale: options?.min || 0.1,
376
376
  maxScale: options?.max || Infinity,
377
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
378
388
  }
379
389
  },
380
390
  onResize ({ matrix, stateManager: state }) {
@@ -391,34 +401,80 @@ function getScaleOptions() {
391
401
  const meta = this.getPluginMetadata('treemap:preset-scale');
392
402
  return meta;
393
403
  }
394
- function onWheel(pluginContext, event, { stateManager: state, component, matrix }) {
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
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) {
395
459
  event.native.preventDefault();
396
460
  const meta = getScaleOptions.call(pluginContext);
397
461
  if (!meta) {
398
462
  return;
399
463
  }
400
- const { scale, minScale, maxScale, scaleFactor } = meta.scaleOptions;
401
- const dynamicScaleFactor = Math.max(scaleFactor, scale * 0.1);
402
- const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
403
- const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
404
- if (newScale === scale) {
405
- return;
464
+ const gestureType = determineGestureType(event.native, meta.gestureState);
465
+ if (gestureType === 'zoom') {
466
+ handleZoom(pluginContext, event, domEvent);
467
+ } else {
468
+ handlePan(pluginContext, event, domEvent);
406
469
  }
407
- state.transition('SCALING');
408
- const mouseX = event.native.offsetX;
409
- const mouseY = event.native.offsetY;
410
- const scaleDiff = newScale / scale;
411
- meta.scaleOptions.scale = newScale;
470
+ }
471
+ function updateViewport(pluginContext, { stateManager: state, component, matrix }, useAnimation = false) {
412
472
  const highlight = getHighlightInstance.apply(pluginContext);
413
- smoothFrame((_, cleanup)=>{
414
- cleanup();
473
+ const doUpdate = ()=>{
415
474
  if (highlight && highlight.highlight) {
416
475
  highlight.highlight.reset();
417
476
  highlight.highlight.setZIndexForHighlight();
418
477
  }
419
- matrix.scale(scaleDiff, scaleDiff);
420
- matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
421
- matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
422
478
  component.cleanup();
423
479
  const { width, height } = component.render.options;
424
480
  component.layoutNodes = component.calculateLayoutNodes(component.data, {
@@ -428,15 +484,56 @@ function onWheel(pluginContext, event, { stateManager: state, component, matrix
428
484
  y: 0
429
485
  }, matrix.a);
430
486
  component.draw(true, false);
431
- stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, newScale);
487
+ stackMatrixTransformWithGraphAndLayer(component.elements, matrix.e, matrix.f, matrix.a);
432
488
  component.update();
433
489
  if (state.canTransition('IDLE')) {
434
490
  state.transition('IDLE');
435
491
  }
436
- return true;
437
- }, {
438
- duration: ANIMATION_DURATION
439
- });
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 } = domEvent;
507
+ const meta = getScaleOptions.call(pluginContext);
508
+ if (!meta) {
509
+ return;
510
+ }
511
+ const { scale, minScale, maxScale, scaleFactor } = meta.scaleOptions;
512
+ const dynamicScaleFactor = Math.max(scaleFactor, scale * 0.1);
513
+ const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
514
+ const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
515
+ if (newScale === scale) {
516
+ return;
517
+ }
518
+ state.transition('SCALING');
519
+ const mouseX = event.native.offsetX;
520
+ const mouseY = event.native.offsetY;
521
+ const scaleDiff = newScale / scale;
522
+ meta.scaleOptions.scale = newScale;
523
+ matrix.scale(scaleDiff, scaleDiff);
524
+ matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
525
+ matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
526
+ updateViewport(pluginContext, domEvent, false);
527
+ }
528
+ function handlePan(pluginContext, event, domEvent) {
529
+ const { stateManager: state, matrix } = domEvent;
530
+ const panSpeed = 0.8;
531
+ const deltaX = event.native.deltaX * panSpeed;
532
+ const deltaY = event.native.deltaY * panSpeed;
533
+ state.transition('PANNING');
534
+ matrix.e -= deltaX;
535
+ matrix.f -= deltaY;
536
+ updateViewport(pluginContext, domEvent, true);
440
537
  }
441
538
 
442
539
  const MAX_SCALE_MULTIPLIER = 2.0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squarified",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "squarified tree map",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",