@tft/interact 12.5.2 → 14.0.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.
Files changed (102) hide show
  1. package/esm2020/index.mjs +6 -0
  2. package/esm2020/lib/directives/account-for-scale.directive.mjs +26 -0
  3. package/esm2020/lib/directives/apply-scale.directive.mjs +28 -0
  4. package/esm2020/lib/directives/auto-scroll.directive.mjs +56 -0
  5. package/esm2020/lib/directives/drag-preview.directive.mjs +20 -0
  6. package/esm2020/lib/directives/drag-root.directive.mjs +32 -0
  7. package/esm2020/lib/directives/draggable.directive.mjs +320 -0
  8. package/esm2020/lib/directives/dropzone.directive.mjs +114 -0
  9. package/esm2020/lib/directives/gesturable.directive.mjs +82 -0
  10. package/esm2020/lib/directives/index.mjs +10 -0
  11. package/esm2020/lib/directives/resizable.directive.mjs +228 -0
  12. package/esm2020/lib/interact.module.mjs +66 -0
  13. package/esm2020/lib/models/index.mjs +2 -0
  14. package/esm2020/lib/models/interact.mjs +28 -0
  15. package/esm2020/lib/pipes/array-of-n.pipe.mjs +22 -0
  16. package/esm2020/lib/pipes/index.mjs +2 -0
  17. package/esm2020/lib/services/interact.service.mjs +197 -0
  18. package/esm2020/lib/utils/helpers.mjs +35 -0
  19. package/esm2020/lib/utils/index.mjs +2 -0
  20. package/esm2020/tft-interact.mjs +5 -0
  21. package/fesm2015/{tft-interact.js → tft-interact.mjs} +464 -267
  22. package/fesm2015/tft-interact.mjs.map +1 -0
  23. package/fesm2020/tft-interact.mjs +1192 -0
  24. package/fesm2020/tft-interact.mjs.map +1 -0
  25. package/lib/directives/account-for-scale.directive.d.ts +3 -0
  26. package/lib/directives/apply-scale.directive.d.ts +3 -0
  27. package/lib/directives/auto-scroll.directive.d.ts +34 -0
  28. package/lib/directives/drag-preview.directive.d.ts +3 -0
  29. package/lib/directives/drag-root.directive.d.ts +3 -0
  30. package/lib/directives/draggable.directive.d.ts +8 -1
  31. package/lib/directives/dropzone.directive.d.ts +5 -0
  32. package/lib/directives/gesturable.directive.d.ts +3 -0
  33. package/lib/directives/index.d.ts +1 -0
  34. package/lib/directives/resizable.directive.d.ts +10 -2
  35. package/lib/interact.module.d.ts +15 -0
  36. package/lib/pipes/array-of-n.pipe.d.ts +3 -0
  37. package/lib/services/interact.service.d.ts +3 -0
  38. package/package.json +24 -11
  39. package/tft-interact-14.0.0.tgz +0 -0
  40. package/bundles/tft-interact.umd.js +0 -1419
  41. package/bundles/tft-interact.umd.js.map +0 -1
  42. package/esm2015/index.js +0 -6
  43. package/esm2015/index.js.map +0 -1
  44. package/esm2015/index.metadata.json +0 -1
  45. package/esm2015/lib/directives/account-for-scale.directive.js +0 -26
  46. package/esm2015/lib/directives/account-for-scale.directive.js.map +0 -1
  47. package/esm2015/lib/directives/account-for-scale.directive.metadata.json +0 -1
  48. package/esm2015/lib/directives/apply-scale.directive.js +0 -26
  49. package/esm2015/lib/directives/apply-scale.directive.js.map +0 -1
  50. package/esm2015/lib/directives/apply-scale.directive.metadata.json +0 -1
  51. package/esm2015/lib/directives/drag-preview.directive.js +0 -20
  52. package/esm2015/lib/directives/drag-preview.directive.js.map +0 -1
  53. package/esm2015/lib/directives/drag-preview.directive.metadata.json +0 -1
  54. package/esm2015/lib/directives/drag-root.directive.js +0 -27
  55. package/esm2015/lib/directives/drag-root.directive.js.map +0 -1
  56. package/esm2015/lib/directives/drag-root.directive.metadata.json +0 -1
  57. package/esm2015/lib/directives/draggable.directive.js +0 -265
  58. package/esm2015/lib/directives/draggable.directive.js.map +0 -1
  59. package/esm2015/lib/directives/draggable.directive.metadata.json +0 -1
  60. package/esm2015/lib/directives/dropzone.directive.js +0 -103
  61. package/esm2015/lib/directives/dropzone.directive.js.map +0 -1
  62. package/esm2015/lib/directives/dropzone.directive.metadata.json +0 -1
  63. package/esm2015/lib/directives/gesturable.directive.js +0 -77
  64. package/esm2015/lib/directives/gesturable.directive.js.map +0 -1
  65. package/esm2015/lib/directives/gesturable.directive.metadata.json +0 -1
  66. package/esm2015/lib/directives/index.js +0 -9
  67. package/esm2015/lib/directives/index.js.map +0 -1
  68. package/esm2015/lib/directives/index.metadata.json +0 -1
  69. package/esm2015/lib/directives/resizable.directive.js +0 -175
  70. package/esm2015/lib/directives/resizable.directive.js.map +0 -1
  71. package/esm2015/lib/directives/resizable.directive.metadata.json +0 -1
  72. package/esm2015/lib/interact.module.js +0 -42
  73. package/esm2015/lib/interact.module.js.map +0 -1
  74. package/esm2015/lib/interact.module.metadata.json +0 -1
  75. package/esm2015/lib/models/index.js +0 -2
  76. package/esm2015/lib/models/index.js.map +0 -1
  77. package/esm2015/lib/models/index.metadata.json +0 -1
  78. package/esm2015/lib/models/interact.js +0 -28
  79. package/esm2015/lib/models/interact.js.map +0 -1
  80. package/esm2015/lib/models/interact.metadata.json +0 -1
  81. package/esm2015/lib/pipes/array-of-n.pipe.js +0 -18
  82. package/esm2015/lib/pipes/array-of-n.pipe.js.map +0 -1
  83. package/esm2015/lib/pipes/array-of-n.pipe.metadata.json +0 -1
  84. package/esm2015/lib/pipes/index.js +0 -2
  85. package/esm2015/lib/pipes/index.js.map +0 -1
  86. package/esm2015/lib/pipes/index.metadata.json +0 -1
  87. package/esm2015/lib/services/interact.service.js +0 -196
  88. package/esm2015/lib/services/interact.service.js.map +0 -1
  89. package/esm2015/lib/services/interact.service.metadata.json +0 -1
  90. package/esm2015/lib/utils/helpers.js +0 -35
  91. package/esm2015/lib/utils/helpers.js.map +0 -1
  92. package/esm2015/lib/utils/helpers.metadata.json +0 -1
  93. package/esm2015/lib/utils/index.js +0 -2
  94. package/esm2015/lib/utils/index.js.map +0 -1
  95. package/esm2015/lib/utils/index.metadata.json +0 -1
  96. package/esm2015/tft-interact.js +0 -7
  97. package/esm2015/tft-interact.js.map +0 -1
  98. package/esm2015/tft-interact.metadata.json +0 -1
  99. package/fesm2015/tft-interact.js.map +0 -1
  100. package/tft-interact-12.5.2.tgz +0 -0
  101. package/tft-interact.d.ts +0 -6
  102. package/tft-interact.metadata.json +0 -1
@@ -0,0 +1,1192 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Pipe, InjectionToken, Injectable, Optional, Inject, Directive, Input, EventEmitter, HostBinding, Output, Self, HostListener, SkipSelf, ContentChild, NgModule } from '@angular/core';
3
+ import { DOCUMENT, CommonModule } from '@angular/common';
4
+ import interact from 'interactjs';
5
+ import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
6
+ import { shareReplay, tap, map, scan } from 'rxjs/operators';
7
+
8
+ class ArrayOfNPipe {
9
+ /**
10
+ *
11
+ * @param n the number to make an array of
12
+ * @param max the max length of the array (useful for limiting rendering in ngFor loop)
13
+ */
14
+ transform(n, max) {
15
+ const length = ((max && max < n) || n === Infinity) ? max : n;
16
+ return new Array(length).fill(length).map((x, i) => i);
17
+ }
18
+ }
19
+ ArrayOfNPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ArrayOfNPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
20
+ ArrayOfNPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: ArrayOfNPipe, name: "arrayOfN" });
21
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ArrayOfNPipe, decorators: [{
22
+ type: Pipe,
23
+ args: [{
24
+ name: 'arrayOfN'
25
+ }]
26
+ }] });
27
+
28
+ const INTERACT_ROOT_CONFIG = new InjectionToken('INTERACT_ROOT_CONFIG_PARAMS');
29
+ const DEFAULT_REGISTRY_ID = 'DEFAULT';
30
+ /**
31
+ * Default initial value for Delta BehaviorSubject
32
+ */
33
+ const defaultDelta = {
34
+ deltaX: 0,
35
+ deltaY: 0,
36
+ targetElement: null
37
+ };
38
+ /**
39
+ * Default initial value for Size BehaviorSubject
40
+ */
41
+ const defaultSize = {
42
+ width: null,
43
+ height: null,
44
+ targetElement: null
45
+ };
46
+ /**
47
+ * Default initial value for Position BehaviorSubject
48
+ */
49
+ const defaultPosition = {
50
+ x: 0,
51
+ y: 0,
52
+ targetElement: null
53
+ };
54
+
55
+ class InteractService {
56
+ constructor(rendererFactory, ngZone, rootConfig) {
57
+ this.rendererFactory = rendererFactory;
58
+ this.ngZone = ngZone;
59
+ this.rootConfig = rootConfig;
60
+ this._interactableIndex = 0;
61
+ this._dropzoneIndex = 0;
62
+ // TODO: consider using Map here
63
+ this.dragRegistrySystem = {};
64
+ this.cssUnit = 'px';
65
+ this.renderer = this.rendererFactory.createRenderer(null, null);
66
+ this.cssUnit = rootConfig?.cssDimensionUnit || 'px';
67
+ }
68
+ addDeltaToPosition(delta, position) {
69
+ return {
70
+ x: delta.deltaX + position.x,
71
+ y: delta.deltaY + position.y,
72
+ targetElement: position.targetElement || delta.targetElement
73
+ };
74
+ }
75
+ addRegistryToSystem(customRegistryId = null) {
76
+ const registryId = customRegistryId || this.createDropzoneId(this._dropzoneIndex++);
77
+ this.dragRegistrySystem[registryId] = {};
78
+ return registryId;
79
+ }
80
+ addDraggableToRegistry(customRegistryId = null, interactableId = null) {
81
+ const registryId = customRegistryId || DEFAULT_REGISTRY_ID;
82
+ // if we pass an id to createInteractableId then it is used when creating the interactable
83
+ // and returned, otherwise a new id is created for the draggable.
84
+ const key = this.createInteractableId(interactableId);
85
+ if (!Object.prototype.hasOwnProperty.call(this.dragRegistrySystem, registryId)) {
86
+ this.dragRegistrySystem[registryId] = {};
87
+ }
88
+ this.dragRegistrySystem[registryId][key] = this.createInteractableState(defaultPosition, defaultSize, defaultDelta);
89
+ return key;
90
+ }
91
+ destroyInteractable(interactableId, registryId = DEFAULT_REGISTRY_ID) {
92
+ const interactableSystem = this.dragRegistrySystem[registryId][interactableId];
93
+ if (interactableSystem) {
94
+ // unsubscribe from all the subscriptions if they're subscriptions
95
+ for (const propName in interactableSystem) {
96
+ const prop = interactableSystem[propName];
97
+ if (prop.unsubscribe && typeof prop.unsubscribe === 'function') {
98
+ prop.unsubscribe();
99
+ }
100
+ ;
101
+ }
102
+ delete this.dragRegistrySystem[registryId][interactableId];
103
+ }
104
+ }
105
+ subscribeToInteractable(interactableId, registryId = DEFAULT_REGISTRY_ID) {
106
+ return this.dragRegistrySystem[registryId][interactableId];
107
+ }
108
+ createInteractableState(initialPosition, initialSize, initialDelta) {
109
+ const deltas$ = new BehaviorSubject(initialDelta);
110
+ // tracks the size of the element
111
+ const size$ = new BehaviorSubject({ ...initialSize, ...initialDelta });
112
+ // Stream of positions as they change
113
+ const position$ = new BehaviorSubject(initialPosition);
114
+ // All draggable data mapped together
115
+ const interactable$ = combineLatest([
116
+ size$.pipe(
117
+ // startWith({...initialSize, ...initialDelta}),
118
+ shareReplay(1),
119
+ // filter(resizeEvent => !!resizeEvent.targetElement),
120
+ tap(({ deltaX, deltaY, width, height, targetElement }) => {
121
+ this.ngZone.runOutsideAngular(() => {
122
+ this.setElementSize(width, height, targetElement);
123
+ });
124
+ // only reposition if necessary i.e when resizing left or up
125
+ if (deltaX || deltaY) {
126
+ deltas$.next({ deltaX, deltaY, targetElement });
127
+ }
128
+ })),
129
+ deltas$.pipe(shareReplay(1), tap((delta) => {
130
+ const position = position$.value;
131
+ const newPosition = this.addDeltaToPosition(delta, position);
132
+ position$.next(newPosition);
133
+ })),
134
+ position$.pipe(shareReplay(1), tap(position => {
135
+ this.ngZone.runOutsideAngular(() => {
136
+ this.setElementTransform(position.x, position.y, position);
137
+ });
138
+ }))
139
+ ]).pipe(shareReplay(1), map(([size, deltas, position]) => {
140
+ return {
141
+ x: position.x,
142
+ y: position.y,
143
+ deltaX: deltas.deltaX,
144
+ deltaY: deltas.deltaY,
145
+ width: size.width,
146
+ height: size.height,
147
+ targetElement: size.targetElement || position.targetElement || deltas.targetElement
148
+ };
149
+ }));
150
+ // TODO: update system to handle only returning interactable$
151
+ return {
152
+ deltas$,
153
+ position$,
154
+ size$,
155
+ interactable$
156
+ };
157
+ }
158
+ createInteractableId(customInteractableId = null) {
159
+ return customInteractableId || `interactable${this._interactableIndex++}`;
160
+ }
161
+ createDropzoneId(index) {
162
+ return `dropzone${index}`;
163
+ }
164
+ getInteractableState(interactableId, registryId = DEFAULT_REGISTRY_ID) {
165
+ return this.dragRegistrySystem[registryId][interactableId].interactable$;
166
+ }
167
+ updateDeltas(interactId, registryId, { deltaX, deltaY }, targetElement) {
168
+ if (!this.interactableExistOnRegistry(interactId, registryId))
169
+ return;
170
+ this.dragRegistrySystem[registryId][interactId].deltas$.next({ deltaX, deltaY, targetElement });
171
+ }
172
+ updatePosition(interactId, registryId, { x, y }, targetElement) {
173
+ if (!this.interactableExistOnRegistry(interactId, registryId))
174
+ return;
175
+ this.dragRegistrySystem[registryId][interactId].position$.next({ x, y, targetElement });
176
+ }
177
+ updateSize(interactId, registryId, { deltaX, deltaY, width, height }, targetElement) {
178
+ if (!this.interactableExistOnRegistry(interactId, registryId))
179
+ return;
180
+ this.dragRegistrySystem[registryId][interactId].size$.next({ deltaX, deltaY, width, height, targetElement });
181
+ }
182
+ interactableExistOnRegistry(interactId, registryId = DEFAULT_REGISTRY_ID) {
183
+ // eslint-disable-next-line no-prototype-builtins
184
+ return this.dragRegistrySystem.hasOwnProperty(registryId) && this.dragRegistrySystem[registryId].hasOwnProperty(interactId);
185
+ }
186
+ /**
187
+ * Uses renderer to set width and height of angular component
188
+ * @param width the desired width of component
189
+ * @param height the desired height of component
190
+ * @param target the element to transform
191
+ */
192
+ setElementSize(width, height, target) {
193
+ if (!target)
194
+ return;
195
+ this.renderer.setStyle(target, 'width', `${width}${this.cssUnit}`);
196
+ this.renderer.setStyle(target, 'height', `${height}${this.cssUnit}`);
197
+ }
198
+ /**
199
+ * Uses renderer to set position of angular component
200
+ * @param x position on x axis
201
+ * @param y position on y axis
202
+ * @param target the element to transform
203
+ */
204
+ setElementTransform(x, y, position) {
205
+ if (!position?.targetElement)
206
+ return;
207
+ this.renderer.setStyle(position.targetElement, 'left', `${x}${this.cssUnit}`);
208
+ this.renderer.setStyle(position.targetElement, 'top', `${y}${this.cssUnit}`);
209
+ }
210
+ /**
211
+ * Generates a translate string for the x and y arguments passed to
212
+ * be passed to css transform
213
+ * @param x the position along the x axis
214
+ * @param y the position along the y axis
215
+ */
216
+ createTransformString(x, y) {
217
+ return `translate3d(${x}${this.cssUnit}, ${y}${this.cssUnit}, 0)`;
218
+ }
219
+ calculatePositionInElement(zoneElement, dragElement, scale) {
220
+ // TODO: this calculation may not cover a lot of scenarios, keep an eye out for possible improvements
221
+ const zoneRect = zoneElement.getBoundingClientRect();
222
+ const dragRect = dragElement.getBoundingClientRect();
223
+ const position = scale
224
+ ? {
225
+ x: (dragRect.left - zoneRect.left) / scale || 1,
226
+ y: (dragRect.top - zoneRect.top) / scale || 1
227
+ } : {
228
+ x: dragRect.left - zoneRect.left,
229
+ y: dragRect.top - zoneRect.top
230
+ };
231
+ // console.log({ y:position.y, drt:dragRect.top, zrt: zoneRect.top})
232
+ return position;
233
+ }
234
+ }
235
+ InteractService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractService, deps: [{ token: i0.RendererFactory2 }, { token: i0.NgZone }, { token: INTERACT_ROOT_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
236
+ InteractService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractService, providedIn: 'root' });
237
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractService, decorators: [{
238
+ type: Injectable,
239
+ args: [{ providedIn: 'root' }]
240
+ }], ctorParameters: function () { return [{ type: i0.RendererFactory2 }, { type: i0.NgZone }, { type: undefined, decorators: [{
241
+ type: Optional
242
+ }, {
243
+ type: Inject,
244
+ args: [INTERACT_ROOT_CONFIG]
245
+ }] }]; } });
246
+
247
+ /**
248
+ * Use to apply scale to the element and tell the drag and resize directives to account for the scale when
249
+ * @example <div tftAccountForScale [scale]="2.5"> <div tftDraggable> </div> </div>
250
+ * @note if `dragRoot` is used this directive should be used on the same element to ensure that the drag
251
+ * element accounts for scale
252
+ */
253
+ class AccountForScaleDirective {
254
+ constructor(el) {
255
+ this.el = el;
256
+ // this setup allows consumers to set x, y scale independently or just set them both with scale
257
+ this.scale = 1;
258
+ }
259
+ }
260
+ AccountForScaleDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: AccountForScaleDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
261
+ AccountForScaleDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: AccountForScaleDirective, selector: "[tftAccountForScale]", inputs: { scale: "scale" }, ngImport: i0 });
262
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: AccountForScaleDirective, decorators: [{
263
+ type: Directive,
264
+ args: [{
265
+ selector: '[tftAccountForScale]'
266
+ }]
267
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { scale: [{
268
+ type: Input
269
+ }] } });
270
+
271
+ class DropzoneDirective {
272
+ constructor(el, renderer, interactService, account_for_scale_dir) {
273
+ this.el = el;
274
+ this.renderer = renderer;
275
+ this.interactService = interactService;
276
+ this.account_for_scale_dir = account_for_scale_dir;
277
+ this.dropzoneConfig = {};
278
+ this.position = 'relative';
279
+ // proxies to pass interact events through to consumer of directive
280
+ this.dropActivate = new EventEmitter();
281
+ this.dragEnter = new EventEmitter();
282
+ this.dragLeave = new EventEmitter();
283
+ this.dragDrop = new EventEmitter();
284
+ }
285
+ get scale() {
286
+ return this.account_for_scale_dir?.scale;
287
+ }
288
+ ngOnInit() {
289
+ // connects
290
+ this.dropzone = this.connectDropzoneEvents(this.el.nativeElement, this.dropzoneConfig);
291
+ this.dropzoneId = this.interactService.addRegistryToSystem(this.dropzoneId);
292
+ }
293
+ ngOnChanges(changes) {
294
+ if (!changes.dropzoneConfig || !this.dropzone)
295
+ return;
296
+ // this.dropzone.unset();
297
+ // this.dropzone = this.connectDropzoneEvents(this.el.nativeElement, this.dropzoneConfig)
298
+ this.dropzone.dropzone(this.dropzoneConfig);
299
+ }
300
+ /**
301
+ * Connects element to interacts dropzone events and returns a reference to the interactable
302
+ * @param nativeElement the element to tie drop events to
303
+ * @param dropzoneConfig the interact configuration to use when connecting
304
+ */
305
+ connectDropzoneEvents(nativeElement, dropzoneConfig) {
306
+ return interact(nativeElement).dropzone(dropzoneConfig)
307
+ // TODO: investigate when this fires in different scenarios
308
+ .on('dropactivate', (event) => {
309
+ this.dropActivate.emit(this.mapDropzoneEvent(event));
310
+ })
311
+ .on('dragenter', (event) => {
312
+ this.renderer.setProperty(event.relatedTarget, 'dropTarget', this);
313
+ this.dragEnter.emit(this.mapDropzoneEvent(event));
314
+ })
315
+ .on('dragleave', (event) => {
316
+ if (event.relatedTarget.dropTarget === this) {
317
+ this.renderer.setProperty(event.relatedTarget, 'dropTarget', null);
318
+ }
319
+ this.dragLeave.emit(this.mapDropzoneEvent(event));
320
+ })
321
+ .on('drop', (event) => {
322
+ this.dragDrop.emit(this.mapDropzoneEvent(event));
323
+ });
324
+ }
325
+ /**
326
+ * Maps the drop event emitted by interact to something a little easier to use
327
+ * @param event the drop event emitted by interact, extended with extra fields we added to
328
+ */
329
+ mapDropzoneEvent(event) {
330
+ const zoneElement = event.target;
331
+ const dragElement = event.relatedTarget?.dragRef?.previewRef;
332
+ const scale = this.scale;
333
+ const positionInDropTarget = zoneElement && dragElement
334
+ ? this.interactService.calculatePositionInElement(zoneElement, dragElement, scale)
335
+ : null;
336
+ return {
337
+ interactEvent: event,
338
+ dragRef: dragElement.dragRef,
339
+ dragOrigin: dragElement.dragOrigin,
340
+ dropTarget: event.relatedTarget.dropTarget,
341
+ positionInDropTarget
342
+ };
343
+ }
344
+ }
345
+ DropzoneDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DropzoneDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: InteractService }, { token: AccountForScaleDirective, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
346
+ DropzoneDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: DropzoneDirective, selector: "[tftDropzone]", inputs: { dropzoneId: "dropzoneId", dropzoneConfig: "dropzoneConfig", dropzoneData: "dropzoneData", position: "position" }, outputs: { dropActivate: "dropActivate", dragEnter: "dragEnter", dragLeave: "dragLeave", dragDrop: "dragDrop" }, host: { properties: { "id": "dropzoneId", "style.position": "this.position" } }, usesOnChanges: true, ngImport: i0 });
347
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DropzoneDirective, decorators: [{
348
+ type: Directive,
349
+ args: [{
350
+ selector: '[tftDropzone]',
351
+ host: {
352
+ '[id]': 'dropzoneId'
353
+ }
354
+ }]
355
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: InteractService }, { type: AccountForScaleDirective, decorators: [{
356
+ type: Optional
357
+ }] }]; }, propDecorators: { dropzoneId: [{
358
+ type: Input
359
+ }], dropzoneConfig: [{
360
+ type: Input
361
+ }], dropzoneData: [{
362
+ type: Input
363
+ }], position: [{
364
+ type: Input
365
+ }, {
366
+ type: HostBinding,
367
+ args: ['style.position']
368
+ }], dropActivate: [{
369
+ type: Output
370
+ }], dragEnter: [{
371
+ type: Output
372
+ }], dragLeave: [{
373
+ type: Output
374
+ }], dragDrop: [{
375
+ type: Output
376
+ }] } });
377
+
378
+ const TFT_DRAG_PREVIEW = new InjectionToken('DragPreviewDirective');
379
+ class DragPreviewDirective {
380
+ constructor(templateRef) {
381
+ this.templateRef = templateRef;
382
+ }
383
+ }
384
+ DragPreviewDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DragPreviewDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
385
+ DragPreviewDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: DragPreviewDirective, selector: "ng-template[tftDragPreview]", inputs: { dragData: "dragData" }, ngImport: i0 });
386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DragPreviewDirective, decorators: [{
387
+ type: Directive,
388
+ args: [{
389
+ // eslint-disable-next-line @angular-eslint/directive-selector
390
+ selector: 'ng-template[tftDragPreview]',
391
+ }]
392
+ }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { dragData: [{
393
+ type: Input
394
+ }] } });
395
+
396
+ /** Gets the closest ancestor of an element that matches a selector. */
397
+ function getClosestMatchingAncestor(element, selector) {
398
+ let currentElement = element.parentElement;
399
+ while (currentElement) {
400
+ // IE doesn't support `matches` so we have to fall back to `msMatchesSelector`.
401
+ if (currentElement.matches ? currentElement.matches(selector) :
402
+ currentElement.msMatchesSelector(selector)) {
403
+ return currentElement;
404
+ }
405
+ currentElement = currentElement.parentElement;
406
+ }
407
+ return null;
408
+ }
409
+ /**
410
+ * Create a copy of the HTML Element
411
+ */
412
+ function cloneElement$1(element) {
413
+ const clone = element.cloneNode(true);
414
+ clone.removeAttribute('id');
415
+ return clone;
416
+ }
417
+ /**
418
+ * Gets the root HTML element of an embedded view.
419
+ * If the root is not an HTML element it gets wrapped in one.
420
+ */
421
+ function getRootNode(viewRef, _document) {
422
+ const rootNodes = viewRef.rootNodes;
423
+ if (rootNodes.length === 1 && rootNodes[0].nodeType === _document.ELEMENT_NODE) {
424
+ return rootNodes[0];
425
+ }
426
+ const wrapper = _document.createElement('div');
427
+ rootNodes.forEach(node => wrapper.appendChild(node));
428
+ return wrapper;
429
+ }
430
+
431
+ /**
432
+ * Use this directive to select which element to attach the element to while dragging
433
+ * @example <div tftDragRoot><div tftDraggable> </div> </div>
434
+ * @note this directive will overwrite any style transformations (e.g. transform: scale(2); )
435
+ */
436
+ class DragRootDirective {
437
+ constructor(el,
438
+ // This is used when passing a dragRoot as in input to a draggable element that not a child of that dragRoot
439
+ // It allows the the interact service to check if the drag root has some scaling applied to it and account for it when the component is dragged
440
+ account_for_scale_dir) {
441
+ this.el = el;
442
+ this.account_for_scale_dir = account_for_scale_dir;
443
+ }
444
+ }
445
+ DragRootDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DragRootDirective, deps: [{ token: i0.ElementRef }, { token: AccountForScaleDirective, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
446
+ DragRootDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: DragRootDirective, selector: "[tftDragRoot]", exportAs: ["tftDragRoot"], ngImport: i0 });
447
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DragRootDirective, decorators: [{
448
+ type: Directive,
449
+ args: [{
450
+ selector: '[tftDragRoot]',
451
+ exportAs: 'tftDragRoot'
452
+ }]
453
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: AccountForScaleDirective, decorators: [{
454
+ type: Optional
455
+ }, {
456
+ type: Self
457
+ }] }]; } });
458
+
459
+ ;
460
+ /**
461
+ * Use to apply scale to the element and tell the drag and resize directives to account for the scale when
462
+ * @example <div tftAccountForScale [scale]="2.5"> <div tftDraggable> </div> </div>
463
+ * @note if `dragRoot` is used this directive should be used on the same element to ensure that the drag
464
+ * element accounts for scale
465
+ */
466
+ class AutoScrollDirective {
467
+ constructor(el) {
468
+ this.el = el;
469
+ /**
470
+ * Limit minimum time between scroll events in milliseconds
471
+ */
472
+ this.autoScrollConfig = {
473
+ margin: 50,
474
+ distance: 3,
475
+ interval: 50,
476
+ speed: 300,
477
+ enabled: true
478
+ };
479
+ this.scrollDeltaObserver = new Subject();
480
+ const { scrollTop, scrollLeft } = el.nativeElement;
481
+ this.scrollSubject = new BehaviorSubject({ scrollTop, scrollLeft });
482
+ this.scrollObserver = this.scrollSubject.pipe(scan((previous, current) => {
483
+ const scrollTop = current.scrollTop - previous.scrollTop;
484
+ const scrollLeft = current.scrollLeft - previous.scrollLeft;
485
+ this.scrollDeltaObserver.next({ scrollTop, scrollLeft });
486
+ return current;
487
+ }), shareReplay(1));
488
+ this.scrollObserver.subscribe();
489
+ }
490
+ // this setup allows consumers to set x, y scale independently or just set them both with scale
491
+ scroll(scrollEvent) {
492
+ const target = scrollEvent.target;
493
+ const { scrollTop, scrollLeft } = target;
494
+ this.scrollSubject.next({ scrollTop, scrollLeft });
495
+ }
496
+ }
497
+ AutoScrollDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: AutoScrollDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
498
+ AutoScrollDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: AutoScrollDirective, selector: "[tftAutoScroll]", inputs: { autoScrollConfig: "autoScrollConfig" }, host: { listeners: { "scroll": "scroll($event)" } }, ngImport: i0 });
499
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: AutoScrollDirective, decorators: [{
500
+ type: Directive,
501
+ args: [{
502
+ selector: '[tftAutoScroll]'
503
+ }]
504
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { autoScrollConfig: [{
505
+ type: Input
506
+ }], scroll: [{
507
+ type: HostListener,
508
+ args: ['scroll', ['$event']]
509
+ }] } });
510
+
511
+ /** @dynamic */
512
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
513
+ class DraggableDirective {
514
+ constructor(el, interactService, renderer, _viewContainerRef, _document, account_for_scale_dir, dropzone_dir, drag_root_dir, auto_scroll_dir) {
515
+ this.el = el;
516
+ this.interactService = interactService;
517
+ this.renderer = renderer;
518
+ this._viewContainerRef = _viewContainerRef;
519
+ this._document = _document;
520
+ this.account_for_scale_dir = account_for_scale_dir;
521
+ this.dropzone_dir = dropzone_dir;
522
+ this.drag_root_dir = drag_root_dir;
523
+ this.auto_scroll_dir = auto_scroll_dir;
524
+ /* zIndex while dragging*/
525
+ this.dragZIndex = 10000;
526
+ this.enableDragDefault = true;
527
+ /**
528
+ * @deprecated replaced by 'dragDisabled' property, please migrate before v13
529
+ */
530
+ this.disabled = false;
531
+ this.dragDisabled = false;
532
+ this.showPlaceholder = false;
533
+ // pipes all interact events to event emitters
534
+ this.dragStart = new EventEmitter();
535
+ this.dragMove = new EventEmitter();
536
+ this.dragInertiaStart = new EventEmitter();
537
+ this.dragEnd = new EventEmitter();
538
+ }
539
+ // check all the different places for scale
540
+ get scale() {
541
+ return this.account_for_scale_dir?.scale || this.drag_root_dir?.account_for_scale_dir?.scale || this.dragRoot?.account_for_scale_dir?.scale || 1;
542
+ }
543
+ ngOnInit() {
544
+ this.interactable = this.initiateDragEvents(this.dragConfig, this.el.nativeElement);
545
+ this.dragRootEl = (this.dragRoot?.el?.nativeElement || this.drag_root_dir?.el?.nativeElement) || this._document.body;
546
+ // register with parent dropzone if it exists, otherwise use default
547
+ // this gives us a place on the registry to store our drag items that don't have a dropzone
548
+ this.registryId = this.dropzone_dir?.dropzoneId || DEFAULT_REGISTRY_ID;
549
+ // add draggable to directory and store its id
550
+ this.interactableId = this.interactService.addDraggableToRegistry(this.registryId, this.interactableId);
551
+ this.alignPositionWithInputs(this.x, this.y);
552
+ // cache subscription to interactable state so we can unsubscribe onDestroy
553
+ this.interactableState = this.interactService
554
+ .getInteractableState(this.interactableId, this.registryId);
555
+ // keep our cached position values in line with our drag state
556
+ this.interactableSubscription = this.interactableState.subscribe((state) => {
557
+ this.y = state.y;
558
+ this.x = state.x;
559
+ });
560
+ this.setDragConfig();
561
+ }
562
+ // TODO: watch config here as well and update interactable
563
+ ngOnChanges(changes) {
564
+ // we do this here instead of using a setter on the input so that it
565
+ // will only run once when there is a change to x and y
566
+ if (changes.y || changes.x) {
567
+ this.alignPositionWithInputs(this.x, this.y);
568
+ }
569
+ if (changes.dragConfig || changes.disabled || changes.dragDisabled) {
570
+ if (!this.interactable)
571
+ return;
572
+ this.setDragConfig();
573
+ }
574
+ /**
575
+ * @deprecated remove this in v13
576
+ */
577
+ if (changes.disabled) {
578
+ console.warn(`It looks like your using the 'disabled' input on a tftDraggable directive, it has been deprecated and is replaced with 'dragDisabled', please migrate by v13`);
579
+ }
580
+ }
581
+ setDragConfig() {
582
+ const enabled = !(this.disabled || this.dragDisabled);
583
+ const dragConfig = { ...this.dragConfig, enabled };
584
+ this.interactable.draggable(dragConfig);
585
+ }
586
+ ngAfterViewInit() {
587
+ this.addDragPropertiesToElement(this.el.nativeElement);
588
+ if (!this.dropzone_dir) {
589
+ this.previewRef = this.el.nativeElement;
590
+ }
591
+ }
592
+ ngOnDestroy() {
593
+ // clean up state and subscriptions related to destroyed component
594
+ this.interactableSubscription.unsubscribe();
595
+ this.interactService.destroyInteractable(this.interactableId, this.registryId);
596
+ }
597
+ dragMoveListener(event, element) {
598
+ const { dx, dy } = event;
599
+ const scale = this.scale;
600
+ const deltaX = scale ? dx / scale : dx;
601
+ const deltaY = scale ? dy / scale : dy;
602
+ this.interactService.updateDeltas(this.interactableId, this.registryId, { deltaX, deltaY }, element);
603
+ }
604
+ setDragStyles(dragElement) {
605
+ this.renderer.setStyle(dragElement, 'touchAction', 'none');
606
+ this.renderer.setStyle(dragElement, 'zIndex', this.dragZIndex);
607
+ this.renderer.setStyle(dragElement, 'position', 'absolute');
608
+ }
609
+ addDragPropertiesToElement(nativeElement) {
610
+ this.renderer.setProperty(nativeElement, 'dropTarget', this.dropzone_dir || null);
611
+ this.renderer.setProperty(nativeElement, 'dragOrigin', this.dropzone_dir || null);
612
+ this.renderer.setProperty(nativeElement, 'dragRef', this);
613
+ }
614
+ mapDragEvent(event) {
615
+ const { target, dropzone } = event;
616
+ const dropzoneElement = dropzone?.target || target.dropTarget?.el.nativeElement;
617
+ const scale = this.scale;
618
+ const positionInDropTarget = target && dropzoneElement
619
+ ? this.interactService.calculatePositionInElement(dropzoneElement, this.previewRef, scale)
620
+ : null;
621
+ return {
622
+ interactEvent: event,
623
+ dragRef: this,
624
+ dragOrigin: target.dragOrigin,
625
+ dropTarget: target.dropTarget,
626
+ positionInDropTarget
627
+ };
628
+ }
629
+ setPosition(x, y) {
630
+ this.interactService.updatePosition(this.interactableId, this.registryId, { x, y }, this.el.nativeElement);
631
+ }
632
+ alignPositionWithInputs(x, y) {
633
+ // short circuit if positions aren't numbers or if they haven't changed
634
+ if (!this.isValidPosition(x, y))
635
+ return;
636
+ this.interactService.updatePosition(this.interactableId, this.registryId, { x, y }, this.el.nativeElement);
637
+ }
638
+ isValidPosition(x, y) {
639
+ return this.isNumeric(x) && this.isNumeric(y);
640
+ }
641
+ // We use use this instead of isNaN here to try and help performance
642
+ // We should be able to reliably restrict to numbers or falsey values
643
+ isNumeric(num) {
644
+ return typeof num === 'number' && !isNaN(num);
645
+ }
646
+ initiateDragEvents(dragConfig, nativeElement) {
647
+ const defaultDragConfig = {
648
+ ...dragConfig,
649
+ enabled: !(this.disabled || this.dragDisabled),
650
+ ...(this.auto_scroll_dir && {
651
+ autoScroll: {
652
+ ...this.auto_scroll_dir.autoScrollConfig,
653
+ container: this.auto_scroll_dir.el.nativeElement
654
+ }
655
+ })
656
+ };
657
+ return interact(nativeElement).draggable(defaultDragConfig)
658
+ .on('dragstart', (event) => {
659
+ const interaction = event.interaction;
660
+ // check if we should append drag element to body
661
+ if (this.enableDragDefault
662
+ && interaction.pointerIsDown
663
+ // TODO: probably don't need following condition checks anymore remove by 2022
664
+ // && !!this.dropzone_dir
665
+ // && !interaction.interacting()
666
+ ) {
667
+ if (this._previewTemplate) {
668
+ const viewRef = this._viewContainerRef.createEmbeddedView(this._previewTemplate.templateRef);
669
+ this.previewRef = getRootNode(viewRef, this._document);
670
+ }
671
+ else {
672
+ this.previewRef = cloneElement(this.el.nativeElement);
673
+ }
674
+ this.addDragPropertiesToElement(this.previewRef);
675
+ this.setDragStyles(this.previewRef);
676
+ this.dragRootEl.prepend(this.previewRef);
677
+ const scale = this.account_for_scale_dir?.scale || this.dragRoot?.account_for_scale_dir?.scale || 1;
678
+ const { x, y } = this.interactService.calculatePositionInElement(this.dragRootEl, nativeElement, scale);
679
+ this.interactService.updatePosition(this.interactableId, this.registryId, { x, y }, this.previewRef);
680
+ if (!this.showPlaceholder) {
681
+ this.renderer.setStyle(this.el.nativeElement, 'display', 'none');
682
+ }
683
+ }
684
+ // subscribe to scroll events if they are there, by only subscribing on dragstart we avoid wasting subscriptions on interactables that aren't be dragged
685
+ if (this.auto_scroll_dir) {
686
+ this.subscribeToScrollEvents();
687
+ }
688
+ this.dragStart.emit(this.mapDragEvent(event));
689
+ })
690
+ .on('dragmove', (event) => {
691
+ if (this.enableDragDefault) {
692
+ const dragElement = this.dropzone_dir && this.previewRef || nativeElement;
693
+ this.dragMoveListener(event, dragElement);
694
+ }
695
+ const mappedEvent = this.mapDragEvent(event);
696
+ this.dragMove.emit(mappedEvent);
697
+ })
698
+ .on('draginertiastart', (event) => {
699
+ this.dragInertiaStart.emit(this.mapDragEvent(event));
700
+ })
701
+ .on('dragend', (event) => {
702
+ const mappedEvent = this.mapDragEvent(event);
703
+ if (!!this.dropzone_dir && this.dropzone_dir === event.target.dropTarget) {
704
+ const { x, y } = mappedEvent.positionInDropTarget;
705
+ this.setPosition(x, y);
706
+ }
707
+ this.renderer.setStyle(this.el.nativeElement, 'display', '');
708
+ this.dragEnd.emit(mappedEvent);
709
+ // unsubscribe from scroll events if subscribed
710
+ if (this.auto_scroll_dir) {
711
+ this.scrollEventsSubscription.unsubscribe();
712
+ }
713
+ if (this.el.nativeElement !== this.previewRef) {
714
+ // we do this in a timeout, so that we don't remove before the dropzone can get the boundingElementRect
715
+ setTimeout(() => {
716
+ this.dragRootEl.removeChild(this.previewRef);
717
+ });
718
+ }
719
+ });
720
+ }
721
+ subscribeToScrollEvents() {
722
+ this.scrollEventsSubscription = this.auto_scroll_dir.scrollDeltaObserver.subscribe(({ scrollLeft, scrollTop }) => {
723
+ const deltaX = this.scale ? scrollLeft / this.scale : scrollLeft;
724
+ const deltaY = this.scale ? scrollTop / this.scale : scrollTop;
725
+ this.interactService.updateDeltas(this.interactableId, this.registryId, { deltaX, deltaY }, this.previewRef);
726
+ });
727
+ }
728
+ }
729
+ DraggableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DraggableDirective, deps: [{ token: i0.ElementRef }, { token: InteractService }, { token: i0.Renderer2 }, { token: i0.ViewContainerRef }, { token: DOCUMENT }, { token: AccountForScaleDirective, optional: true }, { token: DropzoneDirective, optional: true, skipSelf: true }, { token: DragRootDirective, optional: true, skipSelf: true }, { token: AutoScrollDirective, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive });
730
+ DraggableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: DraggableDirective, selector: "[tftDraggable]", inputs: { x: "x", y: "y", dragZIndex: "dragZIndex", zIndex: "zIndex", interactableId: "interactableId", dragData: "dragData", enableDragDefault: "enableDragDefault", disabled: "disabled", dragDisabled: "dragDisabled", dragConfig: "dragConfig", showPlaceholder: "showPlaceholder", dragRoot: "dragRoot" }, outputs: { dragStart: "dragStart", dragMove: "dragMove", dragInertiaStart: "dragInertiaStart", dragEnd: "dragEnd" }, host: { properties: { "style.touchAction": "this.dragDisabled ? \"auto\" : \"none\"", "style.position": "\"absolute\"", "id": "interactableId" } }, queries: [{ propertyName: "_previewTemplate", first: true, predicate: DragPreviewDirective, descendants: true }], usesOnChanges: true, ngImport: i0 });
731
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DraggableDirective, decorators: [{
732
+ type: Directive,
733
+ args: [{
734
+ selector: '[tftDraggable]',
735
+ // eslint-disable-next-line @angular-eslint/no-host-metadata-property
736
+ host: {
737
+ '[style.touchAction]': 'this.dragDisabled ? "auto" : "none"',
738
+ '[style.position]': '"absolute"',
739
+ '[id]': 'interactableId',
740
+ }
741
+ }]
742
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: InteractService }, { type: i0.Renderer2 }, { type: i0.ViewContainerRef }, { type: Document, decorators: [{
743
+ type: Inject,
744
+ args: [DOCUMENT]
745
+ }] }, { type: AccountForScaleDirective, decorators: [{
746
+ type: Optional
747
+ }] }, { type: DropzoneDirective, decorators: [{
748
+ type: Optional
749
+ }, {
750
+ type: SkipSelf
751
+ }] }, { type: DragRootDirective, decorators: [{
752
+ type: Optional
753
+ }, {
754
+ type: SkipSelf
755
+ }] }, { type: AutoScrollDirective, decorators: [{
756
+ type: Optional
757
+ }, {
758
+ type: SkipSelf
759
+ }] }]; }, propDecorators: { x: [{
760
+ type: Input
761
+ }], y: [{
762
+ type: Input
763
+ }], dragZIndex: [{
764
+ type: Input
765
+ }], zIndex: [{
766
+ type: Input
767
+ }], interactableId: [{
768
+ type: Input
769
+ }], dragData: [{
770
+ type: Input
771
+ }], enableDragDefault: [{
772
+ type: Input
773
+ }], disabled: [{
774
+ type: Input
775
+ }], dragDisabled: [{
776
+ type: Input
777
+ }], dragConfig: [{
778
+ type: Input
779
+ }], showPlaceholder: [{
780
+ type: Input
781
+ }], dragRoot: [{
782
+ type: Input
783
+ }], dragStart: [{
784
+ type: Output
785
+ }], dragMove: [{
786
+ type: Output
787
+ }], dragInertiaStart: [{
788
+ type: Output
789
+ }], dragEnd: [{
790
+ type: Output
791
+ }], _previewTemplate: [{
792
+ type: ContentChild,
793
+ args: [DragPreviewDirective]
794
+ }] } });
795
+ /**
796
+ * Component utils
797
+ */
798
+ // create a clone of the component passed in and add it to the root component
799
+ function cloneElement(element) {
800
+ const clone = element.cloneNode(true);
801
+ clone.removeAttribute('id');
802
+ return clone;
803
+ }
804
+ function getPreviewInsertionPoint(documentRef) {
805
+ // We can't use the body if the user is in fullscreen mode,
806
+ // because the preview will render under the fullscreen element.
807
+ return documentRef.fullscreenElement ||
808
+ documentRef.webkitFullscreenElement ||
809
+ documentRef.mozFullScreenElement ||
810
+ documentRef.msFullscreenElement ||
811
+ documentRef.body;
812
+ }
813
+
814
+ /** @dynamic */
815
+ class ResizableDirective {
816
+ constructor(el, _document, interactService, renderer, drag_root_dir, account_for_scale_dir, draggable_dir, dropzone_dir, auto_scroll_dir) {
817
+ this.el = el;
818
+ this._document = _document;
819
+ this.interactService = interactService;
820
+ this.renderer = renderer;
821
+ this.drag_root_dir = drag_root_dir;
822
+ this.account_for_scale_dir = account_for_scale_dir;
823
+ this.draggable_dir = draggable_dir;
824
+ this.dropzone_dir = dropzone_dir;
825
+ this.auto_scroll_dir = auto_scroll_dir;
826
+ this.enableResizeDefault = true;
827
+ // TODO: should this be an input with default or should consuming user set display in styles each time
828
+ this.position = 'absolute';
829
+ this.resizeDisabled = false;
830
+ this.resizeStart = new EventEmitter();
831
+ this.resizeMove = new EventEmitter();
832
+ this.resizeInertiaStart = new EventEmitter();
833
+ this.resizeEnd = new EventEmitter();
834
+ this.defaultConfig = {
835
+ edges: { left: true, right: true, bottom: true, top: true }
836
+ };
837
+ this.activeEdges = { left: false, right: false, top: false, bottom: false };
838
+ this.dragRootEl = drag_root_dir?.el?.nativeElement || this._document.body;
839
+ }
840
+ get scale() {
841
+ return this.account_for_scale_dir?.scale || this.drag_root_dir?.account_for_scale_dir?.scale;
842
+ }
843
+ ngOnInit() {
844
+ this.addResizePropertiesToElement(this.el.nativeElement);
845
+ // we check for a dropzone, we use its id as the registry id if there is on
846
+ // otherwise, we use DEFAULT_REGISTRY_ID as a registry id. This allows us to keep our interactable
847
+ // drag state organized by dropzones.
848
+ this.registryId = this.dropzone_dir?.dropzoneId || DEFAULT_REGISTRY_ID;
849
+ this.interactableId = this.draggable_dir?.interactableId || this.interactService.addDraggableToRegistry(this.registryId, this.interactableId);
850
+ this.interactable = this.initiateResizeEvents({ ...this.defaultConfig, ...this.resizeConfig }, this.el.nativeElement);
851
+ this.alignDimensionsWithInputs(this.width, this.height);
852
+ this.interactableSubscription = this.interactService
853
+ .getInteractableState(this.interactableId, this.registryId)
854
+ .subscribe();
855
+ }
856
+ ngOnChanges(changes) {
857
+ if ((changes.resizeConfig || changes.resizeDisabled) && this.interactable) {
858
+ this.interactable.resizable({ ...this.resizeConfig, enabled: !this.resizeDisabled, });
859
+ }
860
+ if (changes.width || changes.height) {
861
+ this.alignDimensionsWithInputs(this.width, this.height);
862
+ }
863
+ }
864
+ ngOnDestroy() {
865
+ this.interactableSubscription.unsubscribe();
866
+ this.interactService.destroyInteractable(this.interactableId, this.registryId);
867
+ }
868
+ resizeListener(event) {
869
+ const element = this.el.nativeElement;
870
+ const scale = this.scale;
871
+ const { deltaRect } = event;
872
+ const deltaX = deltaRect.left / scale;
873
+ const deltaY = deltaRect.top / scale;
874
+ // We get the width and height directly from the native element because
875
+ // the event dimension can get out of sync when resizing with autoScroll
876
+ const rect = element.getBoundingClientRect();
877
+ const width = (rect.width + deltaRect.width) / scale;
878
+ const height = (rect.height + deltaRect.height) / scale;
879
+ this.interactService.updateSize(this.interactableId, this.registryId, { deltaX, deltaY, width, height }, this.el.nativeElement);
880
+ }
881
+ /**
882
+ * hook elements into interacts resize event listeners
883
+ * we spread the configs so that the consuming component only overwrites the
884
+ * pieces that it explicitly indicates
885
+ * @param resizeConfig
886
+ * @param nativeElement
887
+ */
888
+ initiateResizeEvents(resizeConfig, nativeElement) {
889
+ // TODO: This should try and get interactable from tft_draggable if it exists
890
+ const defaultResizeConfig = {
891
+ ...resizeConfig,
892
+ enabled: !this.resizeDisabled,
893
+ ...(this.auto_scroll_dir && {
894
+ autoScroll: {
895
+ ...this.auto_scroll_dir.autoScrollConfig,
896
+ container: this.auto_scroll_dir.el.nativeElement
897
+ }
898
+ })
899
+ };
900
+ const interactable = this.draggable_dir?.interactable || interact(nativeElement);
901
+ return interactable.resizable(defaultResizeConfig)
902
+ .on('resizestart', (event) => {
903
+ this.activeEdges = event.edges;
904
+ if (this.auto_scroll_dir) {
905
+ this.subscribeToScrollEvents();
906
+ }
907
+ this.resizeStart.emit(this.mapResizeEvent(event));
908
+ })
909
+ .on('resizemove', (event) => {
910
+ if (this.enableResizeDefault) {
911
+ this.resizeListener(event);
912
+ }
913
+ const mappedEvent = this.mapResizeEvent(event);
914
+ this.resizeMove.emit(mappedEvent);
915
+ })
916
+ .on('resizeinertiastart', (event) => this.resizeInertiaStart.emit(this.mapResizeEvent(event)))
917
+ .on('resizeend', (event) => {
918
+ if (this.auto_scroll_dir) {
919
+ this.scrollEventsSubscription.unsubscribe();
920
+ }
921
+ this.activeEdges = { left: false, right: false, top: false, bottom: false };
922
+ this.resizeEnd.emit(this.mapResizeEvent(event));
923
+ });
924
+ }
925
+ mapResizeEvent(event) {
926
+ const target = this.el.nativeElement;
927
+ const relatedTarget = this.draggable_dir?.dropzone_dir?.el.nativeElement;
928
+ const scale = this.scale;
929
+ const positionInDropTarget = target && relatedTarget
930
+ ? this.interactService.calculatePositionInElement(relatedTarget, target, scale)
931
+ : null;
932
+ return {
933
+ interactEvent: event,
934
+ resizeRef: this,
935
+ dragRef: this.draggable_dir,
936
+ dragOrigin: this.dropzone_dir,
937
+ dropTarget: this.el.nativeElement.dropTarget,
938
+ positionInDropTarget,
939
+ size: {
940
+ width: target.clientWidth / scale,
941
+ height: target.clientHeight / scale
942
+ }
943
+ };
944
+ }
945
+ alignDimensionsWithInputs(width, height) {
946
+ this.interactService.updateSize(this.interactableId, this.registryId, { deltaX: 0, deltaY: 0, width, height }, this.el.nativeElement);
947
+ }
948
+ addResizePropertiesToElement(nativeElement) {
949
+ this.renderer.setProperty(nativeElement, 'resizeRef', this);
950
+ }
951
+ subscribeToScrollEvents() {
952
+ this.scrollEventsSubscription = this.auto_scroll_dir.scrollDeltaObserver.subscribe(({ scrollLeft, scrollTop }) => {
953
+ const scale = this.scale;
954
+ const target = this.el.nativeElement;
955
+ const { width: prevWidth, height: prevHeight } = target.getBoundingClientRect();
956
+ const deltaHeight = scrollTop * (this.activeEdges.top ? -1 : this.activeEdges.bottom ? 1 : 0);
957
+ const deltaWidth = scrollLeft * (this.activeEdges.left ? -1 : this.activeEdges.right ? 1 : 0);
958
+ const height = (prevHeight + deltaHeight) / scale;
959
+ const width = (prevWidth + deltaWidth) / scale;
960
+ const deltaX = this.activeEdges.left ? (scrollLeft / scale) : 0;
961
+ const deltaY = this.activeEdges.top ? (scrollTop / scale) : 0;
962
+ this.interactService.updateSize(this.interactableId, this.registryId, { deltaX, deltaY, width, height }, this.el.nativeElement);
963
+ });
964
+ }
965
+ }
966
+ ResizableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ResizableDirective, deps: [{ token: i0.ElementRef }, { token: DOCUMENT }, { token: InteractService }, { token: i0.Renderer2 }, { token: DragRootDirective, optional: true }, { token: AccountForScaleDirective, optional: true }, { token: DraggableDirective, optional: true }, { token: DropzoneDirective, optional: true, skipSelf: true }, { token: AutoScrollDirective, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive });
967
+ ResizableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: ResizableDirective, selector: "[tftResizable]", inputs: { enableResizeDefault: "enableResizeDefault", position: "position", resizeConfig: "resizeConfig", interactableId: "interactableId", width: "width", height: "height", resizeDisabled: "resizeDisabled" }, outputs: { resizeStart: "resizeStart", resizeMove: "resizeMove", resizeInertiaStart: "resizeInertiaStart", resizeEnd: "resizeEnd" }, host: { properties: { "style.touchAction": "this.resizeDisabled ? \"auto\" : \"none\"", "style.position": "position", "style.boxSizing": "\"border-box\"" } }, usesOnChanges: true, ngImport: i0 });
968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ResizableDirective, decorators: [{
969
+ type: Directive,
970
+ args: [{
971
+ selector: '[tftResizable]',
972
+ // eslint-disable-next-line @angular-eslint/no-host-metadata-property
973
+ host: {
974
+ // prevents touch events from colliding with with mouse events
975
+ // on touch screen
976
+ '[style.touchAction]': 'this.resizeDisabled ? "auto" : "none"',
977
+ // default to absolute here to accommodate likely use cases
978
+ // consuming component can easily over ride with position input
979
+ '[style.position]': 'position',
980
+ // follows interactjs suggested practices
981
+ '[style.boxSizing]': '"border-box"',
982
+ }
983
+ }]
984
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: Document, decorators: [{
985
+ type: Inject,
986
+ args: [DOCUMENT]
987
+ }] }, { type: InteractService }, { type: i0.Renderer2 }, { type: DragRootDirective, decorators: [{
988
+ type: Optional
989
+ }] }, { type: AccountForScaleDirective, decorators: [{
990
+ type: Optional
991
+ }] }, { type: DraggableDirective, decorators: [{
992
+ type: Optional
993
+ }] }, { type: DropzoneDirective, decorators: [{
994
+ type: Optional
995
+ }, {
996
+ type: SkipSelf
997
+ }] }, { type: AutoScrollDirective, decorators: [{
998
+ type: Optional
999
+ }, {
1000
+ type: SkipSelf
1001
+ }] }]; }, propDecorators: { enableResizeDefault: [{
1002
+ type: Input
1003
+ }], position: [{
1004
+ type: Input
1005
+ }], resizeConfig: [{
1006
+ type: Input
1007
+ }], interactableId: [{
1008
+ type: Input
1009
+ }], width: [{
1010
+ type: Input
1011
+ }], height: [{
1012
+ type: Input
1013
+ }], resizeDisabled: [{
1014
+ type: Input
1015
+ }], resizeStart: [{
1016
+ type: Output
1017
+ }], resizeMove: [{
1018
+ type: Output
1019
+ }], resizeInertiaStart: [{
1020
+ type: Output
1021
+ }], resizeEnd: [{
1022
+ type: Output
1023
+ }] } });
1024
+
1025
+ /** @dynamic */
1026
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1027
+ class GesturableDirective {
1028
+ constructor(el, draggable_dir) {
1029
+ this.el = el;
1030
+ this.draggable_dir = draggable_dir;
1031
+ this.gestureDisabled = false;
1032
+ // pipes all interact events to event emitters
1033
+ this.gestureStart = new EventEmitter();
1034
+ this.gestureMove = new EventEmitter();
1035
+ this.gestureEnd = new EventEmitter();
1036
+ }
1037
+ ngOnInit() {
1038
+ this.gesturableId = this.draggable_dir?.interactableId || 'id';
1039
+ this.interactable = this.initiateGestureEvents(this.gestureConfig, this.el.nativeElement);
1040
+ this.setGestureConfig();
1041
+ }
1042
+ // TODO: watch config here as well and update interactable
1043
+ ngOnChanges(changes) {
1044
+ // we do this here instead of using a setter on the input so that it
1045
+ // will only run once when there is a change to x and y
1046
+ if (changes.gestureConfig || changes.gestureDisabled) {
1047
+ if (!this.interactable)
1048
+ return;
1049
+ this.setGestureConfig();
1050
+ }
1051
+ }
1052
+ setGestureConfig() {
1053
+ const enabled = !this.gestureDisabled;
1054
+ const gestureConfig = { ...this.gestureConfig, enabled };
1055
+ this.interactable.gesturable(gestureConfig);
1056
+ }
1057
+ initiateGestureEvents(gestureConfig, nativeElement) {
1058
+ const defaultConfig = {
1059
+ enabled: !this.gestureDisabled,
1060
+ };
1061
+ const interactable = this.draggable_dir?.interactable || interact(nativeElement);
1062
+ return interactable.gesturable({ ...defaultConfig, ...gestureConfig })
1063
+ .on('gesturestart', (event) => {
1064
+ this.gestureStart.emit({ gestureRef: this, interactEvent: event });
1065
+ })
1066
+ .on('gesturemove', (event) => {
1067
+ this.gestureMove.emit({ gestureRef: this, interactEvent: event });
1068
+ })
1069
+ .on('gestureend', (event) => {
1070
+ this.gestureEnd.emit({ gestureRef: this, interactEvent: event });
1071
+ });
1072
+ }
1073
+ }
1074
+ GesturableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: GesturableDirective, deps: [{ token: i0.ElementRef }, { token: DraggableDirective, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1075
+ GesturableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: GesturableDirective, selector: "[tftGesturable]", inputs: { gesturableId: "gesturableId", gestureDisabled: "gestureDisabled", gestureConfig: "gestureConfig" }, outputs: { gestureStart: "gestureStart", gestureMove: "gestureMove", gestureEnd: "gestureEnd" }, host: { properties: { "id": "gesturableId" } }, usesOnChanges: true, ngImport: i0 });
1076
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: GesturableDirective, decorators: [{
1077
+ type: Directive,
1078
+ args: [{
1079
+ selector: '[tftGesturable]',
1080
+ // eslint-disable-next-line @angular-eslint/no-host-metadata-property
1081
+ host: {
1082
+ // '[style.touchAction]': 'this.gestureDisabled ? "auto" : "none"',
1083
+ '[id]': 'gesturableId',
1084
+ }
1085
+ }]
1086
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: DraggableDirective, decorators: [{
1087
+ type: Optional
1088
+ }] }]; }, propDecorators: { gesturableId: [{
1089
+ type: Input
1090
+ }], gestureDisabled: [{
1091
+ type: Input
1092
+ }], gestureConfig: [{
1093
+ type: Input
1094
+ }], gestureStart: [{
1095
+ type: Output
1096
+ }], gestureMove: [{
1097
+ type: Output
1098
+ }], gestureEnd: [{
1099
+ type: Output
1100
+ }] } });
1101
+
1102
+ class ApplyScaleDirective {
1103
+ constructor(el) {
1104
+ this.el = el;
1105
+ this._scale = 1;
1106
+ }
1107
+ // this setup allows consumers to set x, y scale independently or just set them both with scale
1108
+ set scale(val) {
1109
+ this._scale;
1110
+ this.fontSize = `${val}px`;
1111
+ }
1112
+ ;
1113
+ }
1114
+ ApplyScaleDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ApplyScaleDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1115
+ ApplyScaleDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: ApplyScaleDirective, selector: "[tftApplyScale]", inputs: { scale: "scale" }, host: { properties: { "style.fontSize": "this.fontSize" } }, ngImport: i0 });
1116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ApplyScaleDirective, decorators: [{
1117
+ type: Directive,
1118
+ args: [{
1119
+ selector: '[tftApplyScale]'
1120
+ }]
1121
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { scale: [{
1122
+ type: Input
1123
+ }], fontSize: [{
1124
+ type: HostBinding,
1125
+ args: ['style.fontSize']
1126
+ }] } });
1127
+
1128
+ const EXPORTS = [
1129
+ DraggableDirective,
1130
+ ResizableDirective,
1131
+ DropzoneDirective,
1132
+ GesturableDirective,
1133
+ DragRootDirective,
1134
+ AutoScrollDirective,
1135
+ AccountForScaleDirective,
1136
+ ApplyScaleDirective,
1137
+ DragPreviewDirective,
1138
+ ArrayOfNPipe,
1139
+ ];
1140
+ class InteractModule {
1141
+ static forRoot(config) {
1142
+ const defaultConfig = { cssDimensionUnit: 'px' };
1143
+ return {
1144
+ ngModule: InteractModule,
1145
+ providers: [
1146
+ { provide: INTERACT_ROOT_CONFIG, useValue: { ...defaultConfig, ...config } },
1147
+ InteractService
1148
+ ]
1149
+ };
1150
+ }
1151
+ }
1152
+ InteractModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1153
+ InteractModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: InteractModule, declarations: [DraggableDirective,
1154
+ ResizableDirective,
1155
+ DropzoneDirective,
1156
+ GesturableDirective,
1157
+ DragRootDirective,
1158
+ AutoScrollDirective,
1159
+ AccountForScaleDirective,
1160
+ ApplyScaleDirective,
1161
+ DragPreviewDirective,
1162
+ ArrayOfNPipe], imports: [CommonModule], exports: [DraggableDirective,
1163
+ ResizableDirective,
1164
+ DropzoneDirective,
1165
+ GesturableDirective,
1166
+ DragRootDirective,
1167
+ AutoScrollDirective,
1168
+ AccountForScaleDirective,
1169
+ ApplyScaleDirective,
1170
+ DragPreviewDirective,
1171
+ ArrayOfNPipe] });
1172
+ InteractModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractModule, imports: [CommonModule] });
1173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: InteractModule, decorators: [{
1174
+ type: NgModule,
1175
+ args: [{
1176
+ imports: [CommonModule],
1177
+ // providers: [{ provide: INTERACT_ROOT_CONFIG, useValue: 'px' }],
1178
+ declarations: [
1179
+ ...EXPORTS,
1180
+ ],
1181
+ exports: [
1182
+ ...EXPORTS
1183
+ ]
1184
+ }]
1185
+ }] });
1186
+
1187
+ /**
1188
+ * Generated bundle index. Do not edit.
1189
+ */
1190
+
1191
+ export { AccountForScaleDirective, ApplyScaleDirective, ArrayOfNPipe, AutoScrollDirective, DEFAULT_REGISTRY_ID, DragPreviewDirective, DragRootDirective, DraggableDirective, DropzoneDirective, GesturableDirective, INTERACT_ROOT_CONFIG, InteractModule, ResizableDirective, TFT_DRAG_PREVIEW, cloneElement$1 as cloneElement, defaultDelta, defaultPosition, defaultSize, getClosestMatchingAncestor, getRootNode };
1192
+ //# sourceMappingURL=tft-interact.mjs.map