ngx-kit 8.0.2 → 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
+ });
103
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
+ });
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))
1756
1781
  continue;
1757
- const r = node.getBoundingClientRect();
1782
+ const el = node;
1783
+ if (!others.has(el))
1784
+ continue;
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) {
@@ -1930,13 +1946,25 @@ class DragDropService {
1930
1946
  findAncestorDropList(start) {
1931
1947
  let node = start;
1932
1948
  while (node) {
1933
- if (node instanceof HTMLElement) {
1949
+ if (isElementNode(node)) {
1934
1950
  const found = this.listsByEl.get(node);
1935
1951
  if (found) {
1936
1952
  return found;
1937
1953
  }
1938
1954
  }
1939
- node = node instanceof ShadowRoot ? node.host : node.parentNode;
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;
1940
1968
  }
1941
1969
  return null;
1942
1970
  }
@@ -1944,13 +1972,31 @@ class DragDropService {
1944
1972
  findAncestorGroup(start) {
1945
1973
  let node = start;
1946
1974
  while (node) {
1947
- if (node instanceof HTMLElement) {
1975
+ // HTMLElement / SVGElement / ...
1976
+ if (isElementNode(node)) {
1948
1977
  const found = this.groupsByEl.get(node);
1949
1978
  if (found) {
1950
1979
  return found;
1951
1980
  }
1952
1981
  }
1953
- node = node instanceof ShadowRoot ? node.host : node.parentNode;
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;
1954
2000
  }
1955
2001
  return null;
1956
2002
  }
@@ -2059,15 +2105,16 @@ class AutoScroller {
2059
2105
  this.raf = 0;
2060
2106
  return;
2061
2107
  }
2062
- if (this.container instanceof Window) {
2063
- 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);
2064
2111
  }
2065
2112
  else {
2066
- this.container.scrollLeft += this.speed.x;
2067
- this.container.scrollTop += this.speed.y;
2113
+ container.scrollLeft += this.speed.x;
2114
+ container.scrollTop += this.speed.y;
2068
2115
  }
2069
- // pointer ثابت مونده ولی محتوای زیرش اسکرول شده — پس sort/placeholder را
2070
- // دوباره با آخرین موقعیت شناخته‌شده اجرا کن.
2116
+ // Pointer ثابت مانده ولی محتوا اسکرول شده؛
2117
+ // بنابراین موقعیت placeholder/sort را دوباره محاسبه کن.
2071
2118
  this.onTick?.();
2072
2119
  this.raf = requestAnimationFrame(this.tick);
2073
2120
  };
@@ -2080,7 +2127,7 @@ function edgeSpeed(pos, min, max, edge, maxSpeed) {
2080
2127
  return 0;
2081
2128
  }
2082
2129
  function getScrollRect(container) {
2083
- if (container instanceof Window) {
2130
+ if (isWindow(container)) {
2084
2131
  return { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
2085
2132
  }
2086
2133
  return container.getBoundingClientRect();
@@ -2090,19 +2137,53 @@ function findScrollableAncestor(el) {
2090
2137
  while (node) {
2091
2138
  const style = getComputedStyle(node);
2092
2139
  const scrollable = /(auto|scroll|overlay)/.test(style.overflowY + style.overflowX);
2093
- if (scrollable && node.scrollHeight > node.clientHeight)
2140
+ if (scrollable && node.scrollHeight > node.clientHeight) {
2094
2141
  return node;
2142
+ }
2095
2143
  node = node.parentElement;
2096
2144
  }
2097
- return window;
2145
+ return el.ownerDocument.defaultView ?? window;
2098
2146
  }
2099
2147
  function getScrollPosition(container) {
2100
- if (container instanceof Window) {
2148
+ if (isWindow(container)) {
2101
2149
  return { left: window.scrollX, top: window.scrollY };
2102
2150
  }
2103
2151
  return { left: container.scrollLeft, top: container.scrollTop };
2104
2152
  }
2105
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
+
2106
2187
  /** Pointer events already claimed by an (inner) draggable. */
2107
2188
  const claimedPointerEvents = new WeakSet();
2108
2189
  const NGX_DRAGGABLE = new InjectionToken('ngx-draggable');
@@ -2134,6 +2215,7 @@ class NgxDraggable {
2134
2215
  set data(v) {
2135
2216
  this._ref.data = v;
2136
2217
  }
2218
+ customPreview;
2137
2219
  dragStart = output();
2138
2220
  dragMove = output();
2139
2221
  dragEnd = output();
@@ -2187,6 +2269,11 @@ class NgxDraggable {
2187
2269
  this.removeDown = this.renderer.listen(this.host.nativeElement, 'pointerdown', (e) => this.pointerDown(e));
2188
2270
  this.removeKeydown = this.renderer.listen(this.host.nativeElement, 'keydown', (e) => this.keyDown(e));
2189
2271
  }
2272
+ ngAfterContentInit() {
2273
+ if (this.customPreview) {
2274
+ this._ref.preview = this.customPreview._ref;
2275
+ }
2276
+ }
2190
2277
  ngOnDestroy() {
2191
2278
  this.removeDown?.();
2192
2279
  this.removeMove?.();
@@ -2216,12 +2303,13 @@ class NgxDraggable {
2216
2303
  e.preventDefault();
2217
2304
  this.down = true;
2218
2305
  this.pointerId = e.pointerId;
2306
+ this._ref.el.setPointerCapture(this.pointerId);
2219
2307
  this.start = { x: e.clientX, y: e.clientY };
2220
2308
  this._ref.boundary = this.boundary();
2221
2309
  this._ref.lockAxis = this.lockAxis();
2222
2310
  this._ref.pointerDown(this.start);
2223
- this.removeMove = this.renderer.listen(this.doc, 'pointermove', (ev) => this.pointerMove(ev));
2224
- 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));
2225
2313
  this.removeEscape = this.renderer.listen(this.doc, 'keydown', (ev) => {
2226
2314
  if (ev.key === 'Escape' && this.dragging())
2227
2315
  this.cancel();
@@ -2299,6 +2387,9 @@ class NgxDraggable {
2299
2387
  pointerUp(e) {
2300
2388
  if (!this.down || e.pointerId !== this.pointerId)
2301
2389
  return;
2390
+ if (this._ref.el.hasPointerCapture(this.pointerId)) {
2391
+ this._ref.el.releasePointerCapture(this.pointerId);
2392
+ }
2302
2393
  if (this.dragging()) {
2303
2394
  // Also when released OUTSIDE every list: endDrag() rolls the drag back and cleans up.
2304
2395
  if (this._ref.isListDrag) {
@@ -2395,14 +2486,16 @@ class NgxDraggable {
2395
2486
  isOnHandle(target) {
2396
2487
  if (!this.dragHandle())
2397
2488
  return true;
2489
+ // TODO notsafe instanseof: node.nodeType === Node.ELEMENT_NODE
2398
2490
  return target instanceof HTMLElement && !!target.closest(this.dragHandle());
2399
2491
  }
2400
2492
  isInteractive(target) {
2493
+ // TODO notsafe instanseof: node.nodeType === Node.ELEMENT_NODE
2401
2494
  return (target instanceof HTMLElement &&
2402
2495
  !!target.closest('button,a,input,textarea,select,[data-no-drag]'));
2403
2496
  }
2404
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 });
2405
- 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 });
2406
2499
  }
2407
2500
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxDraggable, decorators: [{
2408
2501
  type: Directive,
@@ -2420,6 +2513,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
2420
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: [{
2421
2514
  type: Input,
2422
2515
  args: ['data']
2516
+ }], customPreview: [{
2517
+ type: ContentChild,
2518
+ args: [NgxDragPreview]
2423
2519
  }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragMove: [{ type: i0.Output, args: ["dragMove"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }] } });
2424
2520
 
2425
2521
  const ALL_DIRECTIONS = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'];
@@ -2726,6 +2822,7 @@ class NgxResizable {
2726
2822
  /**
2727
2823
  * Explicit resize handle has priority.
2728
2824
  */
2825
+ // TODO notsafe instanceof:node.nodeType === Node.ELEMENT_NODE
2729
2826
  const handle = e.target instanceof HTMLElement
2730
2827
  ? e.target.closest('[data-ngx-resize-handle]')
2731
2828
  : null;
@@ -2798,6 +2895,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
2798
2895
  }] } });
2799
2896
 
2800
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
+ */
2801
2909
  class NgxPlaceholder {
2802
2910
  tpl;
2803
2911
  _ref = new PlaceHolderRef();
@@ -2843,11 +2951,6 @@ class NgxDropList {
2843
2951
  }
2844
2952
  ngOnInit() {
2845
2953
  this._ref.el = this.el.nativeElement;
2846
- // Found by walking the real DOM (not Angular's element-injector tree): a list rendered by a
2847
- // recursively-invoked `ngTemplateOutlet` (a tree/outliner UI, where every level re-uses the
2848
- // SAME `<ng-template>`) gets a fresh injector context per invocation, so `inject(TOKEN,
2849
- // {skipSelf})` can never see a provider from another recursion level — the DOM position is
2850
- // what actually determines nesting/grouping for this library's purposes anyway.
2851
2954
  const parentEl = this.el.nativeElement.parentElement;
2852
2955
  const group = this.service.findAncestorGroup(parentEl);
2853
2956
  const parent = this.service.findAncestorDropList(parentEl);
@@ -2962,5 +3065,5 @@ function clamp(v, max) {
2962
3065
  * Generated bundle index. Do not edit.
2963
3066
  */
2964
3067
 
2965
- 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 };
2966
3069
  //# sourceMappingURL=ngx-kit-drag-resize.mjs.map