@acorex/cdk 22.0.0 → 22.0.1-next.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.
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ElementRef, signal, Directive, NgZone, Renderer2, ChangeDetectorRef, PLATFORM_ID, input, output, contentChildren, DOCUMENT, ViewContainerRef, contentChild, linkedSignal, computed, effect, RendererStyleFlags2, NgModule } from '@angular/core';
2
+ import { inject, ElementRef, signal, Directive, NgZone, Renderer2, ChangeDetectorRef, PLATFORM_ID, input, output, contentChildren, linkedSignal, DOCUMENT, ViewContainerRef, contentChild, computed, effect, RendererStyleFlags2, NgModule } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
4
  import { clamp } from 'lodash-es';
5
5
  import { NXComponent } from '@acorex/cdk/common';
6
+ import { AXHtmlUtil } from '@acorex/core/utils';
6
7
 
7
8
  class AXDragHandleDirective {
8
9
  constructor() {
@@ -52,6 +53,8 @@ class AXDropListDirective extends NXComponent {
52
53
  ...(ngDevMode ? [{ debugName: "dropListOrientation" }] : /* istanbul ignore next */ []));
53
54
  /** Emits when an item is dropped into the list. */
54
55
  this.dropListDropped = output();
56
+ /** Emits when the sort preview transforms change during an active drag. */
57
+ this.sortPreviewChanged = output();
55
58
  /** The `axDrag` directives that are direct children of this list. */
56
59
  this._draggableItems = contentChildren(AXDragDirective, { ...(ngDevMode ? { debugName: "_draggableItems" } : /* istanbul ignore next */ {}), descendants: false });
57
60
  // --- Internal State Management ---
@@ -72,8 +75,15 @@ class AXDropListDirective extends NXComponent {
72
75
  /** The detected `gap` of the list for the current orientation. */
73
76
  this._listGap = signal(0, /* @ts-ignore */
74
77
  ...(ngDevMode ? [{ debugName: "_listGap" }] : /* istanbul ignore next */ []));
75
- /** A signal-based alias for the orientation input for internal use. */
76
- this._orientation = this.dropListOrientation;
78
+ /** Whether the list is in RTL direction (cached at drag start). */
79
+ this._isRtl = signal(false, /* @ts-ignore */
80
+ ...(ngDevMode ? [{ debugName: "_isRtl" }] : /* istanbul ignore next */ []));
81
+ /** Internal orientation state; can be overridden programmatically by host components. */
82
+ this.orientation = linkedSignal(() => this.dropListOrientation(), /* @ts-ignore */
83
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
84
+ /** Internal sorting-disabled state; can be overridden programmatically by host components. */
85
+ this.isSortingDisabled = linkedSignal(() => this.sortingDisabled(), /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "isSortingDisabled" }] : /* istanbul ignore next */ []));
77
87
  /** DOM placeholder element for inter-list drags */
78
88
  this._placeholderElement = null;
79
89
  }
@@ -97,12 +107,16 @@ class AXDropListDirective extends NXComponent {
97
107
  isDragActiveForThisList(dragItem) {
98
108
  return this._activeDragItem() === dragItem;
99
109
  }
110
+ /** Whether the list is horizontal and in RTL direction. */
111
+ isHorizontalRtl() {
112
+ return this.orientation() === 'horizontal' && this._isRtl();
113
+ }
100
114
  /**
101
115
  * Prepares the list for sorting when a drag operation starts from within this list.
102
116
  * @param dragItem The item that is being dragged.
103
117
  */
104
118
  prepareSort(dragItem) {
105
- if (!this.axDropList() || this.sortingDisabled() || this._activeDragItem() || dragItem.dragDisabled())
119
+ if (!this.axDropList() || this.isSortingDisabled() || this._activeDragItem() || dragItem.isDragDisabled())
106
120
  return;
107
121
  this._activeDragItem.set(dragItem);
108
122
  this.element.classList.add('ax-drop-list-sorting-active');
@@ -119,7 +133,7 @@ class AXDropListDirective extends NXComponent {
119
133
  * @param dragItem The item entering this list.
120
134
  */
121
135
  enter(dragItem) {
122
- if (!this.axDropList() || this.sortingDisabled() || this.isDragActiveForThisList(dragItem))
136
+ if (!this.axDropList() || this.isSortingDisabled() || this.isDragActiveForThisList(dragItem))
123
137
  return;
124
138
  // Clean up any existing placeholder from previous list
125
139
  this._removePlaceholderElement();
@@ -135,10 +149,10 @@ class AXDropListDirective extends NXComponent {
135
149
  sort(event, dragItem) {
136
150
  if (!this.axDropList() ||
137
151
  !this.isDragActiveForThisList(dragItem) ||
138
- this.sortingDisabled() ||
152
+ this.isSortingDisabled() ||
139
153
  !this._listInitialRect())
140
154
  return;
141
- const pointerPosition = this._orientation() === 'vertical' ? event.clientY : event.clientX;
155
+ const pointerPosition = this.orientation() === 'vertical' ? event.clientY : event.clientX;
142
156
  const newPlaceholderIndex = this._calculatePlaceholderIndex(pointerPosition);
143
157
  if (this._placeholderIndex() !== newPlaceholderIndex) {
144
158
  this._placeholderIndex.set(newPlaceholderIndex);
@@ -152,7 +166,7 @@ class AXDropListDirective extends NXComponent {
152
166
  * @param sourceList The list where the drag originated.
153
167
  */
154
168
  finalizeSort(event, droppedItem, sourceList) {
155
- if (!this.axDropList() || !this.isDragActiveForThisList(droppedItem) || this.sortingDisabled()) {
169
+ if (!this.axDropList() || !this.isDragActiveForThisList(droppedItem) || this.isSortingDisabled()) {
156
170
  this.resetSortStateAndStyles(sourceList);
157
171
  return;
158
172
  }
@@ -197,8 +211,11 @@ class AXDropListDirective extends NXComponent {
197
211
  /** Caches the geometry of the list and its items at the start of a drag. */
198
212
  _cacheGeometry() {
199
213
  this._listInitialRect.set(this.element.getBoundingClientRect());
214
+ this._isRtl.set(AXHtmlUtil.isRtl(this.element));
200
215
  this._detectAndCacheListGap();
201
- const items = this._draggableItems().map((itemDraggable) => {
216
+ const items = [...this._draggableItems()]
217
+ .sort((a, b) => a.element().compareDocumentPosition(b.element()) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1)
218
+ .map((itemDraggable) => {
202
219
  const el = itemDraggable.element();
203
220
  const style = window.getComputedStyle(el);
204
221
  const rect = el.getBoundingClientRect();
@@ -226,7 +243,7 @@ class AXDropListDirective extends NXComponent {
226
243
  const gapParts = gapValue.split(' ');
227
244
  const rowGap = parseFloat(gapParts[0]);
228
245
  const columnGap = parseFloat(gapParts[1] || gapParts[0]);
229
- this._listGap.set(this._orientation() === 'vertical' ? rowGap : columnGap);
246
+ this._listGap.set(this.orientation() === 'vertical' ? rowGap : columnGap);
230
247
  }
231
248
  else {
232
249
  this._listGap.set(0);
@@ -246,16 +263,19 @@ class AXDropListDirective extends NXComponent {
246
263
  const cachedItems = this._cachedItems();
247
264
  const originalIndex = cachedItems.findIndex((d) => d.item === activeItem);
248
265
  const siblings = cachedItems.filter((d) => d.item !== activeItem);
249
- const scrollDelta = this._orientation() === 'vertical'
266
+ const scrollDelta = this.orientation() === 'vertical'
250
267
  ? this.element.getBoundingClientRect().top - listRect.top
251
268
  : this.element.getBoundingClientRect().left - listRect.left;
252
269
  let newIndexInSiblings = -1;
253
270
  for (let i = 0; i < siblings.length; i++) {
254
271
  const sibling = siblings[i];
255
- const itemMidPoint = (this._orientation() === 'vertical' ? sibling.initialRect.top : sibling.initialRect.left) +
272
+ const itemMidPoint = (this.orientation() === 'vertical' ? sibling.initialRect.top : sibling.initialRect.left) +
256
273
  scrollDelta +
257
- (this._orientation() === 'vertical' ? sibling.height : sibling.width) / 2;
258
- if (pointerPosition < itemMidPoint) {
274
+ (this.orientation() === 'vertical' ? sibling.height : sibling.width) / 2;
275
+ const isBeforeSibling = this.isHorizontalRtl()
276
+ ? pointerPosition > itemMidPoint
277
+ : pointerPosition < itemMidPoint;
278
+ if (isBeforeSibling) {
259
279
  newIndexInSiblings = i;
260
280
  break;
261
281
  }
@@ -274,7 +294,7 @@ class AXDropListDirective extends NXComponent {
274
294
  const end = Math.max(originalIndex, potentialPlaceholderIndex);
275
295
  for (let i = start; i < end; i++) {
276
296
  const itemData = cachedItems[i];
277
- if (itemData.item !== activeItem && itemData.item.dragDisabled()) {
297
+ if (itemData.item !== activeItem && itemData.item.isDragDisabled()) {
278
298
  // We've hit a disabled "wall". Stop the placeholder at the boundary.
279
299
  if (potentialPlaceholderIndex > originalIndex) {
280
300
  return i; // Dragging down/right: stop *before* the wall.
@@ -301,19 +321,24 @@ class AXDropListDirective extends NXComponent {
301
321
  else {
302
322
  // --- INTRA-LIST DRAG: Use transforms (works well for same-list reordering) ---
303
323
  this._removePlaceholderElement();
304
- const getItemSpace = (itemData) => this._orientation() === 'vertical'
324
+ const getItemSpace = (itemData) => this.orientation() === 'vertical'
305
325
  ? itemData.height + itemData.margins.top + itemData.margins.bottom
306
326
  : itemData.width + itemData.margins.left + itemData.margins.right;
307
327
  const draggedItemSize = isIntraListDrag ? getItemSpace(this._cachedItems()[originalIndex]) : 0;
328
+ const axisSign = this.isHorizontalRtl() ? -1 : 1;
308
329
  this._cachedItems().forEach((data, index) => {
309
- if (data.item.dragDisabled()) {
330
+ if (data.item.isDragDisabled()) {
310
331
  this._renderer.removeStyle(data.element, 'transform');
311
332
  return;
312
333
  }
313
334
  const transform = this._calculateTransform(index, originalIndex, draggedItemSize, getItemSpace);
314
- this._renderer.setStyle(data.element, 'transform', transform ? `${this._orientation() === 'vertical' ? 'translateY' : 'translateX'}(${transform}px)` : '');
335
+ const signedTransform = transform * axisSign;
336
+ this._renderer.setStyle(data.element, 'transform', transform
337
+ ? `${this.orientation() === 'vertical' ? 'translateY' : 'translateX'}(${signedTransform}px)`
338
+ : '');
315
339
  });
316
340
  }
341
+ this.sortPreviewChanged.emit();
317
342
  }
318
343
  /** Creates or moves the placeholder element for inter-list drags */
319
344
  _updatePlaceholderElement(activeItem, targetIndex) {
@@ -423,6 +448,7 @@ class AXDropListDirective extends NXComponent {
423
448
  _resetAllTransforms() {
424
449
  this._cachedItems().forEach((data) => this._renderer.setStyle(data.element, 'transform', ''));
425
450
  this._removePlaceholderElement(); // Clean up inter-list placeholder
451
+ this.sortPreviewChanged.emit();
426
452
  }
427
453
  /** Resets the internal state of the directive to its initial values. */
428
454
  resetSortState() {
@@ -434,7 +460,7 @@ class AXDropListDirective extends NXComponent {
434
460
  this._listGap.set(0);
435
461
  }
436
462
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXDropListDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
437
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: AXDropListDirective, isStandalone: true, selector: "[axDropList]", inputs: { axDropList: { classPropertyName: "axDropList", publicName: "axDropList", isSignal: true, isRequired: false, transformFunction: null }, sortingDisabled: { classPropertyName: "sortingDisabled", publicName: "sortingDisabled", isSignal: true, isRequired: false, transformFunction: null }, dropListGroup: { classPropertyName: "dropListGroup", publicName: "dropListGroup", isSignal: true, isRequired: false, transformFunction: null }, dropListOrientation: { classPropertyName: "dropListOrientation", publicName: "dropListOrientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dropListDropped: "dropListDropped" }, queries: [{ propertyName: "_draggableItems", predicate: AXDragDirective, isSignal: true }], exportAs: ["axDropList"], usesInheritance: true, ngImport: i0 }); }
463
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: AXDropListDirective, isStandalone: true, selector: "[axDropList]", inputs: { axDropList: { classPropertyName: "axDropList", publicName: "axDropList", isSignal: true, isRequired: false, transformFunction: null }, sortingDisabled: { classPropertyName: "sortingDisabled", publicName: "sortingDisabled", isSignal: true, isRequired: false, transformFunction: null }, dropListGroup: { classPropertyName: "dropListGroup", publicName: "dropListGroup", isSignal: true, isRequired: false, transformFunction: null }, dropListOrientation: { classPropertyName: "dropListOrientation", publicName: "dropListOrientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dropListDropped: "dropListDropped", sortPreviewChanged: "sortPreviewChanged" }, queries: [{ propertyName: "_draggableItems", predicate: AXDragDirective, isSignal: true }], exportAs: ["axDropList"], usesInheritance: true, ngImport: i0 }); }
438
464
  }
439
465
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXDropListDirective, decorators: [{
440
466
  type: Directive,
@@ -442,7 +468,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
442
468
  selector: '[axDropList]',
443
469
  exportAs: 'axDropList',
444
470
  }]
445
- }], propDecorators: { axDropList: [{ type: i0.Input, args: [{ isSignal: true, alias: "axDropList", required: false }] }], sortingDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortingDisabled", required: false }] }], dropListGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropListGroup", required: false }] }], dropListOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropListOrientation", required: false }] }], dropListDropped: [{ type: i0.Output, args: ["dropListDropped"] }], _draggableItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AXDragDirective), { ...{ descendants: false }, isSignal: true }] }] } });
471
+ }], propDecorators: { axDropList: [{ type: i0.Input, args: [{ isSignal: true, alias: "axDropList", required: false }] }], sortingDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortingDisabled", required: false }] }], dropListGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropListGroup", required: false }] }], dropListOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropListOrientation", required: false }] }], dropListDropped: [{ type: i0.Output, args: ["dropListDropped"] }], sortPreviewChanged: [{ type: i0.Output, args: ["sortPreviewChanged"] }], _draggableItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AXDragDirective), { ...{ descendants: false }, isSignal: true }] }] } });
446
472
 
447
473
  // Zone detection constants for nested drop lists (tree nodes)
448
474
  const ZONE_TOP_THRESHOLD = 0.3; // Top 30% triggers reorder BEFORE
@@ -484,6 +510,18 @@ class AXDragDirective {
484
510
  ...(ngDevMode ? [undefined, { debugName: "dragBoundary" }] : /* istanbul ignore next */ []));
485
511
  this.dragTransitionDuration = input(150, /* @ts-ignore */
486
512
  ...(ngDevMode ? [{ debugName: "dragTransitionDuration" }] : /* istanbul ignore next */ []));
513
+ this.isDragDisabled = linkedSignal(() => this.dragDisabled(), /* @ts-ignore */
514
+ ...(ngDevMode ? [{ debugName: "isDragDisabled" }] : /* istanbul ignore next */ []));
515
+ this.lockAxis = linkedSignal(() => this.dragLockAxis(), /* @ts-ignore */
516
+ ...(ngDevMode ? [{ debugName: "lockAxis" }] : /* istanbul ignore next */ []));
517
+ this.dragBoundaryOverride = linkedSignal(() => this.dragBoundary(), /* @ts-ignore */
518
+ ...(ngDevMode ? [{ debugName: "dragBoundaryOverride" }] : /* istanbul ignore next */ []));
519
+ this.startDelay = linkedSignal(() => this.dragStartDelay() ?? 0, /* @ts-ignore */
520
+ ...(ngDevMode ? [{ debugName: "startDelay" }] : /* istanbul ignore next */ []));
521
+ this.dragTouchAction = linkedSignal(() => 'none', /* @ts-ignore */
522
+ ...(ngDevMode ? [{ debugName: "dragTouchAction" }] : /* istanbul ignore next */ []));
523
+ this.dragCursorOverride = linkedSignal(() => this.dragCursor(), /* @ts-ignore */
524
+ ...(ngDevMode ? [{ debugName: "dragCursorOverride" }] : /* istanbul ignore next */ []));
487
525
  this.dragPositionChanged = output();
488
526
  this.isMoving = signal(false, /* @ts-ignore */
489
527
  ...(ngDevMode ? [{ debugName: "isMoving" }] : /* istanbul ignore next */ []));
@@ -511,19 +549,25 @@ class AXDragDirective {
511
549
  ...(ngDevMode ? [{ debugName: "prevDropZone" }] : /* istanbul ignore next */ []));
512
550
  this.dragStartOffset = signal({ x: 0, y: 0 }, /* @ts-ignore */
513
551
  ...(ngDevMode ? [{ debugName: "dragStartOffset" }] : /* istanbul ignore next */ []));
552
+ this.pointerDownPosition = signal({ x: 0, y: 0 }, /* @ts-ignore */
553
+ ...(ngDevMode ? [{ debugName: "pointerDownPosition" }] : /* istanbul ignore next */ []));
514
554
  this.clonePointerOffset = signal({ x: 0, y: 0 }, /* @ts-ignore */
515
555
  ...(ngDevMode ? [{ debugName: "clonePointerOffset" }] : /* istanbul ignore next */ []));
516
556
  this.clonedElementViewRef = signal(null, /* @ts-ignore */
517
557
  ...(ngDevMode ? [{ debugName: "clonedElementViewRef" }] : /* istanbul ignore next */ []));
518
558
  this.rafId = null;
519
559
  this.pendingPointerEvent = null;
560
+ this.savedBodyCursor = null;
561
+ this.holdDelayTimer = null;
562
+ this.holdDelayReady = signal(false, /* @ts-ignore */
563
+ ...(ngDevMode ? [{ debugName: "holdDelayReady" }] : /* istanbul ignore next */ []));
520
564
  this._parentDropList = inject(AXDropListDirective, { optional: true, skipSelf: true, host: false });
521
565
  this._currentDropList = signal(null, /* @ts-ignore */
522
566
  ...(ngDevMode ? [{ debugName: "_currentDropList" }] : /* istanbul ignore next */ []));
523
567
  this.createCloneElement = computed(() => this.dragElementClone() || !!this._parentDropList, /* @ts-ignore */
524
568
  ...(ngDevMode ? [{ debugName: "createCloneElement" }] : /* istanbul ignore next */ []));
525
569
  this.boundaryElement = computed(() => {
526
- const boundary = this.dragBoundary();
570
+ const boundary = this.dragBoundaryOverride();
527
571
  if (!boundary) {
528
572
  return null;
529
573
  }
@@ -556,17 +600,25 @@ class AXDragDirective {
556
600
  this.listenersAttached = signal(null, /* @ts-ignore */
557
601
  ...(ngDevMode ? [{ debugName: "listenersAttached" }] : /* istanbul ignore next */ []));
558
602
  this.#dragDisabledEffect = effect(() => {
559
- if (this.dragCursor() === 'none') {
603
+ const cursor = this.dragCursorOverride();
604
+ const disabled = this.isDragDisabled();
605
+ const handle = this.handle();
606
+ if (disabled || cursor === 'none') {
607
+ if (!this.isMoving() && !this.holdDelayReady()) {
608
+ this.renderer.removeStyle(handle, 'cursor');
609
+ }
610
+ if (disabled) {
611
+ this.renderer.removeStyle(handle, 'touch-action');
612
+ }
613
+ else {
614
+ this.renderer.setStyle(handle, 'touch-action', this.dragTouchAction());
615
+ }
560
616
  return;
561
617
  }
562
- if (!this.dragDisabled()) {
563
- this.renderer.setStyle(this.handle(), 'cursor', this.dragCursor());
564
- this.renderer.setStyle(this.handle(), 'touch-action', 'none');
565
- }
566
- else {
567
- this.renderer.removeStyle(this.handle(), 'cursor');
568
- this.renderer.removeStyle(this.handle(), 'touch-action');
618
+ if (!this.isMoving() && !this.holdDelayReady()) {
619
+ this.renderer.setStyle(handle, 'cursor', cursor);
569
620
  }
621
+ this.renderer.setStyle(handle, 'touch-action', this.dragTouchAction());
570
622
  }, /* @ts-ignore */
571
623
  ...(ngDevMode ? [{ debugName: "#dragDisabledEffect" }] : /* istanbul ignore next */ []));
572
624
  this.#handleChangeEffect = effect(() => {
@@ -597,7 +649,6 @@ class AXDragDirective {
597
649
  if (this._parentDropList) {
598
650
  this.setElementTransition(this.element());
599
651
  }
600
- this.renderer.setStyle(this.element(), 'z-index', '1');
601
652
  this.elementOpacity.set(getComputedStyle(this.element()).opacity);
602
653
  }
603
654
  }
@@ -615,7 +666,56 @@ class AXDragDirective {
615
666
  if (this._parentDropList && this.isDragging()) {
616
667
  this._parentDropList.cancelSort();
617
668
  }
669
+ this.clearActiveDragCursor();
670
+ this.clearHoldDelayTimer();
671
+ this.renderer.removeStyle(this.element(), 'z-index');
672
+ }
673
+ }
674
+ resolveDropCursor(canDrop) {
675
+ if (this.dragCursorOverride() === 'grab') {
676
+ return canDrop ? 'grabbing' : 'not-allowed';
618
677
  }
678
+ return canDrop ? 'move' : 'not-allowed';
679
+ }
680
+ setActiveDragCursor(cursor) {
681
+ if (!isPlatformBrowser(this.platformId))
682
+ return;
683
+ if (this.savedBodyCursor === null) {
684
+ this.savedBodyCursor = this.document.body.style.cursor;
685
+ }
686
+ this.document.body.style.cursor = cursor;
687
+ }
688
+ clearActiveDragCursor() {
689
+ if (!isPlatformBrowser(this.platformId) || this.savedBodyCursor === null)
690
+ return;
691
+ this.document.body.style.cursor = this.savedBodyCursor;
692
+ this.savedBodyCursor = null;
693
+ }
694
+ clearHoldDelayTimer() {
695
+ if (this.holdDelayTimer !== null) {
696
+ clearTimeout(this.holdDelayTimer);
697
+ this.holdDelayTimer = null;
698
+ }
699
+ this.holdDelayReady.set(false);
700
+ }
701
+ scheduleHoldDelayFeedback() {
702
+ this.clearHoldDelayTimer();
703
+ const delay = this.startDelay();
704
+ if (delay <= 0 || this.dragCursorOverride() !== 'grab') {
705
+ return;
706
+ }
707
+ this.holdDelayTimer = setTimeout(() => {
708
+ this.holdDelayTimer = null;
709
+ if (!this.isDragging() || this.isMoving() || this.isDragDisabled()) {
710
+ return;
711
+ }
712
+ this.holdDelayReady.set(true);
713
+ this.renderer.setStyle(this.handle(), 'cursor', 'grabbing');
714
+ this.setActiveDragCursor('grabbing');
715
+ }, delay);
716
+ }
717
+ setElementDropCursor(element, cursor) {
718
+ this.renderer.setStyle(element, 'cursor', cursor);
619
719
  }
620
720
  setElementTransition(element) {
621
721
  this.renderer.setStyle(element, 'transition-property', 'transform, opacity, translate');
@@ -628,24 +728,23 @@ class AXDragDirective {
628
728
  handlePointerDown(e) {
629
729
  if (!isPlatformBrowser(this.platformId))
630
730
  return;
631
- if (!this.axDrag() || this.dragDisabled() || e.button !== 0 || this._parentDropList?.sortingDisabled())
731
+ if (!this.axDrag() || this.isDragDisabled() || e.button !== 0 || this._parentDropList?.isSortingDisabled())
632
732
  return;
633
733
  this.isDragging.set(true);
634
734
  this.movedAfterDelay.set(false);
635
- if (this.dragCursor() === 'move') {
735
+ if (this.dragCursorOverride() === 'move') {
636
736
  this.renderer.setStyle(this.handle(), 'cursor', 'move');
637
737
  }
638
- else if (this.dragCursor() === 'grab') {
639
- this.renderer.setStyle(this.handle(), 'cursor', 'grabbing');
640
- }
641
738
  this.dragStartOffset.set({ x: e.clientX - this.currentAxis().x, y: e.clientY - this.currentAxis().y });
739
+ this.pointerDownPosition.set({ x: e.clientX, y: e.clientY });
642
740
  this.renderer.setStyle(this.handle(), 'userSelect', 'none');
643
741
  this.activePointerId.set(e.pointerId);
644
742
  this.addDocumentListeners();
645
743
  this.dragStartTime.set(Date.now());
744
+ this.scheduleHoldDelayFeedback();
646
745
  }
647
746
  handleDblClick() {
648
- if (!this.dragResetOnDblClick() || this.dragDisabled())
747
+ if (!this.dragResetOnDblClick() || this.isDragDisabled())
649
748
  return;
650
749
  this.setPosition(0, 0);
651
750
  }
@@ -653,17 +752,26 @@ class AXDragDirective {
653
752
  if (!this.isDragging() || !isPlatformBrowser(this.platformId) || e.pointerId !== this.activePointerId()) {
654
753
  return;
655
754
  }
656
- e.preventDefault();
657
755
  if (!this.isMoving()) {
658
- const delay = this.dragStartDelay() ?? 0;
659
- // FIX: Delay logic was inverted
660
- if (Date.now() - this.dragStartTime() < delay) {
661
- return; // Still waiting for delay
756
+ const delay = this.startDelay();
757
+ const down = this.pointerDownPosition();
758
+ const movedX = e.clientX - down.x;
759
+ const movedY = e.clientY - down.y;
760
+ const movedDistance = Math.sqrt(movedX * movedX + movedY * movedY);
761
+ if (delay > 0 && !this.holdDelayReady() && Date.now() - this.dragStartTime() < delay) {
762
+ if (movedDistance > 8) {
763
+ this.abortDragSequence();
764
+ }
765
+ return;
662
766
  }
767
+ e.preventDefault();
663
768
  this.isMoving.set(true);
664
769
  this.handle().setPointerCapture(e.pointerId);
665
770
  this.startDrag(e);
666
771
  }
772
+ else {
773
+ e.preventDefault();
774
+ }
667
775
  // Run position updates outside Angular zone for better performance
668
776
  this.zone.runOutsideAngular(() => {
669
777
  if (this.createCloneElement()) {
@@ -694,7 +802,24 @@ class AXDragDirective {
694
802
  this.dropZoneHoverHandler(e);
695
803
  }
696
804
  }
805
+ abortDragSequence() {
806
+ this.clearHoldDelayTimer();
807
+ this.isDragging.set(false);
808
+ this.isMoving.set(false);
809
+ this.activePointerId.set(null);
810
+ this.removeDocumentListeners();
811
+ this.renderer.removeStyle(this.handle(), 'userSelect');
812
+ if (this.dragCursorOverride() === 'grab') {
813
+ this.renderer.setStyle(this.handle(), 'cursor', 'grab');
814
+ }
815
+ this.clearActiveDragCursor();
816
+ }
697
817
  startDrag(e) {
818
+ if (this.dragCursorOverride() === 'grab') {
819
+ this.renderer.setStyle(this.handle(), 'cursor', 'grabbing');
820
+ this.setActiveDragCursor('grabbing');
821
+ }
822
+ this.renderer.setStyle(this.element(), 'z-index', '1');
698
823
  // Add CDK-style classes
699
824
  this.renderer.addClass(this.element(), 'ax-dragging');
700
825
  this.renderer.addClass(this.element(), 'ax-drag-placeholder');
@@ -722,9 +847,11 @@ class AXDragDirective {
722
847
  if (e.pointerId !== this.activePointerId() || !isPlatformBrowser(this.platformId) || !this.isDragging()) {
723
848
  return;
724
849
  }
725
- if (this.dragCursor() === 'grab') {
850
+ this.clearHoldDelayTimer();
851
+ if (this.dragCursorOverride() === 'grab') {
726
852
  this.renderer.setStyle(this.handle(), 'cursor', 'grab');
727
853
  }
854
+ this.clearActiveDragCursor();
728
855
  const wasMoving = this.isMoving();
729
856
  this.isMoving.set(false);
730
857
  this.isDragging.set(false);
@@ -738,6 +865,7 @@ class AXDragDirective {
738
865
  this.renderer.removeStyle(this.handle(), 'userSelect');
739
866
  return;
740
867
  }
868
+ this.renderer.removeStyle(this.element(), 'z-index');
741
869
  this.preventClicking();
742
870
  const transform = this.element().style.transform;
743
871
  let x = 0;
@@ -792,12 +920,15 @@ class AXDragDirective {
792
920
  // Update cursor based on whether drop is allowed
793
921
  if (listUnderPointer) {
794
922
  const cloneEl = this.clonedElement();
795
- const cursor = canDrop ? 'move' : 'not-allowed';
796
- if (cloneEl) {
797
- this.renderer.setStyle(cloneEl, 'cursor', cursor);
923
+ const cursor = this.resolveDropCursor(!!canDrop);
924
+ if (this.dragCursorOverride() === 'grab') {
925
+ this.setActiveDragCursor(cursor);
926
+ }
927
+ else if (cloneEl) {
928
+ this.setElementDropCursor(cloneEl, cursor);
798
929
  }
799
930
  else {
800
- this.renderer.setStyle(this.element(), 'cursor', cursor);
931
+ this.setElementDropCursor(this.element(), cursor);
801
932
  }
802
933
  }
803
934
  if (targetList !== previousList) {
@@ -815,7 +946,7 @@ class AXDragDirective {
815
946
  this._currentDropList()?.sort(e, this);
816
947
  }
817
948
  canDropInList(list) {
818
- if (!list || list.sortingDisabled() || !this._parentDropList) {
949
+ if (!list || list.isSortingDisabled() || !this._parentDropList) {
819
950
  return false;
820
951
  }
821
952
  if (list === this._parentDropList) {
@@ -959,9 +1090,9 @@ class AXDragDirective {
959
1090
  constrainedX = clamp(x, minX, maxX);
960
1091
  constrainedY = clamp(y, minY, maxY);
961
1092
  }
962
- if (this.dragLockAxis() === 'x')
1093
+ if (this.lockAxis() === 'x')
963
1094
  constrainedY = this.currentAxis().y;
964
- if (this.dragLockAxis() === 'y')
1095
+ if (this.lockAxis() === 'y')
965
1096
  constrainedX = this.currentAxis().x;
966
1097
  // Direct DOM manipulation without triggering change detection
967
1098
  this.renderer.setStyle(this.element(), 'translate', `${constrainedX}px ${constrainedY}px`);
@@ -983,9 +1114,9 @@ class AXDragDirective {
983
1114
  constrainedX = clamp(x, boundaryRect.left, boundaryRect.right - cloneWidth);
984
1115
  constrainedY = clamp(y, boundaryRect.top, boundaryRect.bottom - cloneHeight);
985
1116
  }
986
- if (this.dragLockAxis() === 'x')
1117
+ if (this.lockAxis() === 'x')
987
1118
  constrainedY = this.currentCloneAxis().y;
988
- if (this.dragLockAxis() === 'y')
1119
+ if (this.lockAxis() === 'y')
989
1120
  constrainedX = this.currentCloneAxis().x;
990
1121
  // Direct DOM manipulation without triggering change detection
991
1122
  this.renderer.setStyle(cloneEl, 'translate', `${constrainedX}px ${constrainedY}px`);