ngx-kit 8.0.1 → 8.1.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, EventEmitter, Injectable, InjectionToken, model, input, output, inject, Renderer2, effect, Input, Directive, HostListener, ApplicationRef, ContentChild, Output, ElementRef } from '@angular/core';
2
+ import { signal, EventEmitter, Injectable, InjectionToken, inject, ApplicationRef, Directive, model, input, output, Renderer2, effect, ContentChild, Input, HostListener, Output, ElementRef } from '@angular/core';
3
3
  import { DOCUMENT } from '@angular/common';
4
4
 
5
5
  /**
@@ -79,103 +79,87 @@ function adjustForZoom(value) {
79
79
  return zoom ? value / zoom : value;
80
80
  }
81
81
 
82
- /**
83
- * Copy the browser-resolved visual state of an element.
84
- *
85
- * A drag preview is moved to <body>, so selectors depending on ancestors no
86
- * longer apply. Copying computed styles (including custom properties) makes
87
- * the preview visually independent from the original DOM subtree.
88
- */
89
- function copyComputedStyle(source, target) {
90
- copyStyleValues(source, target);
91
- // Make the preview deterministic once it is taken out of normal flow (root only:
92
- // descendants keep their own margins so the internal layout is unchanged).
93
- target.style.setProperty('margin', '0', 'important');
94
- target.style.setProperty('box-sizing', 'border-box', 'important');
95
- }
96
- function copyStyleValues(source, target) {
97
- const computed = window.getComputedStyle(source);
98
- for (let i = 0; i < computed.length; i++) {
99
- const property = computed.item(i);
100
- const value = computed.getPropertyValue(property);
101
- if (value) {
102
- target.style.setProperty(property, value, computed.getPropertyPriority(property));
82
+ /** Above any app chrome (modals, sticky headers) but below the browser's own UI. */
83
+ const PREVIEW_Z_INDEX = '2147483000';
84
+ class DragPreviewRef {
85
+ tpl;
86
+ /** Set by `ngxDragPreview` so the embedded view can join Angular's change-detection tree. */
87
+ appRef;
88
+ element;
89
+ isCustom = false;
90
+ _view;
91
+ attach(dragEl, rect) {
92
+ const doc = dragEl.ownerDocument;
93
+ if (this.tpl) {
94
+ this._view = this.tpl.createEmbeddedView({});
95
+ this.appRef?.attachView(this._view);
96
+ // Render bindings inside the embedded view before we move its DOM nodes.
97
+ this._view.detectChanges();
98
+ const element = this._view.rootNodes.find((node) => node.nodeType === Node.ELEMENT_NODE);
99
+ if (!element) {
100
+ this.appRef?.detachView(this._view);
101
+ this._view.destroy();
102
+ this._view = undefined;
103
+ throw new Error('ngxDragPreview template must contain exactly one HTMLElement root node ');
104
+ }
105
+ this.element = element;
106
+ this.isCustom = true;
107
+ }
108
+ else {
109
+ this.isCustom = false;
110
+ const preview = dragEl.cloneNode(true);
111
+ this.element = preview;
112
+ // Avoid duplicated ids in the document.
113
+ this.element.removeAttribute('id');
114
+ this.element.querySelectorAll('[id]').forEach((n) => n.removeAttribute('id'));
115
+ this.element.classList.add('ngx-drag-preview');
116
+ this.element.setAttribute('aria-hidden', 'true');
117
+ this.element.setAttribute('inert', '');
118
+ Object.assign(this.element.style, {
119
+ width: `${rect.width}px`,
120
+ height: `${rect.height}px`,
121
+ margin: '0',
122
+ boxSizing: 'border-box',
123
+ });
124
+ }
125
+ Object.assign(this.element.style, {
126
+ position: 'fixed',
127
+ top: `${rect.top}px`,
128
+ bottom: 'auto',
129
+ pointerEvents: 'none',
130
+ transition: 'none',
131
+ animation: 'none',
132
+ transform: 'none',
133
+ visibility: 'visible',
134
+ display: this.element.style.display === 'none' ? '' : this.element.style.display,
135
+ willChange: 'transform',
136
+ zIndex: PREVIEW_Z_INDEX,
137
+ });
138
+ if (isRtl(doc.body)) {
139
+ Object.assign(this.element.style, {
140
+ right: `${window.innerWidth - rect.right}px`,
141
+ left: 'auto',
142
+ });
143
+ }
144
+ else {
145
+ Object.assign(this.element.style, {
146
+ left: `${rect.left}px`,
147
+ right: 'auto',
148
+ });
103
149
  }
150
+ doc.body.appendChild(this.element);
151
+ return this.element;
104
152
  }
105
- }
106
- function copyComputedStyleTree(source, target) {
107
- copyComputedStyle(source, target);
108
- copyDescendants(source, target);
109
- }
110
- function copyDescendants(source, target) {
111
- const sourceChildren = Array.from(source.children);
112
- const targetChildren = Array.from(target.children);
113
- const count = Math.min(sourceChildren.length, targetChildren.length);
114
- for (let i = 0; i < count; i++) {
115
- copyStyleValues(sourceChildren[i], targetChildren[i]);
116
- copyDescendants(sourceChildren[i], targetChildren[i]);
153
+ detach() {
154
+ this.element?.remove();
155
+ if (this._view) {
156
+ this.appRef?.detachView(this._view);
157
+ this._view.destroy();
158
+ this._view = undefined;
159
+ }
160
+ this.element = undefined;
117
161
  }
118
162
  }
119
- /** Backward-compatible helper retained for consumers of older versions. */
120
- function copyEssentialStyles(source, target) {
121
- copyComputedStyle(source, target);
122
- target.style.setProperty('box-sizing', 'border-box');
123
- }
124
- function combineTransforms(transform, initialTransform) {
125
- return initialTransform && initialTransform !== 'none'
126
- ? transform + ' ' + initialTransform
127
- : transform;
128
- }
129
-
130
- /** Above any app chrome (modals, sticky headers) but below the browser's own UI. */
131
- const PREVIEW_Z_INDEX = '2147483000';
132
- /**
133
- * Creates the visual that follows the pointer while an item of a drop list is dragged.
134
- *
135
- * It is appended to `<body>` with `position: fixed`, so no ancestor of the original item
136
- * (`overflow: hidden`, scroll containers, clipping, transformed/positioned parents) can clip
137
- * or offset it. Computed styles are copied recursively first, because once the clone leaves
138
- * its list every selector that depended on an ancestor (`.column .card {}`) and every
139
- * inherited property (font, colour, `direction`) would otherwise be lost.
140
- *
141
- * Only used for drop-list drags. Free drag never calls this.
142
- */
143
- function cloneDragElementInBody(dragEl, rect, pointerX, pointerY) {
144
- const doc = dragEl.ownerDocument;
145
- const preview = dragEl.cloneNode(true);
146
- // Avoid duplicated ids in the document.
147
- preview.removeAttribute('id');
148
- preview.querySelectorAll('[id]').forEach((n) => n.removeAttribute('id'));
149
- preview.classList.add('ngx-drag-preview');
150
- preview.setAttribute('aria-hidden', 'true');
151
- preview.setAttribute('inert', '');
152
- copyComputedStyleTree(dragEl, preview);
153
- Object.assign(preview.style, {
154
- position: 'fixed',
155
- top: `${rect.top}px`,
156
- left: `${rect.left}px`,
157
- right: 'auto',
158
- bottom: 'auto',
159
- width: `${rect.width}px`,
160
- height: `${rect.height}px`,
161
- margin: '0',
162
- pointerEvents: 'none',
163
- boxSizing: 'border-box',
164
- transition: 'none',
165
- animation: 'none',
166
- transform: 'none',
167
- visibility: 'visible',
168
- display: preview.style.display === 'none' ? '' : preview.style.display,
169
- willChange: 'transform',
170
- zIndex: PREVIEW_Z_INDEX,
171
- });
172
- doc.body.appendChild(preview);
173
- return {
174
- element: preview,
175
- offsetX: pointerX - rect.left,
176
- offsetY: pointerY - rect.top,
177
- };
178
- }
179
163
 
180
164
  let zIndexCounter = 1000;
181
165
  class DragRef {
@@ -196,6 +180,7 @@ class DragRef {
196
180
  placeholder;
197
181
  originDropList = null;
198
182
  originIndex = -1;
183
+ preview;
199
184
  isDragging = signal(false, /* @ts-ignore */
200
185
  ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
201
186
  position = signal({ x: 0, y: 0 }, /* @ts-ignore */
@@ -208,9 +193,6 @@ class DragRef {
208
193
  moveDx = 0;
209
194
  moveDy = 0;
210
195
  scrollCompensation = { x: 0, y: 0 };
211
- preview;
212
- previewOffsetX = 0;
213
- previewOffsetY = 0;
214
196
  sourceVisibility = '';
215
197
  sourceDisplay = '';
216
198
  sourcePointerEvents = '';
@@ -283,12 +265,8 @@ class DragRef {
283
265
  */
284
266
  if (list) {
285
267
  this.listDrag = true;
286
- // Order matters: clone while the source is still visible and fully styled, hide it,
287
- // and only THEN measure (the placeholder takes over the source's slot).
288
- const preview = cloneDragElementInBody(this.el, this.startRect, pointer.x, pointer.y);
289
- this.preview = preview.element;
290
- this.previewOffsetX = preview.offsetX;
291
- this.previewOffsetY = preview.offsetY;
268
+ this.preview ??= new DragPreviewRef();
269
+ this.preview.attach(this.el, this.startRect);
292
270
  this.el.style.visibility = 'hidden';
293
271
  this.el.style.display = 'none';
294
272
  this.el.style.pointerEvents = 'none';
@@ -350,7 +328,7 @@ class DragRef {
350
328
  this.isDragging.set(false);
351
329
  this.placeholder?.detach();
352
330
  this.placeholder = undefined;
353
- this.preview?.remove();
331
+ this.preview?.detach();
354
332
  this.preview = undefined;
355
333
  this.el.classList.remove('ngx-draggable--dragging');
356
334
  this.el.style.visibility = this.sourceVisibility;
@@ -564,7 +542,7 @@ class DragRef {
564
542
  const event = target?._finish(this, true) ?? null;
565
543
  for (const l of this.cachedLists)
566
544
  l._release(this);
567
- this.preview?.remove();
545
+ this.preview?.detach();
568
546
  this.preview = undefined;
569
547
  this.placeholder = undefined;
570
548
  this.activeDropList = null;
@@ -584,9 +562,9 @@ class DragRef {
584
562
  target.onDrop.emit(event);
585
563
  }
586
564
  updatePreview() {
587
- if (!this.preview)
565
+ if (!this.preview || !this.preview.element)
588
566
  return;
589
- this.preview.style.transform = `translate3d(${this.moveDx}px, ${this.moveDy}px, 0)`;
567
+ this.preview.element.style.transform = `translate3d(${this.moveDx}px, ${this.moveDy}px, 0)`;
590
568
  }
591
569
  }
592
570
  /** Is `list` nested (at any depth) inside `ancestor`, per the parent/child list tree? */
@@ -598,18 +576,64 @@ function isDescendantOfList(list, ancestor) {
598
576
  return false;
599
577
  }
600
578
 
579
+ class ElementHelper {
580
+ static collectionHas(a, b) {
581
+ //helper function (see below)
582
+ for (var i = 0, len = a.length; i < len; i++) {
583
+ if (a[i] == b)
584
+ return true;
585
+ }
586
+ return false;
587
+ }
588
+ static findParentBySelector(elm, selector) {
589
+ var all = document.querySelectorAll(selector);
590
+ var cur = elm.parentNode;
591
+ while (cur && !ElementHelper.collectionHas(all, cur)) {
592
+ //keep going up until you find a match
593
+ cur = cur.parentNode; //go up
594
+ }
595
+ return cur; //will return null if not found
596
+ }
597
+ }
598
+ function getFirstLevelDraggables(container) {
599
+ const all = Array.from(container.querySelectorAll('.ngx-draggable:not(.dragging)'));
600
+ const firstLevel = [];
601
+ for (const el of all) {
602
+ let parent = el.parentElement;
603
+ let isNested = false;
604
+ while (parent && parent !== container) {
605
+ if (parent.classList.contains('ngx-draggable')) {
606
+ isNested = true;
607
+ break;
608
+ }
609
+ parent = parent.parentElement;
610
+ }
611
+ if (!isNested) {
612
+ firstLevel.push(el);
613
+ }
614
+ }
615
+ return firstLevel;
616
+ }
617
+ function isElementNode(node) {
618
+ return node?.nodeType === Node.ELEMENT_NODE;
619
+ }
620
+ function isDocumentNode(node) {
621
+ return node?.nodeType === Node.DOCUMENT_NODE;
622
+ }
623
+ function isShadowRootNode(node) {
624
+ return node?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && 'host' in node;
625
+ }
626
+ function getOwnerWindow(node) {
627
+ return node?.ownerDocument?.defaultView ?? null;
628
+ }
629
+ function isWindow(value) {
630
+ return (value != null &&
631
+ typeof value === 'object' &&
632
+ 'window' in value &&
633
+ value.window === value);
634
+ }
635
+
601
636
  class PlaceHolderRef {
602
- /**
603
- * Custom placeholder content, from `*ngxPlaceholder` inside the `ngxDropList`. When unset,
604
- * `attach()` falls back to a shallow clone of the dragged element (previous behaviour).
605
- *
606
- * Example:
607
- *
608
- * <div ngxDropList>
609
- * <span class="my-placeholder" *ngxPlaceholder></span>
610
- * ...
611
- * </div>
612
- */
613
637
  tpl;
614
638
  /** Set by `NgxPlaceholder` so the embedded view can join Angular's change-detection tree. */
615
639
  appRef;
@@ -626,7 +650,7 @@ class PlaceHolderRef {
626
650
  this.appRef?.attachView(this._view);
627
651
  // Render bindings inside the embedded view before we move its DOM nodes.
628
652
  this._view.detectChanges();
629
- const element = this._view.rootNodes.find((node) => node instanceof HTMLElement);
653
+ const element = this._view.rootNodes.find((node) => isElementNode(node));
630
654
  if (!element) {
631
655
  this.appRef?.detachView(this._view);
632
656
  this._view.destroy();
@@ -1142,14 +1166,15 @@ class SortSession {
1142
1166
  let slot = -1;
1143
1167
  let allOthers = 0;
1144
1168
  for (const node of Array.from(this.container.children)) {
1145
- if (!(node instanceof HTMLElement))
1169
+ if (!isElementNode(node))
1146
1170
  continue;
1147
- const isSlot = node === this.placeholder;
1148
- if (!isSlot && !this.itemElements.has(node))
1171
+ const el = node;
1172
+ const isSlot = el === this.placeholder;
1173
+ if (!isSlot && !this.itemElements.has(el))
1149
1174
  continue;
1150
1175
  const modelIdx = isSlot ? -1 : allOthers++;
1151
- const rect = node.getBoundingClientRect();
1152
- const off = this.applied.get(node);
1176
+ const rect = el.getBoundingClientRect();
1177
+ const off = this.applied.get(el);
1153
1178
  const box = toBox(rect);
1154
1179
  if (off) {
1155
1180
  // Strip our own displacement so the snapshot always describes the *undisplaced* layout.
@@ -1165,14 +1190,14 @@ class SortSession {
1165
1190
  slot = entries.length;
1166
1191
  else
1167
1192
  otherModelIndex.push(modelIdx);
1168
- const known = previous.get(node);
1193
+ const known = previous.get(el);
1169
1194
  entries.push(known ?? {
1170
- el: node,
1195
+ el: el,
1171
1196
  isSlot,
1172
- base: initialTransform(node),
1173
- savedTransform: node.style.transform,
1174
- savedTransition: node.style.transition,
1175
- savedWillChange: node.style.willChange,
1197
+ base: initialTransform(el),
1198
+ savedTransform: el.style.transform,
1199
+ savedTransition: el.style.transition,
1200
+ savedWillChange: el.style.willChange,
1176
1201
  });
1177
1202
  boxes.push(box);
1178
1203
  }
@@ -1752,12 +1777,15 @@ class DropListRef {
1752
1777
  const els = [];
1753
1778
  const boxes = [];
1754
1779
  for (const node of Array.from(this.el.children)) {
1755
- if (!(node instanceof HTMLElement) || !others.has(node))
1780
+ if (!isElementNode(node))
1781
+ continue;
1782
+ const el = node;
1783
+ if (!others.has(el))
1756
1784
  continue;
1757
- const r = node.getBoundingClientRect();
1785
+ const r = el.getBoundingClientRect();
1758
1786
  if (r.width === 0 && r.height === 0)
1759
1787
  continue;
1760
- els.push(node);
1788
+ els.push(el);
1761
1789
  boxes.push(toBox(r));
1762
1790
  }
1763
1791
  if (!els.length)
@@ -1782,15 +1810,16 @@ class DropListRef {
1782
1810
  byElement.set(d.el, d);
1783
1811
  let index = 0;
1784
1812
  for (const node of Array.from(this.el.children)) {
1785
- if (!(node instanceof HTMLElement))
1813
+ if (!isElementNode(node))
1786
1814
  continue;
1787
- if (node === this.placeholder?.element)
1815
+ const el = node;
1816
+ if (el === this.placeholder?.element)
1788
1817
  continue;
1789
- if (node.classList.contains('ngx-drag-placeholder'))
1818
+ if (el.classList.contains('ngx-drag-placeholder'))
1790
1819
  continue;
1791
- if (node.classList.contains('ngx-drag-preview'))
1820
+ if (el.classList.contains('ngx-drag-preview'))
1792
1821
  continue;
1793
- const ref = byElement.get(node);
1822
+ const ref = byElement.get(el);
1794
1823
  if (!ref)
1795
1824
  continue;
1796
1825
  if (ref === item)
@@ -1887,19 +1916,6 @@ class DragDropService {
1887
1916
  ...(ngDevMode ? [{ debugName: "dropLists" }] : /* istanbul ignore next */ []));
1888
1917
  activeDrag = signal(null, /* @ts-ignore */
1889
1918
  ...(ngDevMode ? [{ debugName: "activeDrag" }] : /* istanbul ignore next */ []));
1890
- /**
1891
- * DOM-position registries, used to find the nearest enclosing list/group by walking real
1892
- * ancestor elements instead of Angular's element-injector tree.
1893
- *
1894
- * This matters for `ngTemplateOutlet`-based recursive trees (a list of items whose own
1895
- * children are rendered by re-invoking the SAME `<ng-template>`, as in a tree/outliner UI):
1896
- * each invocation gets a FRESH embedded view whose injector context is wherever the outlet
1897
- * was written in the template, not wherever its content ends up in the DOM. So `inject(TOKEN,
1898
- * {skipSelf})` from one recursion level can never see a provider from another level — the
1899
- * only fix within Angular's own DI is to manually thread `ngTemplateOutletInjector` through
1900
- * every recursive call, which isn't reasonable to require of every consumer of this library.
1901
- * A plain DOM walk has no such limitation: it only cares where elements actually ended up.
1902
- */
1903
1919
  listsByEl = new Map();
1904
1920
  groupsByEl = new Map();
1905
1921
  registerDragItem(ref) {
@@ -1928,19 +1944,59 @@ class DragDropService {
1928
1944
  }
1929
1945
  /** Nearest registered drop list starting at (and including) `start`, walking up the DOM. */
1930
1946
  findAncestorDropList(start) {
1931
- for (let n = start; n; n = n.parentElement) {
1932
- const found = this.listsByEl.get(n);
1933
- if (found)
1934
- return found;
1947
+ let node = start;
1948
+ while (node) {
1949
+ if (isElementNode(node)) {
1950
+ const found = this.listsByEl.get(node);
1951
+ if (found) {
1952
+ return found;
1953
+ }
1954
+ }
1955
+ if (isShadowRootNode(node) && 'host' in node) {
1956
+ node = node.host;
1957
+ continue;
1958
+ }
1959
+ if (isDocumentNode(node)) {
1960
+ const frame = node.defaultView?.frameElement;
1961
+ if (frame) {
1962
+ node = frame;
1963
+ continue;
1964
+ }
1965
+ break;
1966
+ }
1967
+ node = node.parentNode;
1935
1968
  }
1936
1969
  return null;
1937
1970
  }
1938
1971
  /** Nearest registered drop-list group starting at (and including) `start`, walking up the DOM. */
1939
1972
  findAncestorGroup(start) {
1940
- for (let n = start; n; n = n.parentElement) {
1941
- const found = this.groupsByEl.get(n);
1942
- if (found)
1943
- return found;
1973
+ let node = start;
1974
+ while (node) {
1975
+ // HTMLElement / SVGElement / ...
1976
+ if (isElementNode(node)) {
1977
+ const found = this.groupsByEl.get(node);
1978
+ if (found) {
1979
+ return found;
1980
+ }
1981
+ }
1982
+ // ShadowRoot
1983
+ // ShadowRoot یک DocumentFragment است و در iframe ممکن است
1984
+ if (isShadowRootNode(node) && 'host' in node && node.host) {
1985
+ node = node.host;
1986
+ continue;
1987
+ }
1988
+ // iframe Document
1989
+ // 9 === DOCUMENT_NODE
1990
+ if (node.nodeType === 9) {
1991
+ const frame = node.defaultView?.frameElement;
1992
+ if (frame) {
1993
+ node = frame;
1994
+ continue;
1995
+ }
1996
+ // root document
1997
+ break;
1998
+ }
1999
+ node = node.parentNode;
1944
2000
  }
1945
2001
  return null;
1946
2002
  }
@@ -2049,15 +2105,16 @@ class AutoScroller {
2049
2105
  this.raf = 0;
2050
2106
  return;
2051
2107
  }
2052
- if (this.container instanceof Window) {
2053
- this.container.scrollBy(this.speed.x, this.speed.y);
2108
+ const container = this.container;
2109
+ if (isWindow(container)) {
2110
+ container.scrollBy(this.speed.x, this.speed.y);
2054
2111
  }
2055
2112
  else {
2056
- this.container.scrollLeft += this.speed.x;
2057
- this.container.scrollTop += this.speed.y;
2113
+ container.scrollLeft += this.speed.x;
2114
+ container.scrollTop += this.speed.y;
2058
2115
  }
2059
- // pointer ثابت مونده ولی محتوای زیرش اسکرول شده — پس sort/placeholder را
2060
- // دوباره با آخرین موقعیت شناخته‌شده اجرا کن.
2116
+ // Pointer ثابت مانده ولی محتوا اسکرول شده؛
2117
+ // بنابراین موقعیت placeholder/sort را دوباره محاسبه کن.
2061
2118
  this.onTick?.();
2062
2119
  this.raf = requestAnimationFrame(this.tick);
2063
2120
  };
@@ -2070,7 +2127,7 @@ function edgeSpeed(pos, min, max, edge, maxSpeed) {
2070
2127
  return 0;
2071
2128
  }
2072
2129
  function getScrollRect(container) {
2073
- if (container instanceof Window) {
2130
+ if (isWindow(container)) {
2074
2131
  return { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
2075
2132
  }
2076
2133
  return container.getBoundingClientRect();
@@ -2080,19 +2137,53 @@ function findScrollableAncestor(el) {
2080
2137
  while (node) {
2081
2138
  const style = getComputedStyle(node);
2082
2139
  const scrollable = /(auto|scroll|overlay)/.test(style.overflowY + style.overflowX);
2083
- if (scrollable && node.scrollHeight > node.clientHeight)
2140
+ if (scrollable && node.scrollHeight > node.clientHeight) {
2084
2141
  return node;
2142
+ }
2085
2143
  node = node.parentElement;
2086
2144
  }
2087
- return window;
2145
+ return el.ownerDocument.defaultView ?? window;
2088
2146
  }
2089
2147
  function getScrollPosition(container) {
2090
- if (container instanceof Window) {
2148
+ if (isWindow(container)) {
2091
2149
  return { left: window.scrollX, top: window.scrollY };
2092
2150
  }
2093
2151
  return { left: container.scrollLeft, top: container.scrollTop };
2094
2152
  }
2095
2153
 
2154
+ const NGX_DRAG_PREVIEW = new InjectionToken('ngx_drag_preview');
2155
+ /**
2156
+ * Custom preview content, from `*ngxDragPreview` inside the `ngxDraggable`.
2157
+ *
2158
+ * @example
2159
+ * ```html
2160
+ * <div ngxDropList>
2161
+ * <div ngxDraggable>
2162
+ * <span class="my-preview" *ngxDragPreview></span>
2163
+ * ...
2164
+ * </div>
2165
+ * </div>
2166
+ * ```
2167
+ */
2168
+ class NgxDragPreview {
2169
+ tpl;
2170
+ _ref = new DragPreviewRef();
2171
+ constructor(tpl) {
2172
+ this.tpl = tpl;
2173
+ this._ref.tpl = tpl;
2174
+ this._ref.appRef = inject(ApplicationRef);
2175
+ }
2176
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxDragPreview, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
2177
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.4", type: NgxDragPreview, isStandalone: true, selector: "[ngxDragPreview]", providers: [{ provide: NGX_DRAG_PREVIEW, useExisting: NgxDragPreview }], ngImport: i0 });
2178
+ }
2179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxDragPreview, decorators: [{
2180
+ type: Directive,
2181
+ args: [{
2182
+ selector: '[ngxDragPreview]',
2183
+ providers: [{ provide: NGX_DRAG_PREVIEW, useExisting: NgxDragPreview }],
2184
+ }]
2185
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
2186
+
2096
2187
  /** Pointer events already claimed by an (inner) draggable. */
2097
2188
  const claimedPointerEvents = new WeakSet();
2098
2189
  const NGX_DRAGGABLE = new InjectionToken('ngx-draggable');
@@ -2124,6 +2215,7 @@ class NgxDraggable {
2124
2215
  set data(v) {
2125
2216
  this._ref.data = v;
2126
2217
  }
2218
+ customPreview;
2127
2219
  dragStart = output();
2128
2220
  dragMove = output();
2129
2221
  dragEnd = output();
@@ -2177,6 +2269,11 @@ class NgxDraggable {
2177
2269
  this.removeDown = this.renderer.listen(this.host.nativeElement, 'pointerdown', (e) => this.pointerDown(e));
2178
2270
  this.removeKeydown = this.renderer.listen(this.host.nativeElement, 'keydown', (e) => this.keyDown(e));
2179
2271
  }
2272
+ ngAfterContentInit() {
2273
+ if (this.customPreview) {
2274
+ this._ref.preview = this.customPreview._ref;
2275
+ }
2276
+ }
2180
2277
  ngOnDestroy() {
2181
2278
  this.removeDown?.();
2182
2279
  this.removeMove?.();
@@ -2206,12 +2303,13 @@ class NgxDraggable {
2206
2303
  e.preventDefault();
2207
2304
  this.down = true;
2208
2305
  this.pointerId = e.pointerId;
2306
+ this._ref.el.setPointerCapture(this.pointerId);
2209
2307
  this.start = { x: e.clientX, y: e.clientY };
2210
2308
  this._ref.boundary = this.boundary();
2211
2309
  this._ref.lockAxis = this.lockAxis();
2212
2310
  this._ref.pointerDown(this.start);
2213
- this.removeMove = this.renderer.listen(this.doc, 'pointermove', (ev) => this.pointerMove(ev));
2214
- this.removeUp = this.renderer.listen(this.doc, 'pointerup', (ev) => this.pointerUp(ev));
2311
+ this.removeMove = this.renderer.listen(this._ref.el, 'pointermove', (ev) => this.pointerMove(ev));
2312
+ this.removeUp = this.renderer.listen(this._ref.el, 'pointerup', (ev) => this.pointerUp(ev));
2215
2313
  this.removeEscape = this.renderer.listen(this.doc, 'keydown', (ev) => {
2216
2314
  if (ev.key === 'Escape' && this.dragging())
2217
2315
  this.cancel();
@@ -2289,6 +2387,9 @@ class NgxDraggable {
2289
2387
  pointerUp(e) {
2290
2388
  if (!this.down || e.pointerId !== this.pointerId)
2291
2389
  return;
2390
+ if (this._ref.el.hasPointerCapture(this.pointerId)) {
2391
+ this._ref.el.releasePointerCapture(this.pointerId);
2392
+ }
2292
2393
  if (this.dragging()) {
2293
2394
  // Also when released OUTSIDE every list: endDrag() rolls the drag back and cleans up.
2294
2395
  if (this._ref.isListDrag) {
@@ -2385,14 +2486,16 @@ class NgxDraggable {
2385
2486
  isOnHandle(target) {
2386
2487
  if (!this.dragHandle())
2387
2488
  return true;
2489
+ // TODO notsafe instanseof: node.nodeType === Node.ELEMENT_NODE
2388
2490
  return target instanceof HTMLElement && !!target.closest(this.dragHandle());
2389
2491
  }
2390
2492
  isInteractive(target) {
2493
+ // TODO notsafe instanseof: node.nodeType === Node.ELEMENT_NODE
2391
2494
  return (target instanceof HTMLElement &&
2392
2495
  !!target.closest('button,a,input,textarea,select,[data-no-drag]'));
2393
2496
  }
2394
2497
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxDraggable, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2395
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.4", type: NgxDraggable, isStandalone: true, selector: "[NgxDraggable],[ngxDraggable]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, boundary: { classPropertyName: "boundary", publicName: "boundary", isSignal: true, isRequired: false, transformFunction: null }, dragRootElement: { classPropertyName: "dragRootElement", publicName: "dragRootElement", isSignal: true, isRequired: false, transformFunction: null }, dragHandle: { classPropertyName: "dragHandle", publicName: "dragHandle", isSignal: true, isRequired: false, transformFunction: null }, lockAxis: { classPropertyName: "lockAxis", publicName: "lockAxis", isSignal: true, isRequired: false, transformFunction: null }, dragThreshold: { classPropertyName: "dragThreshold", publicName: "dragThreshold", isSignal: true, isRequired: false, transformFunction: null }, keyboardStep: { classPropertyName: "keyboardStep", publicName: "keyboardStep", isSignal: true, isRequired: false, transformFunction: null }, autoScroll: { classPropertyName: "autoScroll", publicName: "autoScroll", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", dragRootElement: "dragRootElementChange", dragHandle: "dragHandleChange", dragStart: "dragStart", dragMove: "dragMove", dragEnd: "dragEnd" }, host: { properties: { "style.touch-action": "\"none\"", "attr.tabindex": "disabled() ? null : 0", "attr.aria-grabbed": "dragging()", "class.disable-drag": "disabled()" }, classAttribute: "ngx-draggable" }, providers: [{ provide: NGX_DRAGGABLE, useExisting: NgxDraggable }], ngImport: i0 });
2498
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.4", type: NgxDraggable, isStandalone: true, selector: "[NgxDraggable],[ngxDraggable]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, boundary: { classPropertyName: "boundary", publicName: "boundary", isSignal: true, isRequired: false, transformFunction: null }, dragRootElement: { classPropertyName: "dragRootElement", publicName: "dragRootElement", isSignal: true, isRequired: false, transformFunction: null }, dragHandle: { classPropertyName: "dragHandle", publicName: "dragHandle", isSignal: true, isRequired: false, transformFunction: null }, lockAxis: { classPropertyName: "lockAxis", publicName: "lockAxis", isSignal: true, isRequired: false, transformFunction: null }, dragThreshold: { classPropertyName: "dragThreshold", publicName: "dragThreshold", isSignal: true, isRequired: false, transformFunction: null }, keyboardStep: { classPropertyName: "keyboardStep", publicName: "keyboardStep", isSignal: true, isRequired: false, transformFunction: null }, autoScroll: { classPropertyName: "autoScroll", publicName: "autoScroll", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", dragRootElement: "dragRootElementChange", dragHandle: "dragHandleChange", dragStart: "dragStart", dragMove: "dragMove", dragEnd: "dragEnd" }, host: { properties: { "style.touch-action": "\"none\"", "attr.tabindex": "disabled() ? null : 0", "attr.aria-grabbed": "dragging()", "class.disable-drag": "disabled()" }, classAttribute: "ngx-draggable" }, providers: [{ provide: NGX_DRAGGABLE, useExisting: NgxDraggable }], queries: [{ propertyName: "customPreview", first: true, predicate: NgxDragPreview, descendants: true }], ngImport: i0 });
2396
2499
  }
2397
2500
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxDraggable, decorators: [{
2398
2501
  type: Directive,
@@ -2410,6 +2513,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
2410
2513
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], boundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundary", required: false }] }], dragRootElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragRootElement", required: false }] }, { type: i0.Output, args: ["dragRootElementChange"] }], dragHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragHandle", required: false }] }, { type: i0.Output, args: ["dragHandleChange"] }], lockAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockAxis", required: false }] }], dragThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragThreshold", required: false }] }], keyboardStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboardStep", required: false }] }], autoScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScroll", required: false }] }], data: [{
2411
2514
  type: Input,
2412
2515
  args: ['data']
2516
+ }], customPreview: [{
2517
+ type: ContentChild,
2518
+ args: [NgxDragPreview]
2413
2519
  }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragMove: [{ type: i0.Output, args: ["dragMove"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }] } });
2414
2520
 
2415
2521
  const ALL_DIRECTIONS = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'];
@@ -2716,6 +2822,7 @@ class NgxResizable {
2716
2822
  /**
2717
2823
  * Explicit resize handle has priority.
2718
2824
  */
2825
+ // TODO notsafe instanceof:node.nodeType === Node.ELEMENT_NODE
2719
2826
  const handle = e.target instanceof HTMLElement
2720
2827
  ? e.target.closest('[data-ngx-resize-handle]')
2721
2828
  : null;
@@ -2788,6 +2895,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
2788
2895
  }] } });
2789
2896
 
2790
2897
  const NGX_PLACEHOLDER = new InjectionToken('ngx-place-holder');
2898
+ /**
2899
+ * Custom placeholder content, from `*ngxPlaceholder` inside the `ngxDropList`.
2900
+ *
2901
+ * @example
2902
+ * ```html
2903
+ * <div ngxDropList>
2904
+ * <span class="my-placeholder" *ngxPlaceholder></span>
2905
+ * ...
2906
+ * </div>
2907
+ * ```
2908
+ */
2791
2909
  class NgxPlaceholder {
2792
2910
  tpl;
2793
2911
  _ref = new PlaceHolderRef();
@@ -2833,11 +2951,6 @@ class NgxDropList {
2833
2951
  }
2834
2952
  ngOnInit() {
2835
2953
  this._ref.el = this.el.nativeElement;
2836
- // Found by walking the real DOM (not Angular's element-injector tree): a list rendered by a
2837
- // recursively-invoked `ngTemplateOutlet` (a tree/outliner UI, where every level re-uses the
2838
- // SAME `<ng-template>`) gets a fresh injector context per invocation, so `inject(TOKEN,
2839
- // {skipSelf})` can never see a provider from another recursion level — the DOM position is
2840
- // what actually determines nesting/grouping for this library's purposes anyway.
2841
2954
  const parentEl = this.el.nativeElement.parentElement;
2842
2955
  const group = this.service.findAncestorGroup(parentEl);
2843
2956
  const parent = this.service.findAncestorDropList(parentEl);
@@ -2952,5 +3065,5 @@ function clamp(v, max) {
2952
3065
  * Generated bundle index. Do not edit.
2953
3066
  */
2954
3067
 
2955
- export { AutoScroller, DragDropService, DragRef, DropListGroupRef, DropListRef, NGX_DRAGGABLE, NGX_DROPLIST, NGX_DROPLIST_GROUP, NGX_PLACEHOLDER, NgxDraggable, NgxDropList, NgxDropListGroup, NgxPlaceholder, NgxResizable, PlaceHolderRef, adjustForZoom, checkBoundX, checkBoundY, clampResizeWithinBoundary, copyArrayItem, findScrollableAncestor, getPageZoom, getScrollPosition, isRtl, moveItemInArray, resolveLogicalDirection, transferArrayItem };
3068
+ export { AutoScroller, DragDropService, DragRef, DropListGroupRef, DropListRef, NGX_DRAGGABLE, NGX_DRAG_PREVIEW, NGX_DROPLIST, NGX_DROPLIST_GROUP, NGX_PLACEHOLDER, NgxDragPreview, NgxDraggable, NgxDropList, NgxDropListGroup, NgxPlaceholder, NgxResizable, PlaceHolderRef, adjustForZoom, checkBoundX, checkBoundY, clampResizeWithinBoundary, copyArrayItem, findScrollableAncestor, getPageZoom, getScrollPosition, isRtl, moveItemInArray, resolveLogicalDirection, transferArrayItem };
2956
3069
  //# sourceMappingURL=ngx-kit-drag-resize.mjs.map