forty-cdk 0.3.0 → 0.4.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,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, signal, Directive, afterNextRender, ElementRef, DOCUMENT, PLATFORM_ID, DestroyRef, Injector, input, booleanAttribute, output, computed, TemplateRef, ViewContainerRef, contentChild, model, effect } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
- import { createDefaults, fencePlaceholderIndex, placeholderInsertion, playFlip, LiveAnnouncer, injectPrefersReducedMotion, injectTextDirection, RovingTabindex, Collection, reconcileRovingActive, registerHandle, createAutoScroller, firstEnabledHost, moveIndex, buildDragSlots, indexOfSlot, PreviewController, resolveDropTarget, stepSlot, createPointerDragSession, createTemplatePreview, resolveListNavigation, clampPreviewPosition } from 'forty-cdk/core';
4
+ import { createDefaults, fencePlaceholderIndex, placeholderInsertion, playFlip, LiveAnnouncer, injectPrefersReducedMotion, injectTextDirection, RovingTabindex, Collection, reconcileRovingActive, registerHandle, createAutoScroller, firstEnabledHost, moveIndex, buildDragSlots, indexOfSlot, PreviewController, resolveDropTarget, stepSlot, createPointerHandleGuard, createPointerDragSession, createTemplatePreview, resolveListNavigation, clampPreviewPosition } from 'forty-cdk/core';
5
5
 
6
6
  const FOR_DRAG_DROP_CONTEXT = new InjectionToken('FOR_DRAG_DROP_CONTEXT');
7
7
  function injectDropListContext(piece) {
@@ -325,6 +325,7 @@ class ForDropList {
325
325
  #sorter = null;
326
326
  #autoScroller = null;
327
327
  #lastPoint = null;
328
+ #resolveRaf = null;
328
329
  /** Insertion index this list is the current drop target at, else `null`. */
329
330
  dragOverIndex = this.#dragOver.asReadonly();
330
331
  /** `true` while a drag originating from this list is in progress. */
@@ -344,7 +345,13 @@ class ForDropList {
344
345
  maxSpeed: this.#defaults.autoScrollMaxSpeed,
345
346
  onFrame: () => this.#onAutoScrollFrame(),
346
347
  });
347
- this.#destroyRef.onDestroy(() => this.#autoScroller?.stop());
348
+ this.#destroyRef.onDestroy(() => {
349
+ this.#autoScroller?.stop();
350
+ if (this.#resolveRaf !== null) {
351
+ this.#document.defaultView?.cancelAnimationFrame(this.#resolveRaf);
352
+ this.#resolveRaf = null;
353
+ }
354
+ });
348
355
  }
349
356
  }
350
357
  registerItem(handle) {
@@ -442,13 +449,27 @@ class ForDropList {
442
449
  return;
443
450
  }
444
451
  this.#lastPoint = point;
445
- this.#resolveDrop(point, lifted);
446
452
  if (this.autoScroll()) {
447
453
  this.#autoScroller?.update(point);
448
454
  }
449
455
  else {
450
456
  this.#autoScroller?.stop();
451
457
  }
458
+ if (this.#resolveRaf !== null) {
459
+ return;
460
+ }
461
+ this.#resolveDrop(point, lifted);
462
+ const win = this.#document.defaultView;
463
+ if (win) {
464
+ this.#resolveRaf = win.requestAnimationFrame(() => {
465
+ this.#resolveRaf = null;
466
+ const currentLifted = this.#liftedHost();
467
+ const currentPoint = this.#lastPoint;
468
+ if (currentLifted !== null && currentPoint !== null) {
469
+ this.#resolveDrop(currentPoint, currentLifted);
470
+ }
471
+ });
472
+ }
452
473
  }
453
474
  #resolveDrop(point, lifted) {
454
475
  const connected = this.#effectiveConnected();
@@ -480,6 +501,16 @@ class ForDropList {
480
501
  this.#announcer.announce(this.#defaults.announceMove(label, target.index + 1, targetCtx.items().length), 'polite');
481
502
  }
482
503
  }
504
+ #flushResolve(lifted) {
505
+ if (this.#resolveRaf === null) {
506
+ return;
507
+ }
508
+ this.#document.defaultView?.cancelAnimationFrame(this.#resolveRaf);
509
+ this.#resolveRaf = null;
510
+ if (this.#lastPoint !== null) {
511
+ this.#resolveDrop(this.#lastPoint, lifted);
512
+ }
513
+ }
483
514
  #resolveBoundary() {
484
515
  const boundary = this.boundary();
485
516
  if (boundary === null) {
@@ -536,6 +567,7 @@ class ForDropList {
536
567
  if (liftedHost === null) {
537
568
  return;
538
569
  }
570
+ this.#flushResolve(liftedHost);
539
571
  const connected = this.#effectiveConnected();
540
572
  const items = this.#items.items();
541
573
  const slots = buildDragSlots(items.length, connected.map((c) => c.items().length));
@@ -609,6 +641,10 @@ class ForDropList {
609
641
  }, /* @ts-ignore */
610
642
  ...(ngDevMode ? [{ debugName: "#effectiveConnected" }] : /* istanbul ignore next */ []));
611
643
  #teardown(connected, keepPreview = false) {
644
+ if (this.#resolveRaf !== null) {
645
+ this.#document.defaultView?.cancelAnimationFrame(this.#resolveRaf);
646
+ this.#resolveRaf = null;
647
+ }
612
648
  this.#autoScroller?.stop();
613
649
  this.#lastPoint = null;
614
650
  this.#dragOver.set(null);
@@ -708,7 +744,6 @@ class ForDraggable {
708
744
  /** `true` while a pointer-drag placeholder template occupies this item's slot (host hidden). */
709
745
  placeholderActive = signal(false, /* @ts-ignore */
710
746
  ...(ngDevMode ? [{ debugName: "placeholderActive" }] : /* istanbul ignore next */ []));
711
- #handles = new Set();
712
747
  #pointerDragging = false;
713
748
  #pointerSession = null;
714
749
  /** Data payload handed back in the `dragDrop` event when this item is dropped. */
@@ -726,6 +761,7 @@ class ForDraggable {
726
761
  dragEnd = output();
727
762
  effectiveDisabled = computed(() => this.dragDisabled() || this.#list.disabled(), /* @ts-ignore */
728
763
  ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
764
+ #handleGuard = createPointerHandleGuard(this.effectiveDisabled);
729
765
  /** `true` when this item is the currently lifted draggable. Reflected as `data-dragging`. */
730
766
  lifted = computed(() => this.#list.isLifted(this.#host.nativeElement), /* @ts-ignore */
731
767
  ...(ngDevMode ? [{ debugName: "lifted" }] : /* istanbul ignore next */ []));
@@ -746,8 +782,7 @@ class ForDraggable {
746
782
  return this.#list.isFirstFocusableItem(this.#host.nativeElement) ? 0 : -1;
747
783
  }, /* @ts-ignore */
748
784
  ...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
749
- touchAction = computed(() => !this.effectiveDisabled() && this.#handles.size === 0 ? 'none' : null, /* @ts-ignore */
750
- ...(ngDevMode ? [{ debugName: "touchAction" }] : /* istanbul ignore next */ []));
785
+ touchAction = this.#handleGuard.touchAction;
751
786
  constructor() {
752
787
  const handle = {
753
788
  host: this.#host.nativeElement,
@@ -762,7 +797,7 @@ class ForDraggable {
762
797
  armThreshold: POINTER_ARM_THRESHOLD_PX$1,
763
798
  cancelOnEscape: true,
764
799
  capturePointer: true,
765
- canStart: (event) => this.#canStartPointer(event),
800
+ canStart: (event) => this.#handleGuard.canStart(event),
766
801
  onLift: (event) => this.#onPointerLift(event),
767
802
  onMove: (event) => this.#onPointerMove(event),
768
803
  onCommit: () => this.#onPointerCommit(),
@@ -773,23 +808,10 @@ class ForDraggable {
773
808
  }
774
809
  }
775
810
  registerHandle(el) {
776
- this.#handles.add(el);
811
+ this.#handleGuard.register(el);
777
812
  }
778
813
  unregisterHandle(el) {
779
- this.#handles.delete(el);
780
- }
781
- #canStartPointer(event) {
782
- if (this.effectiveDisabled()) {
783
- return false;
784
- }
785
- if (event.pointerType === 'mouse' && event.button !== 0) {
786
- return false;
787
- }
788
- if (this.#handles.size === 0) {
789
- return true;
790
- }
791
- const target = event.target;
792
- return target !== null && [...this.#handles].some((h) => h.contains(target));
814
+ this.#handleGuard.unregister(el);
793
815
  }
794
816
  #onPointerLift(event) {
795
817
  const preview = this.#buildPreview();
@@ -951,12 +973,12 @@ class ForFreeDrag {
951
973
  #document = inject(DOCUMENT);
952
974
  #destroyRef = inject(DestroyRef);
953
975
  #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
954
- #handles = new Set();
955
976
  #lift = signal(null, /* @ts-ignore */
956
977
  ...(ngDevMode ? [{ debugName: "#lift" }] : /* istanbul ignore next */ []));
957
978
  #pointerSession = null;
958
979
  /** When true, the element can't be dragged (it stays focusable; the transform doesn't change). */
959
980
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
981
+ #handleGuard = createPointerHandleGuard(this.disabled);
960
982
  /**
961
983
  * The element actually moved: an `HTMLElement`, or a selector resolved via `closest()` from the
962
984
  * host. `null` (the default) moves the host itself. Mirrors CDK's `cdkDragRootElement` — drag a
@@ -992,8 +1014,7 @@ class ForFreeDrag {
992
1014
  /** True while a pointer drag is armed. Reflected as `data-dragging`. */
993
1015
  dragging = computed(() => this.#lift() !== null, /* @ts-ignore */
994
1016
  ...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
995
- touchAction = computed(() => !this.disabled() && this.#handles.size === 0 ? 'none' : null, /* @ts-ignore */
996
- ...(ngDevMode ? [{ debugName: "touchAction" }] : /* istanbul ignore next */ []));
1017
+ touchAction = this.#handleGuard.touchAction;
997
1018
  constructor() {
998
1019
  if (this.#isBrowser) {
999
1020
  effect(() => {
@@ -1007,7 +1028,7 @@ class ForFreeDrag {
1007
1028
  armThreshold: POINTER_ARM_THRESHOLD_PX,
1008
1029
  cancelOnEscape: true,
1009
1030
  capturePointer: true,
1010
- canStart: (event) => this.#canStartPointer(event),
1031
+ canStart: (event) => this.#handleGuard.canStart(event),
1011
1032
  onLift: (event) => this.#onLift(event),
1012
1033
  onMove: (event) => this.#onMove(event),
1013
1034
  onCommit: () => this.#onCommit(),
@@ -1017,10 +1038,10 @@ class ForFreeDrag {
1017
1038
  }
1018
1039
  }
1019
1040
  registerHandle(el) {
1020
- this.#handles.add(el);
1041
+ this.#handleGuard.register(el);
1021
1042
  }
1022
1043
  unregisterHandle(el) {
1023
- this.#handles.delete(el);
1044
+ this.#handleGuard.unregister(el);
1024
1045
  }
1025
1046
  #resolvedRoot = computed(() => {
1026
1047
  const root = this.rootElement();
@@ -1040,19 +1061,6 @@ class ForFreeDrag {
1040
1061
  ? this.#host.nativeElement.closest(boundary)
1041
1062
  : boundary;
1042
1063
  }
1043
- #canStartPointer(event) {
1044
- if (this.disabled()) {
1045
- return false;
1046
- }
1047
- if (event.pointerType === 'mouse' && event.button !== 0) {
1048
- return false;
1049
- }
1050
- if (this.#handles.size === 0) {
1051
- return true;
1052
- }
1053
- const target = event.target;
1054
- return target !== null && [...this.#handles].some((h) => h.contains(target));
1055
- }
1056
1064
  #onLift(event) {
1057
1065
  const rect = this.#resolvedRoot().getBoundingClientRect();
1058
1066
  const position = this.position();