@knotx/plugins-canvas 0.3.4 → 0.3.5

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.cjs CHANGED
@@ -75,8 +75,8 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
75
75
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
76
76
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
77
77
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
78
- var _init_dec, _render_dec, _setTransform_dec, _zoomOut_dec, _zoomIn_dec, _scrollNodeIntoView_dec, _removeListener_dec, _addListener_dec, _interaction_dec, _container_dec, _getNode_dec, _transform_dec, _ref_dec, _a, _init;
79
- class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref")], _transform_dec = [decorators.register("transform")], _getNode_dec = [decorators.inject.getNode()], _container_dec = [decorators.inject.container()], _interaction_dec = [decorators.inject.interaction()], _addListener_dec = [decorators.register("addListener")], _removeListener_dec = [decorators.register("removeListener")], _scrollNodeIntoView_dec = [decorators.tool("Scroll node into viewport center", {
78
+ var _init_dec, _render_dec, _setTransform_dec, _zoomOut_dec, _zoomIn_dec, _scrollNodeIntoView_dec, _removeListener_dec, _addListener_dec, _interaction_dec, _container_dec, _getNode_dec, _edgeScroll_dec, _transform_dec, _ref_dec, _a, _init;
79
+ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref")], _transform_dec = [decorators.register("transform")], _edgeScroll_dec = [decorators.register("edgeScroll")], _getNode_dec = [decorators.inject.getNode()], _container_dec = [decorators.inject.container()], _interaction_dec = [decorators.inject.interaction()], _addListener_dec = [decorators.register("addListener")], _removeListener_dec = [decorators.register("removeListener")], _scrollNodeIntoView_dec = [decorators.tool("Scroll node into viewport center", {
80
80
  type: "object",
81
81
  properties: {
82
82
  nodeId: { type: "string" },
@@ -122,19 +122,76 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
122
122
  __publicField(this, "name", "canvas");
123
123
  __publicField(this, "ref", __runInitializers(_init, 8, this, null)), __runInitializers(_init, 11, this);
124
124
  __publicField(this, "transform", __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
125
- __publicField(this, "getNode", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
126
- __publicField(this, "container", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
127
- __publicField(this, "interaction", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
125
+ __publicField(this, "edgeScroll", __runInitializers(_init, 16, this, {
126
+ config: {
127
+ enabled: false,
128
+ edgeSize: 50,
129
+ maxSpeed: 10,
130
+ cornerSize: 25
131
+ },
132
+ setConfig: (config) => {
133
+ this.edgeScroll.config = __spreadValues(__spreadValues({}, this.edgeScroll.config), config);
134
+ if (this.edgeScroll.config.enabled && this.edgeScrollAnimationFrame !== null) {
135
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
136
+ this.edgeScrollAnimationFrame = null;
137
+ }
138
+ }
139
+ })), __runInitializers(_init, 19, this);
140
+ __publicField(this, "getNode", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
141
+ __publicField(this, "container", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
142
+ __publicField(this, "interaction", __runInitializers(_init, 28, this)), __runInitializers(_init, 31, this);
128
143
  __publicField(this, "listeners", {
129
144
  click: /* @__PURE__ */ new Set(),
130
145
  contextmenu: /* @__PURE__ */ new Set()
131
146
  });
132
- __publicField(this, "addListener", __runInitializers(_init, 28, this, (type, listener) => {
147
+ __publicField(this, "edgeScrollAnimationFrame", null);
148
+ __publicField(this, "mousePosition", null);
149
+ __publicField(this, "addListener", __runInitializers(_init, 32, this, (type, listener) => {
133
150
  this.listeners[type].add(listener);
134
- })), __runInitializers(_init, 31, this);
135
- __publicField(this, "removeListener", __runInitializers(_init, 32, this, (type, listener) => {
136
- this.listeners[type].delete(listener);
137
151
  })), __runInitializers(_init, 35, this);
152
+ __publicField(this, "removeListener", __runInitializers(_init, 36, this, (type, listener) => {
153
+ this.listeners[type].delete(listener);
154
+ })), __runInitializers(_init, 39, this);
155
+ __publicField(this, "handleEdgeScroll", () => {
156
+ if (!this.edgeScroll.config.enabled || !this.mousePosition || !this.ref) {
157
+ return;
158
+ }
159
+ const { x, y } = this.mousePosition;
160
+ const { edgeSize, maxSpeed, cornerSize = edgeSize / 2 } = this.edgeScroll.config;
161
+ const viewportWidth = this.container.width;
162
+ const viewportHeight = this.container.height;
163
+ let deltaX = 0;
164
+ let deltaY = 0;
165
+ let isCorner = false;
166
+ if (x <= cornerSize && y <= cornerSize || x <= cornerSize && y >= viewportHeight - cornerSize || x >= viewportWidth - cornerSize && y <= cornerSize || x >= viewportWidth - cornerSize && y >= viewportHeight - cornerSize) {
167
+ isCorner = true;
168
+ }
169
+ if (x <= edgeSize) {
170
+ const distance = isCorner ? cornerSize - x : edgeSize - x;
171
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
172
+ deltaX = maxSpeed * factor;
173
+ } else if (x >= viewportWidth - edgeSize) {
174
+ const distance = isCorner ? x - (viewportWidth - cornerSize) : x - (viewportWidth - edgeSize);
175
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
176
+ deltaX = -maxSpeed * factor;
177
+ }
178
+ if (y <= edgeSize) {
179
+ const distance = isCorner ? cornerSize - y : edgeSize - y;
180
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
181
+ deltaY = maxSpeed * factor;
182
+ } else if (y >= viewportHeight - edgeSize) {
183
+ const distance = isCorner ? y - (viewportHeight - cornerSize) : y - (viewportHeight - edgeSize);
184
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
185
+ deltaY = -maxSpeed * factor;
186
+ }
187
+ if (deltaX !== 0 || deltaY !== 0) {
188
+ const currentState = this.ref.instance.transformState;
189
+ const newPositionX = currentState.positionX + deltaX;
190
+ const newPositionY = currentState.positionY + deltaY;
191
+ this.ref.setTransform(newPositionX, newPositionY, currentState.scale, 0);
192
+ }
193
+ this.edgeScrollAnimationFrame = requestAnimationFrame(this.handleEdgeScroll);
194
+ });
138
195
  }
139
196
  scrollNodeIntoView({ nodeId, scale = this.transform.scale, block = "nearest", inline = "nearest", offset = 0, animationTime }) {
140
197
  var _a2, _b, _c, _d, _e, _f, _g;
@@ -213,7 +270,18 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
213
270
  (_a2 = this.ref) == null ? void 0 : _a2.setTransform(positionX != null ? positionX : this.transform.positionX, positionY != null ? positionY : this.transform.positionY, scale != null ? scale : this.transform.scale, animationTime);
214
271
  }
215
272
  render({ children } = { children: null }) {
216
- const [transform, setTransform] = react.useState(null);
273
+ const [transformRef, setTransformRef] = react.useReducer((prevRef, ref) => {
274
+ this.ref = ref;
275
+ if (!prevRef && ref.instance.isInitialized && this.config.defaultLocated) {
276
+ this.scrollNodeIntoView(__spreadValues({
277
+ inline: "center",
278
+ block: "center",
279
+ scale: 1,
280
+ animationTime: 0
281
+ }, this.config.defaultLocated));
282
+ }
283
+ return ref;
284
+ }, null);
217
285
  const [fixedLayers, childrenLayers] = react.useMemo(() => {
218
286
  const fixedLayers2 = [];
219
287
  const childrenLayers2 = [];
@@ -230,35 +298,34 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
230
298
  return [fixedLayers2, childrenLayers2];
231
299
  }, [children]);
232
300
  react.useEffect(() => {
233
- this.ref = transform;
234
- const wrapperElement = transform == null ? void 0 : transform.instance.wrapperComponent;
301
+ const wrapperElement = transformRef == null ? void 0 : transformRef.instance.wrapperComponent;
235
302
  if (!wrapperElement) {
236
303
  return;
237
304
  }
238
305
  Object.defineProperty(wrapperElement, "scrollTop", {
239
306
  get() {
240
- return -transform.instance.transformState.positionY;
307
+ return -transformRef.instance.transformState.positionY;
241
308
  },
242
309
  set(value) {
243
- transform.instance.setTransformState(transform.instance.transformState.scale, transform.instance.transformState.positionX, -value);
310
+ transformRef.instance.setTransformState(transformRef.instance.transformState.scale, transformRef.instance.transformState.positionX, -value);
244
311
  }
245
312
  });
246
313
  Object.defineProperty(wrapperElement, "scrollLeft", {
247
314
  get() {
248
- return -transform.instance.transformState.positionX;
315
+ return -transformRef.instance.transformState.positionX;
249
316
  },
250
317
  set(value) {
251
- transform.instance.setTransformState(transform.instance.transformState.scale, -value, transform.instance.transformState.positionY);
318
+ transformRef.instance.setTransformState(transformRef.instance.transformState.scale, -value, transformRef.instance.transformState.positionY);
252
319
  }
253
320
  });
254
321
  let frame;
255
- return transform == null ? void 0 : transform.instance.onChange((ref) => {
322
+ return transformRef == null ? void 0 : transformRef.instance.onChange((ref) => {
256
323
  cancelAnimationFrame(frame);
257
324
  frame = requestAnimationFrame(() => {
258
325
  this.transform = __spreadValues({}, ref.state);
259
326
  });
260
327
  });
261
- }, [transform]);
328
+ }, [transformRef]);
262
329
  const onClick = react.useCallback((e) => {
263
330
  if (!this.isCanvasEvent(e)) {
264
331
  return;
@@ -281,10 +348,29 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
281
348
  listener(e);
282
349
  });
283
350
  }, []);
351
+ const onMouseMove = react.useCallback((e) => {
352
+ if (this.edgeScroll.config.enabled) {
353
+ const rect = e.currentTarget.getBoundingClientRect();
354
+ this.mousePosition = {
355
+ x: e.clientX - rect.left,
356
+ y: e.clientY - rect.top
357
+ };
358
+ if (this.edgeScrollAnimationFrame === null) {
359
+ this.edgeScrollAnimationFrame = requestAnimationFrame(this.handleEdgeScroll);
360
+ }
361
+ }
362
+ }, []);
363
+ const onMouseLeave = react.useCallback(() => {
364
+ this.mousePosition = null;
365
+ if (this.edgeScrollAnimationFrame !== null) {
366
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
367
+ this.edgeScrollAnimationFrame = null;
368
+ }
369
+ }, []);
284
370
  return /* @__PURE__ */ jsxRuntime.jsxs(
285
371
  reactZoomPanPinch.TransformWrapper,
286
372
  __spreadProps(__spreadValues({}, this.config), {
287
- ref: setTransform,
373
+ ref: setTransformRef,
288
374
  children: [
289
375
  /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: fixedLayers }),
290
376
  /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -299,7 +385,9 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
299
385
  },
300
386
  wrapperProps: {
301
387
  onClick,
302
- onContextMenu
388
+ onContextMenu,
389
+ onMouseMove,
390
+ onMouseLeave
303
391
  },
304
392
  contentClass: core.bem(this.name, "content"),
305
393
  contentStyle: {
@@ -329,6 +417,10 @@ class Canvas extends (_a = core.BasePlugin, _ref_dec = [decorators.register("ref
329
417
  };
330
418
  this.subscriptions.push(() => {
331
419
  Object.values(this.listeners).forEach((set) => set.clear());
420
+ if (this.edgeScrollAnimationFrame !== null) {
421
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
422
+ this.edgeScrollAnimationFrame = null;
423
+ }
332
424
  });
333
425
  }
334
426
  isCanvasEvent(e) {
@@ -344,6 +436,7 @@ __decorateElement(_init, 1, "render", _render_dec, Canvas);
344
436
  __decorateElement(_init, 1, "init", _init_dec, Canvas);
345
437
  __decorateElement(_init, 5, "ref", _ref_dec, Canvas);
346
438
  __decorateElement(_init, 5, "transform", _transform_dec, Canvas);
439
+ __decorateElement(_init, 5, "edgeScroll", _edgeScroll_dec, Canvas);
347
440
  __decorateElement(_init, 5, "getNode", _getNode_dec, Canvas);
348
441
  __decorateElement(_init, 5, "container", _container_dec, Canvas);
349
442
  __decorateElement(_init, 5, "interaction", _interaction_dec, Canvas);
package/dist/index.d.cts CHANGED
@@ -3,9 +3,27 @@ import { InferParamsFromSchema } from '@knotx/decorators';
3
3
  import { MouseEvent, ReactNode } from 'react';
4
4
  import { ReactZoomPanPinchContentRef, ReactZoomPanPinchState, ReactZoomPanPinchProps } from 'react-zoom-pan-pinch';
5
5
 
6
- type CanvasConfig = ReactZoomPanPinchProps;
6
+ type CanvasConfig = ReactZoomPanPinchProps & {
7
+ defaultLocated?: {
8
+ nodeId: string;
9
+ scale?: number;
10
+ block?: 'center' | 'end' | 'nearest' | 'start';
11
+ inline?: 'center' | 'end' | 'nearest' | 'start';
12
+ offset?: number | {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ animationTime?: number;
17
+ };
18
+ };
7
19
  type CanvasTransformState = ReactZoomPanPinchState;
8
20
  type CanvasRef = ReactZoomPanPinchContentRef;
21
+ interface EdgeScrollConfig {
22
+ enabled: boolean;
23
+ edgeSize: number;
24
+ maxSpeed: number;
25
+ cornerSize?: number;
26
+ }
9
27
  declare module '@knotx/core' {
10
28
  interface PluginData {
11
29
  canvas: {
@@ -13,6 +31,10 @@ declare module '@knotx/core' {
13
31
  transform: CanvasTransformState;
14
32
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
15
33
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
34
+ edgeScroll: {
35
+ config: EdgeScrollConfig;
36
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
37
+ };
16
38
  };
17
39
  }
18
40
  interface PluginTools {
@@ -52,10 +74,16 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
52
74
  name: "canvas";
53
75
  ref: CanvasRef | null;
54
76
  transform: CanvasTransformState;
77
+ edgeScroll: {
78
+ config: EdgeScrollConfig;
79
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
80
+ };
55
81
  private getNode;
56
82
  private container;
57
83
  interaction: Engine['interaction'];
58
84
  private listeners;
85
+ private edgeScrollAnimationFrame;
86
+ private mousePosition;
59
87
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
60
88
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
61
89
  scrollNodeIntoView({ nodeId, scale, block, inline, offset, animationTime }: InferParamsFromSchema<{
@@ -131,6 +159,7 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
131
159
  };
132
160
  };
133
161
  }>): void;
162
+ private handleEdgeScroll;
134
163
  render({ children }?: {
135
164
  children: ReactNode;
136
165
  }): JSX.Element;
@@ -138,4 +167,4 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
138
167
  private isCanvasEvent;
139
168
  }
140
169
 
141
- export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState };
170
+ export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState, type EdgeScrollConfig };
package/dist/index.d.mts CHANGED
@@ -3,9 +3,27 @@ import { InferParamsFromSchema } from '@knotx/decorators';
3
3
  import { MouseEvent, ReactNode } from 'react';
4
4
  import { ReactZoomPanPinchContentRef, ReactZoomPanPinchState, ReactZoomPanPinchProps } from 'react-zoom-pan-pinch';
5
5
 
6
- type CanvasConfig = ReactZoomPanPinchProps;
6
+ type CanvasConfig = ReactZoomPanPinchProps & {
7
+ defaultLocated?: {
8
+ nodeId: string;
9
+ scale?: number;
10
+ block?: 'center' | 'end' | 'nearest' | 'start';
11
+ inline?: 'center' | 'end' | 'nearest' | 'start';
12
+ offset?: number | {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ animationTime?: number;
17
+ };
18
+ };
7
19
  type CanvasTransformState = ReactZoomPanPinchState;
8
20
  type CanvasRef = ReactZoomPanPinchContentRef;
21
+ interface EdgeScrollConfig {
22
+ enabled: boolean;
23
+ edgeSize: number;
24
+ maxSpeed: number;
25
+ cornerSize?: number;
26
+ }
9
27
  declare module '@knotx/core' {
10
28
  interface PluginData {
11
29
  canvas: {
@@ -13,6 +31,10 @@ declare module '@knotx/core' {
13
31
  transform: CanvasTransformState;
14
32
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
15
33
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
34
+ edgeScroll: {
35
+ config: EdgeScrollConfig;
36
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
37
+ };
16
38
  };
17
39
  }
18
40
  interface PluginTools {
@@ -52,10 +74,16 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
52
74
  name: "canvas";
53
75
  ref: CanvasRef | null;
54
76
  transform: CanvasTransformState;
77
+ edgeScroll: {
78
+ config: EdgeScrollConfig;
79
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
80
+ };
55
81
  private getNode;
56
82
  private container;
57
83
  interaction: Engine['interaction'];
58
84
  private listeners;
85
+ private edgeScrollAnimationFrame;
86
+ private mousePosition;
59
87
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
60
88
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
61
89
  scrollNodeIntoView({ nodeId, scale, block, inline, offset, animationTime }: InferParamsFromSchema<{
@@ -131,6 +159,7 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
131
159
  };
132
160
  };
133
161
  }>): void;
162
+ private handleEdgeScroll;
134
163
  render({ children }?: {
135
164
  children: ReactNode;
136
165
  }): JSX.Element;
@@ -138,4 +167,4 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
138
167
  private isCanvasEvent;
139
168
  }
140
169
 
141
- export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState };
170
+ export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState, type EdgeScrollConfig };
package/dist/index.d.ts CHANGED
@@ -3,9 +3,27 @@ import { InferParamsFromSchema } from '@knotx/decorators';
3
3
  import { MouseEvent, ReactNode } from 'react';
4
4
  import { ReactZoomPanPinchContentRef, ReactZoomPanPinchState, ReactZoomPanPinchProps } from 'react-zoom-pan-pinch';
5
5
 
6
- type CanvasConfig = ReactZoomPanPinchProps;
6
+ type CanvasConfig = ReactZoomPanPinchProps & {
7
+ defaultLocated?: {
8
+ nodeId: string;
9
+ scale?: number;
10
+ block?: 'center' | 'end' | 'nearest' | 'start';
11
+ inline?: 'center' | 'end' | 'nearest' | 'start';
12
+ offset?: number | {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ animationTime?: number;
17
+ };
18
+ };
7
19
  type CanvasTransformState = ReactZoomPanPinchState;
8
20
  type CanvasRef = ReactZoomPanPinchContentRef;
21
+ interface EdgeScrollConfig {
22
+ enabled: boolean;
23
+ edgeSize: number;
24
+ maxSpeed: number;
25
+ cornerSize?: number;
26
+ }
9
27
  declare module '@knotx/core' {
10
28
  interface PluginData {
11
29
  canvas: {
@@ -13,6 +31,10 @@ declare module '@knotx/core' {
13
31
  transform: CanvasTransformState;
14
32
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
15
33
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
34
+ edgeScroll: {
35
+ config: EdgeScrollConfig;
36
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
37
+ };
16
38
  };
17
39
  }
18
40
  interface PluginTools {
@@ -52,10 +74,16 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
52
74
  name: "canvas";
53
75
  ref: CanvasRef | null;
54
76
  transform: CanvasTransformState;
77
+ edgeScroll: {
78
+ config: EdgeScrollConfig;
79
+ setConfig: (config: Partial<EdgeScrollConfig>) => void;
80
+ };
55
81
  private getNode;
56
82
  private container;
57
83
  interaction: Engine['interaction'];
58
84
  private listeners;
85
+ private edgeScrollAnimationFrame;
86
+ private mousePosition;
59
87
  addListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
60
88
  removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void;
61
89
  scrollNodeIntoView({ nodeId, scale, block, inline, offset, animationTime }: InferParamsFromSchema<{
@@ -131,6 +159,7 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
131
159
  };
132
160
  };
133
161
  }>): void;
162
+ private handleEdgeScroll;
134
163
  render({ children }?: {
135
164
  children: ReactNode;
136
165
  }): JSX.Element;
@@ -138,4 +167,4 @@ declare class Canvas extends BasePlugin<'canvas', CanvasConfig> {
138
167
  private isCanvasEvent;
139
168
  }
140
169
 
141
- export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState };
170
+ export { Canvas, type CanvasConfig, type CanvasEventListener, type CanvasEventType, type CanvasRef, type CanvasTransformState, type EdgeScrollConfig };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { jsxs, jsx } from '@knotx/jsx/jsx-runtime';
2
2
  import { Layer, InteractionPriority, bem, BasePlugin } from '@knotx/core';
3
3
  import { register, inject, tool, layer, OnInit } from '@knotx/decorators';
4
4
  import { merge } from 'lodash-es';
5
- import { useState, useMemo, useEffect, useCallback, Fragment } from 'react';
5
+ import { useReducer, useMemo, useEffect, useCallback, Fragment } from 'react';
6
6
  import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
7
7
 
8
8
  var __create = Object.create;
@@ -73,8 +73,8 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
73
73
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
74
74
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
75
75
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
76
- var _init_dec, _render_dec, _setTransform_dec, _zoomOut_dec, _zoomIn_dec, _scrollNodeIntoView_dec, _removeListener_dec, _addListener_dec, _interaction_dec, _container_dec, _getNode_dec, _transform_dec, _ref_dec, _a, _init;
77
- class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_dec = [register("transform")], _getNode_dec = [inject.getNode()], _container_dec = [inject.container()], _interaction_dec = [inject.interaction()], _addListener_dec = [register("addListener")], _removeListener_dec = [register("removeListener")], _scrollNodeIntoView_dec = [tool("Scroll node into viewport center", {
76
+ var _init_dec, _render_dec, _setTransform_dec, _zoomOut_dec, _zoomIn_dec, _scrollNodeIntoView_dec, _removeListener_dec, _addListener_dec, _interaction_dec, _container_dec, _getNode_dec, _edgeScroll_dec, _transform_dec, _ref_dec, _a, _init;
77
+ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_dec = [register("transform")], _edgeScroll_dec = [register("edgeScroll")], _getNode_dec = [inject.getNode()], _container_dec = [inject.container()], _interaction_dec = [inject.interaction()], _addListener_dec = [register("addListener")], _removeListener_dec = [register("removeListener")], _scrollNodeIntoView_dec = [tool("Scroll node into viewport center", {
78
78
  type: "object",
79
79
  properties: {
80
80
  nodeId: { type: "string" },
@@ -120,19 +120,76 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
120
120
  __publicField(this, "name", "canvas");
121
121
  __publicField(this, "ref", __runInitializers(_init, 8, this, null)), __runInitializers(_init, 11, this);
122
122
  __publicField(this, "transform", __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
123
- __publicField(this, "getNode", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
124
- __publicField(this, "container", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
125
- __publicField(this, "interaction", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
123
+ __publicField(this, "edgeScroll", __runInitializers(_init, 16, this, {
124
+ config: {
125
+ enabled: false,
126
+ edgeSize: 50,
127
+ maxSpeed: 10,
128
+ cornerSize: 25
129
+ },
130
+ setConfig: (config) => {
131
+ this.edgeScroll.config = __spreadValues(__spreadValues({}, this.edgeScroll.config), config);
132
+ if (this.edgeScroll.config.enabled && this.edgeScrollAnimationFrame !== null) {
133
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
134
+ this.edgeScrollAnimationFrame = null;
135
+ }
136
+ }
137
+ })), __runInitializers(_init, 19, this);
138
+ __publicField(this, "getNode", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
139
+ __publicField(this, "container", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
140
+ __publicField(this, "interaction", __runInitializers(_init, 28, this)), __runInitializers(_init, 31, this);
126
141
  __publicField(this, "listeners", {
127
142
  click: /* @__PURE__ */ new Set(),
128
143
  contextmenu: /* @__PURE__ */ new Set()
129
144
  });
130
- __publicField(this, "addListener", __runInitializers(_init, 28, this, (type, listener) => {
145
+ __publicField(this, "edgeScrollAnimationFrame", null);
146
+ __publicField(this, "mousePosition", null);
147
+ __publicField(this, "addListener", __runInitializers(_init, 32, this, (type, listener) => {
131
148
  this.listeners[type].add(listener);
132
- })), __runInitializers(_init, 31, this);
133
- __publicField(this, "removeListener", __runInitializers(_init, 32, this, (type, listener) => {
134
- this.listeners[type].delete(listener);
135
149
  })), __runInitializers(_init, 35, this);
150
+ __publicField(this, "removeListener", __runInitializers(_init, 36, this, (type, listener) => {
151
+ this.listeners[type].delete(listener);
152
+ })), __runInitializers(_init, 39, this);
153
+ __publicField(this, "handleEdgeScroll", () => {
154
+ if (!this.edgeScroll.config.enabled || !this.mousePosition || !this.ref) {
155
+ return;
156
+ }
157
+ const { x, y } = this.mousePosition;
158
+ const { edgeSize, maxSpeed, cornerSize = edgeSize / 2 } = this.edgeScroll.config;
159
+ const viewportWidth = this.container.width;
160
+ const viewportHeight = this.container.height;
161
+ let deltaX = 0;
162
+ let deltaY = 0;
163
+ let isCorner = false;
164
+ if (x <= cornerSize && y <= cornerSize || x <= cornerSize && y >= viewportHeight - cornerSize || x >= viewportWidth - cornerSize && y <= cornerSize || x >= viewportWidth - cornerSize && y >= viewportHeight - cornerSize) {
165
+ isCorner = true;
166
+ }
167
+ if (x <= edgeSize) {
168
+ const distance = isCorner ? cornerSize - x : edgeSize - x;
169
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
170
+ deltaX = maxSpeed * factor;
171
+ } else if (x >= viewportWidth - edgeSize) {
172
+ const distance = isCorner ? x - (viewportWidth - cornerSize) : x - (viewportWidth - edgeSize);
173
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
174
+ deltaX = -maxSpeed * factor;
175
+ }
176
+ if (y <= edgeSize) {
177
+ const distance = isCorner ? cornerSize - y : edgeSize - y;
178
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
179
+ deltaY = maxSpeed * factor;
180
+ } else if (y >= viewportHeight - edgeSize) {
181
+ const distance = isCorner ? y - (viewportHeight - cornerSize) : y - (viewportHeight - edgeSize);
182
+ const factor = Math.min(distance / (isCorner ? cornerSize : edgeSize), 1);
183
+ deltaY = -maxSpeed * factor;
184
+ }
185
+ if (deltaX !== 0 || deltaY !== 0) {
186
+ const currentState = this.ref.instance.transformState;
187
+ const newPositionX = currentState.positionX + deltaX;
188
+ const newPositionY = currentState.positionY + deltaY;
189
+ this.ref.setTransform(newPositionX, newPositionY, currentState.scale, 0);
190
+ }
191
+ this.edgeScrollAnimationFrame = requestAnimationFrame(this.handleEdgeScroll);
192
+ });
136
193
  }
137
194
  scrollNodeIntoView({ nodeId, scale = this.transform.scale, block = "nearest", inline = "nearest", offset = 0, animationTime }) {
138
195
  var _a2, _b, _c, _d, _e, _f, _g;
@@ -211,7 +268,18 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
211
268
  (_a2 = this.ref) == null ? void 0 : _a2.setTransform(positionX != null ? positionX : this.transform.positionX, positionY != null ? positionY : this.transform.positionY, scale != null ? scale : this.transform.scale, animationTime);
212
269
  }
213
270
  render({ children } = { children: null }) {
214
- const [transform, setTransform] = useState(null);
271
+ const [transformRef, setTransformRef] = useReducer((prevRef, ref) => {
272
+ this.ref = ref;
273
+ if (!prevRef && ref.instance.isInitialized && this.config.defaultLocated) {
274
+ this.scrollNodeIntoView(__spreadValues({
275
+ inline: "center",
276
+ block: "center",
277
+ scale: 1,
278
+ animationTime: 0
279
+ }, this.config.defaultLocated));
280
+ }
281
+ return ref;
282
+ }, null);
215
283
  const [fixedLayers, childrenLayers] = useMemo(() => {
216
284
  const fixedLayers2 = [];
217
285
  const childrenLayers2 = [];
@@ -228,35 +296,34 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
228
296
  return [fixedLayers2, childrenLayers2];
229
297
  }, [children]);
230
298
  useEffect(() => {
231
- this.ref = transform;
232
- const wrapperElement = transform == null ? void 0 : transform.instance.wrapperComponent;
299
+ const wrapperElement = transformRef == null ? void 0 : transformRef.instance.wrapperComponent;
233
300
  if (!wrapperElement) {
234
301
  return;
235
302
  }
236
303
  Object.defineProperty(wrapperElement, "scrollTop", {
237
304
  get() {
238
- return -transform.instance.transformState.positionY;
305
+ return -transformRef.instance.transformState.positionY;
239
306
  },
240
307
  set(value) {
241
- transform.instance.setTransformState(transform.instance.transformState.scale, transform.instance.transformState.positionX, -value);
308
+ transformRef.instance.setTransformState(transformRef.instance.transformState.scale, transformRef.instance.transformState.positionX, -value);
242
309
  }
243
310
  });
244
311
  Object.defineProperty(wrapperElement, "scrollLeft", {
245
312
  get() {
246
- return -transform.instance.transformState.positionX;
313
+ return -transformRef.instance.transformState.positionX;
247
314
  },
248
315
  set(value) {
249
- transform.instance.setTransformState(transform.instance.transformState.scale, -value, transform.instance.transformState.positionY);
316
+ transformRef.instance.setTransformState(transformRef.instance.transformState.scale, -value, transformRef.instance.transformState.positionY);
250
317
  }
251
318
  });
252
319
  let frame;
253
- return transform == null ? void 0 : transform.instance.onChange((ref) => {
320
+ return transformRef == null ? void 0 : transformRef.instance.onChange((ref) => {
254
321
  cancelAnimationFrame(frame);
255
322
  frame = requestAnimationFrame(() => {
256
323
  this.transform = __spreadValues({}, ref.state);
257
324
  });
258
325
  });
259
- }, [transform]);
326
+ }, [transformRef]);
260
327
  const onClick = useCallback((e) => {
261
328
  if (!this.isCanvasEvent(e)) {
262
329
  return;
@@ -279,10 +346,29 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
279
346
  listener(e);
280
347
  });
281
348
  }, []);
349
+ const onMouseMove = useCallback((e) => {
350
+ if (this.edgeScroll.config.enabled) {
351
+ const rect = e.currentTarget.getBoundingClientRect();
352
+ this.mousePosition = {
353
+ x: e.clientX - rect.left,
354
+ y: e.clientY - rect.top
355
+ };
356
+ if (this.edgeScrollAnimationFrame === null) {
357
+ this.edgeScrollAnimationFrame = requestAnimationFrame(this.handleEdgeScroll);
358
+ }
359
+ }
360
+ }, []);
361
+ const onMouseLeave = useCallback(() => {
362
+ this.mousePosition = null;
363
+ if (this.edgeScrollAnimationFrame !== null) {
364
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
365
+ this.edgeScrollAnimationFrame = null;
366
+ }
367
+ }, []);
282
368
  return /* @__PURE__ */ jsxs(
283
369
  TransformWrapper,
284
370
  __spreadProps(__spreadValues({}, this.config), {
285
- ref: setTransform,
371
+ ref: setTransformRef,
286
372
  children: [
287
373
  /* @__PURE__ */ jsx(Fragment, { children: fixedLayers }),
288
374
  /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
@@ -297,7 +383,9 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
297
383
  },
298
384
  wrapperProps: {
299
385
  onClick,
300
- onContextMenu
386
+ onContextMenu,
387
+ onMouseMove,
388
+ onMouseLeave
301
389
  },
302
390
  contentClass: bem(this.name, "content"),
303
391
  contentStyle: {
@@ -327,6 +415,10 @@ class Canvas extends (_a = BasePlugin, _ref_dec = [register("ref")], _transform_
327
415
  };
328
416
  this.subscriptions.push(() => {
329
417
  Object.values(this.listeners).forEach((set) => set.clear());
418
+ if (this.edgeScrollAnimationFrame !== null) {
419
+ cancelAnimationFrame(this.edgeScrollAnimationFrame);
420
+ this.edgeScrollAnimationFrame = null;
421
+ }
330
422
  });
331
423
  }
332
424
  isCanvasEvent(e) {
@@ -342,6 +434,7 @@ __decorateElement(_init, 1, "render", _render_dec, Canvas);
342
434
  __decorateElement(_init, 1, "init", _init_dec, Canvas);
343
435
  __decorateElement(_init, 5, "ref", _ref_dec, Canvas);
344
436
  __decorateElement(_init, 5, "transform", _transform_dec, Canvas);
437
+ __decorateElement(_init, 5, "edgeScroll", _edgeScroll_dec, Canvas);
345
438
  __decorateElement(_init, 5, "getNode", _getNode_dec, Canvas);
346
439
  __decorateElement(_init, 5, "container", _container_dec, Canvas);
347
440
  __decorateElement(_init, 5, "interaction", _interaction_dec, Canvas);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/plugins-canvas",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Canvas Plugin for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -29,23 +29,23 @@
29
29
  ],
30
30
  "peerDependencies": {
31
31
  "react": ">=17.0.0",
32
- "@knotx/jsx": "0.3.4"
32
+ "@knotx/jsx": "0.3.5"
33
33
  },
34
34
  "dependencies": {
35
35
  "lodash-es": "^4.17.21",
36
36
  "react-zoom-pan-pinch": "^3.7.0",
37
37
  "rxjs": "^7.8.1",
38
- "@knotx/core": "0.3.4",
39
- "@knotx/decorators": "0.3.4"
38
+ "@knotx/core": "0.3.5",
39
+ "@knotx/decorators": "0.3.5"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/lodash-es": "^4.17.12",
43
43
  "@types/react": "^17.0.0",
44
44
  "react": "^17.0.0",
45
- "@knotx/build-config": "0.3.4",
46
- "@knotx/eslint-config": "0.3.4",
47
- "@knotx/jsx": "0.3.4",
48
- "@knotx/typescript-config": "0.3.4"
45
+ "@knotx/build-config": "0.3.5",
46
+ "@knotx/eslint-config": "0.3.5",
47
+ "@knotx/jsx": "0.3.5",
48
+ "@knotx/typescript-config": "0.3.5"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "unbuild",