forty-cdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/accordion/README.md +122 -0
  2. package/aspect-ratio/README.md +76 -0
  3. package/avatar/README.md +100 -0
  4. package/breadcrumbs/README.md +49 -0
  5. package/breakpoints/README.md +81 -0
  6. package/button/README.md +49 -0
  7. package/calendar/README.md +458 -0
  8. package/carousel/README.md +358 -0
  9. package/checkbox/README.md +146 -0
  10. package/combobox/README.md +535 -0
  11. package/context-menu/README.md +139 -0
  12. package/date-field/README.md +184 -0
  13. package/date-picker/README.md +338 -0
  14. package/dialog/README.md +388 -0
  15. package/disclosure/README.md +114 -0
  16. package/drag-drop/README.md +359 -0
  17. package/drawer/README.md +560 -0
  18. package/dropdown-menu/README.md +176 -0
  19. package/fesm2022/forty-cdk-accordion.mjs +348 -0
  20. package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
  21. package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
  22. package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
  23. package/fesm2022/forty-cdk-avatar.mjs +308 -0
  24. package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
  25. package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
  26. package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
  27. package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
  28. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
  29. package/fesm2022/forty-cdk-button.mjs +134 -0
  30. package/fesm2022/forty-cdk-button.mjs.map +1 -0
  31. package/fesm2022/forty-cdk-calendar.mjs +2034 -0
  32. package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
  33. package/fesm2022/forty-cdk-carousel.mjs +968 -0
  34. package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
  35. package/fesm2022/forty-cdk-checkbox.mjs +226 -0
  36. package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
  37. package/fesm2022/forty-cdk-combobox.mjs +2596 -0
  38. package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
  39. package/fesm2022/forty-cdk-context-menu.mjs +413 -0
  40. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
  41. package/fesm2022/forty-cdk-core.mjs +9022 -0
  42. package/fesm2022/forty-cdk-core.mjs.map +1 -0
  43. package/fesm2022/forty-cdk-date-field.mjs +744 -0
  44. package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
  45. package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
  46. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
  47. package/fesm2022/forty-cdk-dialog.mjs +707 -0
  48. package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
  49. package/fesm2022/forty-cdk-disclosure.mjs +190 -0
  50. package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
  51. package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
  52. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
  53. package/fesm2022/forty-cdk-drawer.mjs +1641 -0
  54. package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
  55. package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
  56. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
  57. package/fesm2022/forty-cdk-field.mjs +425 -0
  58. package/fesm2022/forty-cdk-field.mjs.map +1 -0
  59. package/fesm2022/forty-cdk-fieldset.mjs +164 -0
  60. package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
  61. package/fesm2022/forty-cdk-file-upload.mjs +221 -0
  62. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
  63. package/fesm2022/forty-cdk-hover-card.mjs +496 -0
  64. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
  65. package/fesm2022/forty-cdk-input.mjs +274 -0
  66. package/fesm2022/forty-cdk-input.mjs.map +1 -0
  67. package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
  68. package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
  69. package/fesm2022/forty-cdk-listbox.mjs +1279 -0
  70. package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
  71. package/fesm2022/forty-cdk-menu.mjs +1439 -0
  72. package/fesm2022/forty-cdk-menu.mjs.map +1 -0
  73. package/fesm2022/forty-cdk-menubar.mjs +787 -0
  74. package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
  75. package/fesm2022/forty-cdk-meter.mjs +211 -0
  76. package/fesm2022/forty-cdk-meter.mjs.map +1 -0
  77. package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
  78. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
  79. package/fesm2022/forty-cdk-number-input.mjs +559 -0
  80. package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
  81. package/fesm2022/forty-cdk-otp-input.mjs +527 -0
  82. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
  83. package/fesm2022/forty-cdk-pagination.mjs +323 -0
  84. package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
  85. package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
  86. package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
  87. package/fesm2022/forty-cdk-popover.mjs +698 -0
  88. package/fesm2022/forty-cdk-popover.mjs.map +1 -0
  89. package/fesm2022/forty-cdk-progress.mjs +226 -0
  90. package/fesm2022/forty-cdk-progress.mjs.map +1 -0
  91. package/fesm2022/forty-cdk-radio-group.mjs +378 -0
  92. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
  93. package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
  94. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
  95. package/fesm2022/forty-cdk-search.mjs +205 -0
  96. package/fesm2022/forty-cdk-search.mjs.map +1 -0
  97. package/fesm2022/forty-cdk-select.mjs +1661 -0
  98. package/fesm2022/forty-cdk-select.mjs.map +1 -0
  99. package/fesm2022/forty-cdk-separator.mjs +82 -0
  100. package/fesm2022/forty-cdk-separator.mjs.map +1 -0
  101. package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
  102. package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
  103. package/fesm2022/forty-cdk-slider.mjs +803 -0
  104. package/fesm2022/forty-cdk-slider.mjs.map +1 -0
  105. package/fesm2022/forty-cdk-stepper.mjs +886 -0
  106. package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
  107. package/fesm2022/forty-cdk-switch.mjs +137 -0
  108. package/fesm2022/forty-cdk-switch.mjs.map +1 -0
  109. package/fesm2022/forty-cdk-table.mjs +1518 -0
  110. package/fesm2022/forty-cdk-table.mjs.map +1 -0
  111. package/fesm2022/forty-cdk-tabs.mjs +400 -0
  112. package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
  113. package/fesm2022/forty-cdk-time-field.mjs +593 -0
  114. package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
  115. package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
  116. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
  117. package/fesm2022/forty-cdk-toast.mjs +1153 -0
  118. package/fesm2022/forty-cdk-toast.mjs.map +1 -0
  119. package/fesm2022/forty-cdk-toggle.mjs +516 -0
  120. package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
  121. package/fesm2022/forty-cdk-toolbar.mjs +374 -0
  122. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
  123. package/fesm2022/forty-cdk-tooltip.mjs +672 -0
  124. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
  125. package/fesm2022/forty-cdk-tree.mjs +2007 -0
  126. package/fesm2022/forty-cdk-tree.mjs.map +1 -0
  127. package/fesm2022/forty-cdk-virtualization.mjs +1 -1
  128. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  129. package/fesm2022/forty-cdk.mjs +0 -43310
  130. package/fesm2022/forty-cdk.mjs.map +1 -1
  131. package/field/README.md +97 -0
  132. package/fieldset/README.md +86 -0
  133. package/file-upload/README.md +73 -0
  134. package/hover-card/README.md +171 -0
  135. package/input/README.md +156 -0
  136. package/listbox/README.md +424 -0
  137. package/menu/README.md +181 -0
  138. package/menubar/README.md +140 -0
  139. package/meter/README.md +128 -0
  140. package/navigation-menu/README.md +253 -0
  141. package/number-input/README.md +171 -0
  142. package/otp-input/README.md +198 -0
  143. package/package.json +213 -1
  144. package/pagination/README.md +61 -0
  145. package/pane-resizer/README.md +136 -0
  146. package/popover/README.md +262 -0
  147. package/progress/README.md +115 -0
  148. package/radio-group/README.md +129 -0
  149. package/scroll-area/README.md +184 -0
  150. package/search/README.md +42 -0
  151. package/select/README.md +488 -0
  152. package/separator/README.md +84 -0
  153. package/signal-forms/README.md +72 -0
  154. package/slider/README.md +152 -0
  155. package/stepper/README.md +292 -0
  156. package/switch/README.md +116 -0
  157. package/table/README.md +769 -0
  158. package/tabs/README.md +130 -0
  159. package/time-field/README.md +157 -0
  160. package/time-picker/README.md +172 -0
  161. package/toast/README.md +398 -0
  162. package/toggle/README.md +224 -0
  163. package/toolbar/README.md +109 -0
  164. package/tooltip/README.md +274 -0
  165. package/tree/README.md +708 -0
  166. package/types/forty-cdk-accordion.d.ts +242 -0
  167. package/types/forty-cdk-aspect-ratio.d.ts +59 -0
  168. package/types/forty-cdk-avatar.d.ts +133 -0
  169. package/types/forty-cdk-breadcrumbs.d.ts +92 -0
  170. package/types/forty-cdk-breakpoints.d.ts +141 -0
  171. package/types/forty-cdk-button.d.ts +80 -0
  172. package/types/forty-cdk-calendar.d.ts +914 -0
  173. package/types/forty-cdk-carousel.d.ts +530 -0
  174. package/types/forty-cdk-checkbox.d.ts +141 -0
  175. package/types/forty-cdk-combobox.d.ts +1259 -0
  176. package/types/forty-cdk-context-menu.d.ts +313 -0
  177. package/types/forty-cdk-core.d.ts +5774 -0
  178. package/types/forty-cdk-date-field.d.ts +307 -0
  179. package/types/forty-cdk-date-picker.d.ts +622 -0
  180. package/types/forty-cdk-dialog.d.ts +546 -0
  181. package/types/forty-cdk-disclosure.d.ts +127 -0
  182. package/types/forty-cdk-drag-drop.d.ts +456 -0
  183. package/types/forty-cdk-drawer.d.ts +871 -0
  184. package/types/forty-cdk-dropdown-menu.d.ts +242 -0
  185. package/types/forty-cdk-field.d.ts +236 -0
  186. package/types/forty-cdk-fieldset.d.ts +119 -0
  187. package/types/forty-cdk-file-upload.d.ts +124 -0
  188. package/types/forty-cdk-hover-card.d.ts +320 -0
  189. package/types/forty-cdk-input.d.ts +169 -0
  190. package/types/forty-cdk-internationalized-date.d.ts +1 -1
  191. package/types/forty-cdk-listbox.d.ts +513 -0
  192. package/types/forty-cdk-menu.d.ts +629 -0
  193. package/types/forty-cdk-menubar.d.ts +451 -0
  194. package/types/forty-cdk-meter.d.ts +122 -0
  195. package/types/forty-cdk-navigation-menu.d.ts +514 -0
  196. package/types/forty-cdk-number-input.d.ts +319 -0
  197. package/types/forty-cdk-otp-input.d.ts +248 -0
  198. package/types/forty-cdk-pagination.d.ts +214 -0
  199. package/types/forty-cdk-pane-resizer.d.ts +145 -0
  200. package/types/forty-cdk-popover.d.ts +509 -0
  201. package/types/forty-cdk-progress.d.ts +143 -0
  202. package/types/forty-cdk-radio-group.d.ts +222 -0
  203. package/types/forty-cdk-scroll-area.d.ts +258 -0
  204. package/types/forty-cdk-search.d.ts +142 -0
  205. package/types/forty-cdk-select.d.ts +899 -0
  206. package/types/forty-cdk-separator.d.ts +59 -0
  207. package/types/forty-cdk-signal-forms.d.ts +58 -0
  208. package/types/forty-cdk-slider.d.ts +379 -0
  209. package/types/forty-cdk-stepper.d.ts +650 -0
  210. package/types/forty-cdk-switch.d.ts +87 -0
  211. package/types/forty-cdk-table.d.ts +723 -0
  212. package/types/forty-cdk-tabs.d.ts +235 -0
  213. package/types/forty-cdk-time-field.d.ts +307 -0
  214. package/types/forty-cdk-time-picker.d.ts +578 -0
  215. package/types/forty-cdk-toast.d.ts +598 -0
  216. package/types/forty-cdk-toggle.d.ts +310 -0
  217. package/types/forty-cdk-toolbar.d.ts +217 -0
  218. package/types/forty-cdk-tooltip.d.ts +436 -0
  219. package/types/forty-cdk-tree.d.ts +688 -0
  220. package/types/forty-cdk.d.ts +1 -19743
@@ -0,0 +1,1180 @@
1
+ import * as i0 from '@angular/core';
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
+ 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';
5
+
6
+ const FOR_DRAG_DROP_CONTEXT = new InjectionToken('FOR_DRAG_DROP_CONTEXT');
7
+ function injectDropListContext(piece) {
8
+ const ctx = inject(FOR_DRAG_DROP_CONTEXT, { optional: true });
9
+ if (!ctx) {
10
+ throw new Error(`[forty-cdk/drag-drop] ${piece} must be used inside a [forDropList] element.`);
11
+ }
12
+ return ctx;
13
+ }
14
+ const FOR_DRAGGABLE_CONTEXT = new InjectionToken('FOR_DRAGGABLE_CONTEXT');
15
+ function injectDraggableContext(piece) {
16
+ const ctx = inject(FOR_DRAGGABLE_CONTEXT, { optional: true });
17
+ if (!ctx) {
18
+ throw new Error(`[forty-cdk/drag-drop] ${piece} must be used inside a [forDraggable] or [forFreeDrag] element.`);
19
+ }
20
+ return ctx;
21
+ }
22
+
23
+ /**
24
+ * Library fallback for drag-drop defaults, read at the root injector when no
25
+ * consumer has called `provideForDragDropDefaults`. Exported for the shared
26
+ * defaults contract spec; not re-exported from the primitive's public entry.
27
+ */
28
+ const FOR_DRAG_DROP_FALLBACK_DEFAULTS = {
29
+ itemRoleDescription: 'sortable',
30
+ announceLift: (label, index, total) => `${label}, lifted. ${index} of ${total}.`,
31
+ announceMove: (label, index, total) => `${label}, moved to position ${index} of ${total}.`,
32
+ announceDrop: (label, index, total) => `${label}, dropped at position ${index} of ${total}.`,
33
+ announceCancel: (label) => `${label}, movement cancelled.`,
34
+ autoScrollEdgeSize: 50,
35
+ autoScrollMaxSpeed: 16,
36
+ };
37
+ const { token, provideDefaults } = createDefaults('FOR_DRAG_DROP_DEFAULTS', FOR_DRAG_DROP_FALLBACK_DEFAULTS);
38
+ /** Token holding the resolved drag-drop defaults for the current scope. */
39
+ const FOR_DRAG_DROP_DEFAULTS = token;
40
+ /**
41
+ * Configures forty-cdk drag-drop defaults for this injector scope. Partial
42
+ * overrides inherit unspecified keys from the parent scope (or library
43
+ * defaults at the root).
44
+ */
45
+ function provideForDragDropDefaults(defaults = {}) {
46
+ return provideDefaults(defaults);
47
+ }
48
+
49
+ const FOR_DROP_LIST_GROUP = new InjectionToken('FOR_DROP_LIST_GROUP');
50
+ /**
51
+ * Groups sibling `[forDropList]` elements so items can be transferred between
52
+ * them without hand-wiring `[connectedTo]` on each list. Every list nested
53
+ * under `[forDropListGroup]` automatically becomes a connected transfer target
54
+ * for every other member of the group.
55
+ */
56
+ class ForDropListGroup {
57
+ #members = signal([], /* @ts-ignore */
58
+ ...(ngDevMode ? [{ debugName: "#members" }] : /* istanbul ignore next */ []));
59
+ /** All registered drop lists in this group, in registration order. */
60
+ members = this.#members.asReadonly();
61
+ /** @internal */
62
+ register(ctx) {
63
+ const current = this.#members();
64
+ if (current.includes(ctx)) {
65
+ return;
66
+ }
67
+ this.#members.set([...current, ctx]);
68
+ }
69
+ /** @internal */
70
+ unregister(ctx) {
71
+ this.#members.set(this.#members().filter((c) => c !== ctx));
72
+ }
73
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDropListGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
74
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDropListGroup, isStandalone: true, selector: "[forDropListGroup]", providers: [{ provide: FOR_DROP_LIST_GROUP, useExisting: ForDropListGroup }], exportAs: ["forDropListGroup"], ngImport: i0 });
75
+ }
76
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDropListGroup, decorators: [{
77
+ type: Directive,
78
+ args: [{
79
+ selector: '[forDropListGroup]',
80
+ exportAs: 'forDropListGroup',
81
+ providers: [{ provide: FOR_DROP_LIST_GROUP, useExisting: ForDropListGroup }],
82
+ }]
83
+ }] });
84
+
85
+ /**
86
+ * Lazy `liveSort` strategy: relocates the rendered placeholder's root nodes to the live resolved
87
+ * drop index during a pointer drag — within the source list and across connected lists — so
88
+ * siblings part to reveal where the item will land.
89
+ *
90
+ * Created at `pointerLift` only when `liveSort` is on (and on a browser platform); when it is off
91
+ * the list creates no sorter and `#resolveDrop` skips placeholder movement entirely, replacing the
92
+ * former inline `if (this.liveSort())` gate. A `dragDisabled` sibling in the source list acts as a
93
+ * hard visual fence: the placeholder stops at the first pinned item instead of travelling past it.
94
+ *
95
+ * Constructed directly (`new PlaceholderSorter(options)`); it holds no injection context.
96
+ */
97
+ class PlaceholderSorter {
98
+ #source;
99
+ #lifted;
100
+ #doc;
101
+ #sourceItems;
102
+ #originIndex;
103
+ #nodes = null;
104
+ constructor(options) {
105
+ this.#source = options.source;
106
+ this.#lifted = options.lifted;
107
+ this.#doc = options.doc;
108
+ this.#sourceItems = options.sourceItems;
109
+ this.#originIndex = options.originIndex;
110
+ }
111
+ /** Store (or clear) the placeholder's root nodes supplied by the draggable. */
112
+ setNodes(nodes) {
113
+ this.#nodes = nodes;
114
+ }
115
+ /** Move the placeholder to the fenced resolved drop index in `targetCtx`. */
116
+ onTargetChange(targetCtx, index) {
117
+ this.#move(targetCtx, this.#fencedIndex(targetCtx, index));
118
+ }
119
+ #fencedIndex(targetCtx, index) {
120
+ if (targetCtx !== this.#source) {
121
+ return index;
122
+ }
123
+ const disabled = this.#sourceItems()
124
+ .filter((h) => h.host !== this.#lifted)
125
+ .map((h) => h.disabled());
126
+ const origin = this.#originIndex();
127
+ return fencePlaceholderIndex(index, disabled, origin < 0 ? 0 : origin);
128
+ }
129
+ #move(targetCtx, index) {
130
+ const nodes = this.#nodes;
131
+ if (nodes === null || nodes.length === 0) {
132
+ return;
133
+ }
134
+ const fragment = this.#doc.createDocumentFragment();
135
+ for (const node of nodes) {
136
+ fragment.appendChild(node);
137
+ }
138
+ const hosts = targetCtx
139
+ .items()
140
+ .map((h) => h.host)
141
+ .filter((h) => h !== this.#lifted);
142
+ const { parent, ref } = placeholderInsertion(hosts, index, targetCtx.host);
143
+ parent.insertBefore(fragment, ref);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Lazy `animateReorder` strategy: runs the FLIP reflow of displaced sibling items and settles the
149
+ * floating preview into the final slot on a committed drop.
150
+ *
151
+ * Created in `drop()` only when the animation gate is satisfied (`animateReorder` on, a browser
152
+ * platform, motion not reduced, and the item actually moved); when it is not, the list creates no
153
+ * animator and tears down instantly, replacing the former inline `animate && first` gate.
154
+ *
155
+ * {@link captureFirst} measures every item's pre-change position and must run **before** the
156
+ * `dragDrop` event mutates the DOM; {@link schedule} runs the Invert + Play after the consumer's
157
+ * data change has rendered. Constructed directly (`new ReorderAnimator(options)`); it holds no
158
+ * injection context beyond the injector handed in for `afterNextRender`.
159
+ */
160
+ class ReorderAnimator {
161
+ #containers;
162
+ #injector;
163
+ #win;
164
+ #first = null;
165
+ constructor(options) {
166
+ this.#containers = options.containers;
167
+ this.#injector = options.injector;
168
+ this.#win = options.win;
169
+ }
170
+ /** Capture each item's First (pre-change) rect across all containers. Run before the data change. */
171
+ captureFirst() {
172
+ const map = new Map();
173
+ for (const ctx of this.#containers) {
174
+ for (const h of ctx.items()) {
175
+ const r = h.host.getBoundingClientRect();
176
+ map.set(h.host, { left: r.left, top: r.top });
177
+ }
178
+ }
179
+ this.#first = map;
180
+ }
181
+ /**
182
+ * Schedule the FLIP play (and preview settle, when a preview was kept) after the next render —
183
+ * once the consumer's data change has reflowed the displaced siblings into their new positions.
184
+ */
185
+ schedule(liftedHost, preview) {
186
+ const first = this.#first;
187
+ if (first === null) {
188
+ return;
189
+ }
190
+ const win = this.#win;
191
+ afterNextRender(() => {
192
+ playFlip({ first, win, exclude: preview ? liftedHost : null });
193
+ if (preview) {
194
+ if (liftedHost.isConnected && win) {
195
+ const r = liftedHost.getBoundingClientRect();
196
+ preview.settle(r.left, r.top, win);
197
+ }
198
+ else {
199
+ preview.destroy();
200
+ }
201
+ }
202
+ }, { injector: this.#injector });
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Optional DI seam overriding the fallback `orientation` of every `[forDropList]` in scope.
208
+ * `ForDropList`'s `orientation` input falls back to this token's value (`'vertical'` when the
209
+ * token is not provided); a consumer `orientation` binding always wins over it. Wrappers
210
+ * compose a horizontal list without leaking `orientation="horizontal"` boilerplate into the
211
+ * consumer's template — `ForTableColumnReorder` provides `'horizontal'` so column reordering
212
+ * resolves along the row axis out of the box.
213
+ */
214
+ const FOR_DROP_LIST_DEFAULT_ORIENTATION = new InjectionToken('FOR_DROP_LIST_DEFAULT_ORIENTATION');
215
+ /**
216
+ * Root directive of the drag-drop primitive. Apply on any container element to
217
+ * create a reorderable list. Items inside are declared with `[forDraggable]`.
218
+ *
219
+ * Supports both keyboard dragging and pointer (mouse / touch / pen) dragging.
220
+ */
221
+ class ForDropList {
222
+ #hostRef = inject(ElementRef);
223
+ #defaults = inject(FOR_DRAG_DROP_DEFAULTS);
224
+ #announcer = inject(LiveAnnouncer);
225
+ #group = inject(FOR_DROP_LIST_GROUP, { optional: true });
226
+ #document = inject(DOCUMENT);
227
+ #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
228
+ #destroyRef = inject(DestroyRef);
229
+ #injector = inject(Injector);
230
+ #prefersReducedMotion = injectPrefersReducedMotion();
231
+ #defaultOrientation = inject(FOR_DROP_LIST_DEFAULT_ORIENTATION, { optional: true }) ?? 'vertical';
232
+ /**
233
+ * Layout orientation of the list. Affects which arrow keys move focus and the lifted item, and
234
+ * how a pointer drag resolves the live drop index. Defaults to `'vertical'`, or to
235
+ * `FOR_DROP_LIST_DEFAULT_ORIENTATION` when an in-scope provider sets it (e.g.
236
+ * `ForTableColumnReorder` defaults it to `'horizontal'`).
237
+ *
238
+ * `'mixed'` is for wrapping grids (`flex-wrap` / CSS grid) of uniformly-sized items: the drop
239
+ * index is resolved in 2D so an item flowing across rows lands in the slot under the pointer's
240
+ * row and column, and every arrow key steps the lifted item linearly in DOM order. A `'mixed'`
241
+ * list that happens to render as a single row or single column resolves identically to
242
+ * `'horizontal'` / `'vertical'`.
243
+ */
244
+ orientation = input(this.#defaultOrientation, /* @ts-ignore */
245
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
246
+ /**
247
+ * Writing direction. When unset (default `null`), the inherited ambient direction is
248
+ * resolved from the nearest ancestor carrying a `dir` attribute (or `<html dir>`),
249
+ * defaulting to `'ltr'`.
250
+ */
251
+ _dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
252
+ dir = injectTextDirection(this._dirInput);
253
+ /** When true, the entire list and all its items are disabled. */
254
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
255
+ /**
256
+ * Other lists this one can transfer items into. Union'd with any `[forDropListGroup]`
257
+ * members. Disabled lists are excluded from the effective connected set.
258
+ */
259
+ connectedTo = input([], /* @ts-ignore */
260
+ ...(ngDevMode ? [{ debugName: "connectedTo" }] : /* istanbul ignore next */ []));
261
+ /**
262
+ * When true (the default), a pointer drag that nears the edge of the nearest scrollable
263
+ * container (the list, a scrollable ancestor, or the viewport) auto-scrolls it toward that
264
+ * edge so off-screen drop targets become reachable. Set `false` to opt out. Edge size and
265
+ * max speed are configured via `provideForDragDropDefaults`. Keyboard dragging is unaffected.
266
+ */
267
+ autoScroll = input(true, { ...(ngDevMode ? { debugName: "autoScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
268
+ /**
269
+ * When true, the `[forDragPlaceholder]` follows the live resolved drop index during a
270
+ * **pointer** drag — within this list and across connected lists — so siblings part to
271
+ * reveal where the item will land. When false (the default), the placeholder stays in the
272
+ * dragged item's source slot (the #806 behaviour). Has no effect without a
273
+ * `[forDragPlaceholder]` template, and none on keyboard dragging.
274
+ *
275
+ * A `dragDisabled` sibling acts as a hard fence: the placeholder stops at the first
276
+ * pinned item instead of travelling past it. This is purely visual — the committed drop
277
+ * index emitted by `dragDrop` is resolved separately and unaffected.
278
+ */
279
+ liveSort = input(false, { ...(ngDevMode ? { debugName: "liveSort" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
280
+ /**
281
+ * When true, a committed drop (keyboard or pointer) animates: displaced sibling items
282
+ * transition from their old positions to their new ones (FLIP), and — on a pointer drag — the
283
+ * floating preview settles into the final slot before it is removed. Opt-in and default off.
284
+ * Duration / easing are styled by the consumer via CSS on the published hooks
285
+ * (`[data-drag-animating]` on items, `[data-settling]` on the preview); the library imposes
286
+ * none. Fully skipped under `prefers-reduced-motion: reduce` (teardown stays instant).
287
+ */
288
+ animateReorder = input(false, { ...(ngDevMode ? { debugName: "animateReorder" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
289
+ /**
290
+ * Confine the pointer-drag preview within a boundary during a pointer drag. Accepts an
291
+ * `HTMLElement`, or a selector string resolved via `closest()` from the list host. `null`
292
+ * (the default) leaves the preview unbounded. Affects the visual preview only — the resolved
293
+ * drop index is unchanged. Has no effect on keyboard dragging (no floating preview).
294
+ */
295
+ boundary = input(null, /* @ts-ignore */
296
+ ...(ngDevMode ? [{ debugName: "boundary" }] : /* istanbul ignore next */ []));
297
+ /**
298
+ * Constrain pointer-drag preview movement to one axis. `'x'` keeps the preview at its lift-time
299
+ * `y` (horizontal movement); `'y'` keeps it at its lift-time `x` (vertical movement). `null`
300
+ * (the default) is free movement. Independent of `orientation` (which drives the drop index);
301
+ * this is a purely visual constraint, and has no effect on keyboard dragging.
302
+ */
303
+ lockAxis = input(null, /* @ts-ignore */
304
+ ...(ngDevMode ? [{ debugName: "lockAxis" }] : /* istanbul ignore next */ []));
305
+ /**
306
+ * Emitted by the **source** list when a drop commits (keyboard or pointer) — whether
307
+ * the item stays in this list (reorder) or moves to a connected list (transfer). Apply
308
+ * `moveItemInArray` or `transferArrayItem` to your data signal inside the handler.
309
+ */
310
+ dragDrop = output();
311
+ host = this.#hostRef.nativeElement;
312
+ /** Effective disabled — the list's own `disabled` input (fieldset wiring deferred to v2). */
313
+ effectiveDisabled = computed(() => this.disabled(), /* @ts-ignore */
314
+ ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
315
+ roving = new RovingTabindex();
316
+ #items = new Collection();
317
+ items = this.#items.items;
318
+ #liftedHost = signal(null, /* @ts-ignore */
319
+ ...(ngDevMode ? [{ debugName: "#liftedHost" }] : /* istanbul ignore next */ []));
320
+ #flatIndex = signal(0, /* @ts-ignore */
321
+ ...(ngDevMode ? [{ debugName: "#flatIndex" }] : /* istanbul ignore next */ []));
322
+ #dragOver = signal(null, /* @ts-ignore */
323
+ ...(ngDevMode ? [{ debugName: "#dragOver" }] : /* istanbul ignore next */ []));
324
+ #previewController = null;
325
+ #sorter = null;
326
+ #autoScroller = null;
327
+ #lastPoint = null;
328
+ /** Insertion index this list is the current drop target at, else `null`. */
329
+ dragOverIndex = this.#dragOver.asReadonly();
330
+ /** `true` while a drag originating from this list is in progress. */
331
+ isDragging = computed(() => this.#liftedHost() !== null, /* @ts-ignore */
332
+ ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
333
+ constructor() {
334
+ reconcileRovingActive(this.roving, this.#items.items);
335
+ const group = this.#group;
336
+ if (group) {
337
+ registerHandle(this, (ctx) => group.register(ctx), (ctx) => group.unregister(ctx));
338
+ }
339
+ if (this.#isBrowser) {
340
+ this.#autoScroller = createAutoScroller({
341
+ host: this.host,
342
+ win: this.#document.defaultView,
343
+ edgeSize: this.#defaults.autoScrollEdgeSize,
344
+ maxSpeed: this.#defaults.autoScrollMaxSpeed,
345
+ onFrame: () => this.#onAutoScrollFrame(),
346
+ });
347
+ this.#destroyRef.onDestroy(() => this.#autoScroller?.stop());
348
+ }
349
+ }
350
+ registerItem(handle) {
351
+ this.#items.register(handle);
352
+ }
353
+ unregisterItem(handle) {
354
+ this.#items.unregister(handle);
355
+ this.roving.unregister(handle.host);
356
+ }
357
+ itemTabindex(el) {
358
+ return this.roving.hasActive() ? this.roving.tabindexFor(el) : null;
359
+ }
360
+ isFirstFocusableItem(el) {
361
+ return firstEnabledHost(this.#items.items()) === el;
362
+ }
363
+ isItemHighlighted(el) {
364
+ return this.roving.active() === el;
365
+ }
366
+ setActiveItem(el) {
367
+ this.roving.setActive(el);
368
+ }
369
+ navigate(el, action) {
370
+ if (this.effectiveDisabled()) {
371
+ return;
372
+ }
373
+ const items = this.#items.items();
374
+ if (items.length === 0) {
375
+ return;
376
+ }
377
+ const currentIndex = items.findIndex((h) => h.host === el);
378
+ const next = moveIndex(currentIndex < 0 ? 0 : currentIndex, items.length, action, {
379
+ loop: false,
380
+ isDisabled: (i) => items[i].disabled(),
381
+ });
382
+ if (next === null) {
383
+ return;
384
+ }
385
+ items[next]?.host.focus();
386
+ }
387
+ #beginLift(el) {
388
+ if (this.effectiveDisabled() || this.#liftedHost() !== null) {
389
+ return -1;
390
+ }
391
+ const items = this.#items.items();
392
+ const handle = items.find((h) => h.host === el);
393
+ if (!handle || handle.disabled()) {
394
+ return -1;
395
+ }
396
+ const from = this.#items.indexOfHost(el);
397
+ if (from < 0) {
398
+ return -1;
399
+ }
400
+ const connected = this.#effectiveConnected();
401
+ const slots = buildDragSlots(items.length, connected.map((c) => c.items().length));
402
+ const flatIndex = indexOfSlot(slots, 0, from);
403
+ this.#liftedHost.set(el);
404
+ this.#flatIndex.set(flatIndex < 0 ? 0 : flatIndex);
405
+ this.#dragOver.set(from);
406
+ const label = (el.textContent ?? '').trim();
407
+ this.#announcer.announce(this.#defaults.announceLift(label, from + 1, items.length), 'assertive');
408
+ return from;
409
+ }
410
+ lift(el) {
411
+ return this.#beginLift(el);
412
+ }
413
+ pointerLift(el, point, preview) {
414
+ const from = this.#beginLift(el);
415
+ if (from < 0) {
416
+ return -1;
417
+ }
418
+ if (this.#isBrowser) {
419
+ this.#previewController = new PreviewController({
420
+ source: el,
421
+ point,
422
+ preview,
423
+ doc: this.#document,
424
+ boundary: this.#resolveBoundary(),
425
+ lockAxis: this.lockAxis,
426
+ });
427
+ if (this.liveSort()) {
428
+ this.#sorter = new PlaceholderSorter({
429
+ source: this,
430
+ lifted: el,
431
+ doc: this.#document,
432
+ sourceItems: this.#items.items,
433
+ originIndex: () => this.#items.indexOfHost(el),
434
+ });
435
+ }
436
+ }
437
+ return from;
438
+ }
439
+ pointerMove(point) {
440
+ const lifted = this.#liftedHost();
441
+ if (lifted === null || !this.#isBrowser) {
442
+ return;
443
+ }
444
+ this.#lastPoint = point;
445
+ this.#resolveDrop(point, lifted);
446
+ if (this.autoScroll()) {
447
+ this.#autoScroller?.update(point);
448
+ }
449
+ else {
450
+ this.#autoScroller?.stop();
451
+ }
452
+ }
453
+ #resolveDrop(point, lifted) {
454
+ const connected = this.#effectiveConnected();
455
+ const containers = [this, ...connected];
456
+ const geoms = containers.map((ctx) => ({
457
+ rect: ctx.host.getBoundingClientRect(),
458
+ itemRects: ctx
459
+ .items()
460
+ .filter((h) => h.host !== lifted)
461
+ .map((h) => h.host.getBoundingClientRect()),
462
+ }));
463
+ const target = resolveDropTarget(point, geoms, this.orientation(), this.dir());
464
+ this.#previewController?.moveTo(point);
465
+ if (!target) {
466
+ return;
467
+ }
468
+ const slots = buildDragSlots(this.#items.items().length, connected.map((c) => c.items().length));
469
+ const flat = indexOfSlot(slots, target.containerIndex, target.index);
470
+ if (flat < 0) {
471
+ return;
472
+ }
473
+ containers.forEach((ctx, i) => ctx.setDragOver(i === target.containerIndex ? target.index : null));
474
+ const changed = flat !== this.#flatIndex();
475
+ this.#flatIndex.set(flat);
476
+ if (changed) {
477
+ const targetCtx = containers[target.containerIndex];
478
+ this.#sorter?.onTargetChange(targetCtx, target.index);
479
+ const label = (lifted.textContent ?? '').trim();
480
+ this.#announcer.announce(this.#defaults.announceMove(label, target.index + 1, targetCtx.items().length), 'polite');
481
+ }
482
+ }
483
+ #resolveBoundary() {
484
+ const boundary = this.boundary();
485
+ if (boundary === null) {
486
+ return null;
487
+ }
488
+ return typeof boundary === 'string' ? this.host.closest(boundary) : boundary;
489
+ }
490
+ #onAutoScrollFrame() {
491
+ const lifted = this.#liftedHost();
492
+ const point = this.#lastPoint;
493
+ if (lifted === null || point === null) {
494
+ return;
495
+ }
496
+ this.#resolveDrop(point, lifted);
497
+ }
498
+ isLifted(el) {
499
+ return this.#liftedHost() === el;
500
+ }
501
+ moveLifted(action) {
502
+ const liftedHost = this.#liftedHost();
503
+ if (liftedHost === null) {
504
+ return;
505
+ }
506
+ const delta = action === 'next' || action === 'last' ? 1 : -1;
507
+ const connected = this.#effectiveConnected();
508
+ const slots = buildDragSlots(this.#items.items().length, connected.map((c) => c.items().length));
509
+ const current = this.#flatIndex();
510
+ const next = stepSlot(slots, current, delta);
511
+ if (next === current) {
512
+ return;
513
+ }
514
+ const prevSlot = slots[current];
515
+ const nextSlot = slots[next];
516
+ if (!prevSlot || !nextSlot) {
517
+ return;
518
+ }
519
+ const prevTarget = prevSlot.containerIndex === 0 ? this : connected[prevSlot.containerIndex - 1];
520
+ const nextTarget = nextSlot.containerIndex === 0 ? this : connected[nextSlot.containerIndex - 1];
521
+ if (prevTarget && prevTarget !== nextTarget) {
522
+ prevTarget.setDragOver(null);
523
+ }
524
+ if (nextTarget) {
525
+ nextTarget.setDragOver(nextSlot.index);
526
+ }
527
+ this.#flatIndex.set(next);
528
+ if (nextTarget) {
529
+ const targetItems = nextTarget.items();
530
+ const label = (liftedHost.textContent ?? '').trim();
531
+ this.#announcer.announce(this.#defaults.announceMove(label, nextSlot.index + 1, targetItems.length), 'polite');
532
+ }
533
+ }
534
+ drop() {
535
+ const liftedHost = this.#liftedHost();
536
+ if (liftedHost === null) {
537
+ return;
538
+ }
539
+ const connected = this.#effectiveConnected();
540
+ const items = this.#items.items();
541
+ const slots = buildDragSlots(items.length, connected.map((c) => c.items().length));
542
+ const slot = slots[this.#flatIndex()];
543
+ if (!slot) {
544
+ this.#teardown(connected);
545
+ return;
546
+ }
547
+ const previousIndex = this.#items.indexOfHost(liftedHost);
548
+ const currentIndex = slot.index;
549
+ const container = slot.containerIndex === 0 ? this : (connected[slot.containerIndex - 1] ?? this);
550
+ const handle = items.find((h) => h.host === liftedHost);
551
+ const item = handle ? handle.data() : undefined;
552
+ const label = (liftedHost.textContent ?? '').trim();
553
+ const targetItems = container.items();
554
+ const moved = !(previousIndex === currentIndex && container === this);
555
+ const animate = this.animateReorder() && this.#isBrowser && !this.#prefersReducedMotion() && moved;
556
+ const preview = this.#previewController?.preview ?? null;
557
+ const animator = animate
558
+ ? new ReorderAnimator({
559
+ containers: [this, ...connected],
560
+ injector: this.#injector,
561
+ win: this.#document.defaultView,
562
+ })
563
+ : null;
564
+ animator?.captureFirst();
565
+ this.dragDrop.emit({
566
+ item,
567
+ previousContainer: this,
568
+ container,
569
+ previousIndex,
570
+ currentIndex,
571
+ });
572
+ this.#announcer.announce(this.#defaults.announceDrop(label, currentIndex + 1, targetItems.length), 'assertive');
573
+ if (animator) {
574
+ animator.schedule(liftedHost, preview);
575
+ this.#teardown(connected, true);
576
+ }
577
+ else {
578
+ this.#teardown(connected);
579
+ }
580
+ }
581
+ cancel() {
582
+ const liftedHost = this.#liftedHost();
583
+ if (liftedHost === null) {
584
+ return;
585
+ }
586
+ const label = (liftedHost.textContent ?? '').trim();
587
+ this.#announcer.announce(this.#defaults.announceCancel(label), 'assertive');
588
+ this.#teardown(this.#effectiveConnected());
589
+ }
590
+ setDragOver(index) {
591
+ this.#dragOver.set(index);
592
+ }
593
+ setLivePlaceholder(nodes) {
594
+ this.#sorter?.setNodes(nodes);
595
+ }
596
+ #effectiveConnected = computed(() => {
597
+ const explicit = this.connectedTo();
598
+ const groupMembers = this.#group?.members() ?? [];
599
+ const seen = new Set();
600
+ seen.add(this);
601
+ const result = [];
602
+ for (const ctx of [...explicit, ...groupMembers]) {
603
+ if (!seen.has(ctx) && !ctx.disabled()) {
604
+ seen.add(ctx);
605
+ result.push(ctx);
606
+ }
607
+ }
608
+ return result;
609
+ }, /* @ts-ignore */
610
+ ...(ngDevMode ? [{ debugName: "#effectiveConnected" }] : /* istanbul ignore next */ []));
611
+ #teardown(connected, keepPreview = false) {
612
+ this.#autoScroller?.stop();
613
+ this.#lastPoint = null;
614
+ this.#dragOver.set(null);
615
+ for (const ctx of connected) {
616
+ ctx.setDragOver(null);
617
+ }
618
+ this.#liftedHost.set(null);
619
+ this.#flatIndex.set(0);
620
+ if (!keepPreview) {
621
+ this.#previewController?.destroy();
622
+ }
623
+ this.#previewController = null;
624
+ this.#sorter = null;
625
+ }
626
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDropList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
627
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDropList, isStandalone: true, selector: "[forDropList]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, _dirInput: { classPropertyName: "_dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, connectedTo: { classPropertyName: "connectedTo", publicName: "connectedTo", isSignal: true, isRequired: false, transformFunction: null }, autoScroll: { classPropertyName: "autoScroll", publicName: "autoScroll", isSignal: true, isRequired: false, transformFunction: null }, liveSort: { classPropertyName: "liveSort", publicName: "liveSort", isSignal: true, isRequired: false, transformFunction: null }, animateReorder: { classPropertyName: "animateReorder", publicName: "animateReorder", isSignal: true, isRequired: false, transformFunction: null }, boundary: { classPropertyName: "boundary", publicName: "boundary", isSignal: true, isRequired: false, transformFunction: null }, lockAxis: { classPropertyName: "lockAxis", publicName: "lockAxis", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragDrop: "dragDrop" }, host: { properties: { "attr.data-orientation": "orientation()", "attr.dir": "dir()", "attr.data-disabled": "effectiveDisabled() ? '' : null", "attr.data-dragging": "isDragging() ? '' : null", "attr.data-drag-over": "dragOverIndex() !== null ? '' : null" } }, providers: [{ provide: FOR_DRAG_DROP_CONTEXT, useExisting: ForDropList }], exportAs: ["forDropList"], ngImport: i0 });
628
+ }
629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDropList, decorators: [{
630
+ type: Directive,
631
+ args: [{
632
+ selector: '[forDropList]',
633
+ exportAs: 'forDropList',
634
+ host: {
635
+ '[attr.data-orientation]': 'orientation()',
636
+ '[attr.dir]': 'dir()',
637
+ '[attr.data-disabled]': "effectiveDisabled() ? '' : null",
638
+ '[attr.data-dragging]': "isDragging() ? '' : null",
639
+ '[attr.data-drag-over]': "dragOverIndex() !== null ? '' : null",
640
+ },
641
+ providers: [{ provide: FOR_DRAG_DROP_CONTEXT, useExisting: ForDropList }],
642
+ }]
643
+ }], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], connectedTo: [{ type: i0.Input, args: [{ isSignal: true, alias: "connectedTo", required: false }] }], autoScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScroll", required: false }] }], liveSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "liveSort", required: false }] }], animateReorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "animateReorder", required: false }] }], boundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundary", required: false }] }], lockAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockAxis", required: false }] }], dragDrop: [{ type: i0.Output, args: ["dragDrop"] }] } });
644
+
645
+ /**
646
+ * Optional custom drag preview. Apply `[forDragPreview]` on an `<ng-template>` inside a
647
+ * `[forDraggable]`; its content renders as the floating element that follows the pointer during a
648
+ * pointer drag, replacing the default cloned preview. Has no effect on keyboard dragging.
649
+ */
650
+ class ForDragPreview {
651
+ /** The template rendered as the floating pointer-drag preview. */
652
+ templateRef = inject(TemplateRef);
653
+ constructor() {
654
+ injectDraggableContext('ForDragPreview');
655
+ }
656
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragPreview, deps: [], target: i0.ɵɵFactoryTarget.Directive });
657
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDragPreview, isStandalone: true, selector: "ng-template[forDragPreview]", exportAs: ["forDragPreview"], ngImport: i0 });
658
+ }
659
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragPreview, decorators: [{
660
+ type: Directive,
661
+ args: [{
662
+ selector: 'ng-template[forDragPreview]',
663
+ exportAs: 'forDragPreview',
664
+ }]
665
+ }], ctorParameters: () => [] });
666
+
667
+ /**
668
+ * Optional custom drag placeholder. Apply `[forDragPlaceholder]` on an `<ng-template>` inside a
669
+ * `[forDraggable]`; during a **pointer** drag the dragged item's host is hidden and this template
670
+ * renders in its slot, preserving the gap. Keyboard dragging keeps the default (the lifted item
671
+ * stays in place, reflecting `data-dragging`).
672
+ */
673
+ class ForDragPlaceholder {
674
+ /** The template rendered in the dragged item's slot during a pointer drag. */
675
+ templateRef = inject(TemplateRef);
676
+ constructor() {
677
+ injectDraggableContext('ForDragPlaceholder');
678
+ }
679
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragPlaceholder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
680
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDragPlaceholder, isStandalone: true, selector: "ng-template[forDragPlaceholder]", exportAs: ["forDragPlaceholder"], ngImport: i0 });
681
+ }
682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragPlaceholder, decorators: [{
683
+ type: Directive,
684
+ args: [{
685
+ selector: 'ng-template[forDragPlaceholder]',
686
+ exportAs: 'forDragPlaceholder',
687
+ }]
688
+ }], ctorParameters: () => [] });
689
+
690
+ const POINTER_ARM_THRESHOLD_PX$1 = 5;
691
+ /**
692
+ * Marks an element as a draggable item inside a `[forDropList]`. Handles
693
+ * keyboard and pointer interaction for lift, move, drop, and cancel.
694
+ */
695
+ class ForDraggable {
696
+ #list = injectDropListContext('ForDraggable');
697
+ #host = inject(ElementRef);
698
+ #defaults = inject(FOR_DRAG_DROP_DEFAULTS);
699
+ #destroyRef = inject(DestroyRef);
700
+ #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
701
+ #document = inject(DOCUMENT);
702
+ #vcr = inject(ViewContainerRef);
703
+ previewTpl = contentChild(ForDragPreview, /* @ts-ignore */
704
+ ...(ngDevMode ? [{ debugName: "previewTpl" }] : /* istanbul ignore next */ []));
705
+ placeholderTpl = contentChild(ForDragPlaceholder, /* @ts-ignore */
706
+ ...(ngDevMode ? [{ debugName: "placeholderTpl" }] : /* istanbul ignore next */ []));
707
+ #placeholderView = null;
708
+ /** `true` while a pointer-drag placeholder template occupies this item's slot (host hidden). */
709
+ placeholderActive = signal(false, /* @ts-ignore */
710
+ ...(ngDevMode ? [{ debugName: "placeholderActive" }] : /* istanbul ignore next */ []));
711
+ #handles = new Set();
712
+ #pointerDragging = false;
713
+ #pointerSession = null;
714
+ /** Data payload handed back in the `dragDrop` event when this item is dropped. */
715
+ dragData = input.required(/* @ts-ignore */
716
+ ...(ngDevMode ? [{ debugName: "dragData" }] : /* istanbul ignore next */ []));
717
+ /**
718
+ * When true, this item cannot be lifted. The item remains focusable. It also acts as a
719
+ * hard fence for the `liveSort` placeholder: a sibling being dragged cannot move its
720
+ * placeholder across a pinned item (the committed drop index is unaffected).
721
+ */
722
+ dragDisabled = input(false, { ...(ngDevMode ? { debugName: "dragDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
723
+ /** Emitted when a drag (keyboard or pointer) starts from this item. */
724
+ dragStart = output();
725
+ /** Emitted when a drag originating from this item ends (committed or cancelled). */
726
+ dragEnd = output();
727
+ effectiveDisabled = computed(() => this.dragDisabled() || this.#list.disabled(), /* @ts-ignore */
728
+ ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
729
+ /** `true` when this item is the currently lifted draggable. Reflected as `data-dragging`. */
730
+ lifted = computed(() => this.#list.isLifted(this.#host.nativeElement), /* @ts-ignore */
731
+ ...(ngDevMode ? [{ debugName: "lifted" }] : /* istanbul ignore next */ []));
732
+ /** `true` when this item is the roving-tabindex active candidate. */
733
+ highlighted = computed(() => this.#list.isItemHighlighted(this.#host.nativeElement), /* @ts-ignore */
734
+ ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
735
+ /** `aria-roledescription` value from defaults. */
736
+ roleDescription = computed(() => this.#defaults.itemRoleDescription, /* @ts-ignore */
737
+ ...(ngDevMode ? [{ debugName: "roleDescription" }] : /* istanbul ignore next */ []));
738
+ tabindex = computed(() => {
739
+ if (this.effectiveDisabled()) {
740
+ return -1;
741
+ }
742
+ const rovingTabindex = this.#list.itemTabindex(this.#host.nativeElement);
743
+ if (rovingTabindex !== null) {
744
+ return rovingTabindex;
745
+ }
746
+ return this.#list.isFirstFocusableItem(this.#host.nativeElement) ? 0 : -1;
747
+ }, /* @ts-ignore */
748
+ ...(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 */ []));
751
+ constructor() {
752
+ const handle = {
753
+ host: this.#host.nativeElement,
754
+ data: this.dragData,
755
+ disabled: this.effectiveDisabled,
756
+ };
757
+ registerHandle(handle, (h) => this.#list.registerItem(h), (h) => this.#list.unregisterItem(h));
758
+ if (this.#isBrowser) {
759
+ this.#pointerSession = createPointerDragSession({
760
+ host: this.#host.nativeElement,
761
+ document: this.#document,
762
+ armThreshold: POINTER_ARM_THRESHOLD_PX$1,
763
+ cancelOnEscape: true,
764
+ capturePointer: true,
765
+ canStart: (event) => this.#canStartPointer(event),
766
+ onLift: (event) => this.#onPointerLift(event),
767
+ onMove: (event) => this.#onPointerMove(event),
768
+ onCommit: () => this.#onPointerCommit(),
769
+ onCancel: () => this.#onPointerCancel(),
770
+ });
771
+ this.#destroyRef.onDestroy(() => this.#pointerSession?.destroy());
772
+ this.#destroyRef.onDestroy(() => this.#clearPlaceholder());
773
+ }
774
+ }
775
+ registerHandle(el) {
776
+ this.#handles.add(el);
777
+ }
778
+ 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));
793
+ }
794
+ #onPointerLift(event) {
795
+ const preview = this.#buildPreview();
796
+ const index = this.#list.pointerLift(this.#host.nativeElement, { x: event.clientX, y: event.clientY }, preview);
797
+ if (index < 0) {
798
+ preview?.destroy();
799
+ return false;
800
+ }
801
+ this.#renderPlaceholder();
802
+ this.#pointerDragging = true;
803
+ this.dragStart.emit({ source: this.#list, index });
804
+ return true;
805
+ }
806
+ #onPointerMove(event) {
807
+ this.#list.pointerMove({ x: event.clientX, y: event.clientY });
808
+ }
809
+ #onPointerCommit() {
810
+ this.#pointerDragging = false;
811
+ this.#clearPlaceholder();
812
+ this.#list.drop();
813
+ this.dragEnd.emit({ dropped: true });
814
+ }
815
+ #onPointerCancel() {
816
+ if (!this.#pointerDragging) {
817
+ return;
818
+ }
819
+ this.#pointerDragging = false;
820
+ this.#clearPlaceholder();
821
+ this.#list.cancel();
822
+ this.dragEnd.emit({ dropped: false });
823
+ }
824
+ #buildPreview() {
825
+ const preview = this.previewTpl();
826
+ if (!preview || !this.#isBrowser) {
827
+ return null;
828
+ }
829
+ const viewRef = this.#vcr.createEmbeddedView(preview.templateRef);
830
+ viewRef.detectChanges();
831
+ return createTemplatePreview(viewRef.rootNodes, this.#document, () => viewRef.destroy());
832
+ }
833
+ #renderPlaceholder() {
834
+ const placeholder = this.placeholderTpl();
835
+ if (!placeholder || !this.#isBrowser) {
836
+ return;
837
+ }
838
+ this.#placeholderView = this.#vcr.createEmbeddedView(placeholder.templateRef);
839
+ this.#placeholderView.detectChanges();
840
+ this.placeholderActive.set(true);
841
+ this.#list.setLivePlaceholder(this.#placeholderView.rootNodes);
842
+ }
843
+ #clearPlaceholder() {
844
+ this.#list.setLivePlaceholder(null);
845
+ this.placeholderActive.set(false);
846
+ this.#placeholderView?.destroy();
847
+ this.#placeholderView = null;
848
+ }
849
+ onNativeDragStart(event) {
850
+ if (this.#pointerDragging) {
851
+ event.preventDefault();
852
+ }
853
+ }
854
+ onFocus() {
855
+ if (!this.effectiveDisabled()) {
856
+ this.#list.setActiveItem(this.#host.nativeElement);
857
+ }
858
+ }
859
+ onBlur() {
860
+ if (this.lifted() && !this.#pointerDragging) {
861
+ this.#list.cancel();
862
+ this.dragEnd.emit({ dropped: false });
863
+ }
864
+ }
865
+ onKeyDown(event) {
866
+ if (this.effectiveDisabled() || this.#pointerDragging) {
867
+ return;
868
+ }
869
+ const host = this.#host.nativeElement;
870
+ if (this.lifted()) {
871
+ if (event.key === ' ' || event.key === 'Enter') {
872
+ event.preventDefault();
873
+ this.#list.drop();
874
+ this.dragEnd.emit({ dropped: true });
875
+ return;
876
+ }
877
+ if (event.key === 'Escape') {
878
+ event.preventDefault();
879
+ this.#list.cancel();
880
+ this.dragEnd.emit({ dropped: false });
881
+ return;
882
+ }
883
+ const action = resolveListNavigation(event, {
884
+ orientation: this.#list.orientation(),
885
+ dir: this.#list.dir(),
886
+ });
887
+ if (action) {
888
+ event.preventDefault();
889
+ this.#list.moveLifted(action);
890
+ }
891
+ return;
892
+ }
893
+ if (event.key === ' ' || event.key === 'Enter') {
894
+ event.preventDefault();
895
+ const index = this.#list.lift(host);
896
+ if (index >= 0) {
897
+ this.dragStart.emit({ source: this.#list, index });
898
+ }
899
+ return;
900
+ }
901
+ const action = resolveListNavigation(event, {
902
+ orientation: this.#list.orientation(),
903
+ dir: this.#list.dir(),
904
+ });
905
+ if (action) {
906
+ event.preventDefault();
907
+ this.#list.navigate(host, action);
908
+ }
909
+ }
910
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDraggable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
911
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForDraggable, isStandalone: true, selector: "[forDraggable]", inputs: { dragData: { classPropertyName: "dragData", publicName: "dragData", isSignal: true, isRequired: true, transformFunction: null }, dragDisabled: { classPropertyName: "dragDisabled", publicName: "dragDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragStart: "dragStart", dragEnd: "dragEnd" }, host: { listeners: { "dragstart": "onNativeDragStart($event)", "keydown": "onKeyDown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.tabindex": "tabindex()", "attr.aria-roledescription": "roleDescription() || null", "attr.aria-disabled": "effectiveDisabled() ? 'true' : null", "attr.data-dragging": "lifted() ? '' : null", "attr.data-disabled": "effectiveDisabled() ? '' : null", "style.touch-action": "touchAction()", "style.display": "placeholderActive() ? 'none' : null" } }, providers: [{ provide: FOR_DRAGGABLE_CONTEXT, useExisting: ForDraggable }], queries: [{ propertyName: "previewTpl", first: true, predicate: ForDragPreview, descendants: true, isSignal: true }, { propertyName: "placeholderTpl", first: true, predicate: ForDragPlaceholder, descendants: true, isSignal: true }], exportAs: ["forDraggable"], ngImport: i0 });
912
+ }
913
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDraggable, decorators: [{
914
+ type: Directive,
915
+ args: [{
916
+ selector: '[forDraggable]',
917
+ exportAs: 'forDraggable',
918
+ providers: [{ provide: FOR_DRAGGABLE_CONTEXT, useExisting: ForDraggable }],
919
+ host: {
920
+ '[attr.tabindex]': 'tabindex()',
921
+ '[attr.aria-roledescription]': 'roleDescription() || null',
922
+ '[attr.aria-disabled]': "effectiveDisabled() ? 'true' : null",
923
+ '[attr.data-dragging]': "lifted() ? '' : null",
924
+ '[attr.data-disabled]': "effectiveDisabled() ? '' : null",
925
+ '[style.touch-action]': 'touchAction()',
926
+ '[style.display]': "placeholderActive() ? 'none' : null",
927
+ '(dragstart)': 'onNativeDragStart($event)',
928
+ '(keydown)': 'onKeyDown($event)',
929
+ '(focus)': 'onFocus()',
930
+ '(blur)': 'onBlur()',
931
+ },
932
+ }]
933
+ }], ctorParameters: () => [], propDecorators: { previewTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForDragPreview), { isSignal: true }] }], placeholderTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForDragPlaceholder), { isSignal: true }] }], dragData: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragData", required: true }] }], dragDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragDisabled", required: false }] }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }] } });
934
+
935
+ const POINTER_ARM_THRESHOLD_PX = 5;
936
+ /**
937
+ * Standalone free-drag directive: repositions its host (or a resolved `rootElement`) by pointer
938
+ * drag via a CSS `transform: translate(...)`, with **no** `[forDropList]` dependency. Unlike
939
+ * `[forDraggable]`, it never commits a reorder — it only moves an arbitrary element around,
940
+ * optionally confined to a `boundary` and locked to one axis. Composes the shared
941
+ * `createPointerDragSession` transport and accepts `[forDragHandle]` children.
942
+ *
943
+ * Pointer-only: there is no WAI-ARIA APG pattern for "freely reposition an element", so this is
944
+ * a documented headless exception (see `.claude/rules/conventions.md`). It owns no role or ARIA
945
+ * state and must not destroy the moved element's semantics — the consumer keeps the moved
946
+ * element operable at its default position (e.g. a repositionable dialog stays usable by
947
+ * keyboard); dragging is a pointer convenience, not the only way to use it.
948
+ */
949
+ class ForFreeDrag {
950
+ #host = inject(ElementRef);
951
+ #document = inject(DOCUMENT);
952
+ #destroyRef = inject(DestroyRef);
953
+ #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
954
+ #handles = new Set();
955
+ #lift = signal(null, /* @ts-ignore */
956
+ ...(ngDevMode ? [{ debugName: "#lift" }] : /* istanbul ignore next */ []));
957
+ #pointerSession = null;
958
+ /** When true, the element can't be dragged (it stays focusable; the transform doesn't change). */
959
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
960
+ /**
961
+ * The element actually moved: an `HTMLElement`, or a selector resolved via `closest()` from the
962
+ * host. `null` (the default) moves the host itself. Mirrors CDK's `cdkDragRootElement` — drag a
963
+ * child handle, move an ancestor (e.g. drag a whole dialog by its header).
964
+ */
965
+ rootElement = input(null, /* @ts-ignore */
966
+ ...(ngDevMode ? [{ debugName: "rootElement" }] : /* istanbul ignore next */ []));
967
+ /**
968
+ * Confine movement within an `HTMLElement`, a `closest()` selector resolved from the host, or
969
+ * `null` (the default) for unbounded movement. Unlike `[forDropList]`'s boundary (which clamps a
970
+ * floating clone), this clamps the **real moved element** so its box stays fully inside.
971
+ */
972
+ boundary = input(null, /* @ts-ignore */
973
+ ...(ngDevMode ? [{ debugName: "boundary" }] : /* istanbul ignore next */ []));
974
+ /**
975
+ * Constrain movement to one axis. `'x'` pins the element at its lift-time `y` (horizontal-only);
976
+ * `'y'` pins it at its lift-time `x` (vertical-only). `null` (the default) is free.
977
+ */
978
+ lockAxis = input(null, /* @ts-ignore */
979
+ ...(ngDevMode ? [{ debugName: "lockAxis" }] : /* istanbul ignore next */ []));
980
+ /** Two-way translate offset (px) from the element's natural position. Controllable / restorable. */
981
+ position = model({ x: 0, y: 0 }, /* @ts-ignore */
982
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
983
+ /** Emitted when a pointer drag starts, with the lift-time position. */
984
+ dragStart = output();
985
+ /**
986
+ * Emitted on every armed move with the live position. (Non-native verb — `dragMove`, not
987
+ * `drag` / `dragMoved` — so `@angular-eslint/no-output-native` stays clear.)
988
+ */
989
+ dragMove = output();
990
+ /** Emitted when the drag ends, with the final position. */
991
+ dragEnd = output();
992
+ /** True while a pointer drag is armed. Reflected as `data-dragging`. */
993
+ dragging = computed(() => this.#lift() !== null, /* @ts-ignore */
994
+ ...(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 */ []));
997
+ constructor() {
998
+ if (this.#isBrowser) {
999
+ effect(() => {
1000
+ const { x, y } = this.position();
1001
+ const el = this.#resolvedRoot();
1002
+ el.style.transform = x === 0 && y === 0 ? '' : `translate(${x}px, ${y}px)`;
1003
+ });
1004
+ this.#pointerSession = createPointerDragSession({
1005
+ host: this.#host.nativeElement,
1006
+ document: this.#document,
1007
+ armThreshold: POINTER_ARM_THRESHOLD_PX,
1008
+ cancelOnEscape: true,
1009
+ capturePointer: true,
1010
+ canStart: (event) => this.#canStartPointer(event),
1011
+ onLift: (event) => this.#onLift(event),
1012
+ onMove: (event) => this.#onMove(event),
1013
+ onCommit: () => this.#onCommit(),
1014
+ onCancel: () => this.#onCancel(),
1015
+ });
1016
+ this.#destroyRef.onDestroy(() => this.#pointerSession?.destroy());
1017
+ }
1018
+ }
1019
+ registerHandle(el) {
1020
+ this.#handles.add(el);
1021
+ }
1022
+ unregisterHandle(el) {
1023
+ this.#handles.delete(el);
1024
+ }
1025
+ #resolvedRoot = computed(() => {
1026
+ const root = this.rootElement();
1027
+ const host = this.#host.nativeElement;
1028
+ if (root === null) {
1029
+ return host;
1030
+ }
1031
+ return typeof root === 'string' ? (host.closest(root) ?? host) : root;
1032
+ }, /* @ts-ignore */
1033
+ ...(ngDevMode ? [{ debugName: "#resolvedRoot" }] : /* istanbul ignore next */ []));
1034
+ #resolveBoundary() {
1035
+ const boundary = this.boundary();
1036
+ if (boundary === null) {
1037
+ return null;
1038
+ }
1039
+ return typeof boundary === 'string'
1040
+ ? this.#host.nativeElement.closest(boundary)
1041
+ : boundary;
1042
+ }
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
+ #onLift(event) {
1057
+ const rect = this.#resolvedRoot().getBoundingClientRect();
1058
+ const position = this.position();
1059
+ this.#lift.set({
1060
+ point: { x: event.clientX, y: event.clientY },
1061
+ position,
1062
+ topLeft: { x: rect.left, y: rect.top },
1063
+ natural: { x: rect.left - position.x, y: rect.top - position.y },
1064
+ size: { width: rect.width, height: rect.height },
1065
+ });
1066
+ this.dragStart.emit(position);
1067
+ return true;
1068
+ }
1069
+ #onMove(event) {
1070
+ const lift = this.#lift();
1071
+ if (!lift) {
1072
+ return;
1073
+ }
1074
+ const desired = {
1075
+ x: lift.topLeft.x + (event.clientX - lift.point.x),
1076
+ y: lift.topLeft.y + (event.clientY - lift.point.y),
1077
+ };
1078
+ const boundaryRect = this.#resolveBoundary()?.getBoundingClientRect() ?? null;
1079
+ const clamped = clampPreviewPosition(desired, lift.size, boundaryRect, this.lockAxis(), lift.topLeft);
1080
+ const next = { x: clamped.x - lift.natural.x, y: clamped.y - lift.natural.y };
1081
+ this.position.set(next);
1082
+ this.dragMove.emit(next);
1083
+ }
1084
+ #onCommit() {
1085
+ this.#lift.set(null);
1086
+ this.dragEnd.emit(this.position());
1087
+ }
1088
+ #onCancel() {
1089
+ const lift = this.#lift();
1090
+ if (lift) {
1091
+ this.position.set(lift.position);
1092
+ }
1093
+ this.#lift.set(null);
1094
+ this.dragEnd.emit(this.position());
1095
+ }
1096
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForFreeDrag, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1097
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForFreeDrag, isStandalone: true, selector: "[forFreeDrag]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rootElement: { classPropertyName: "rootElement", publicName: "rootElement", isSignal: true, isRequired: false, transformFunction: null }, boundary: { classPropertyName: "boundary", publicName: "boundary", isSignal: true, isRequired: false, transformFunction: null }, lockAxis: { classPropertyName: "lockAxis", publicName: "lockAxis", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { position: "positionChange", dragStart: "dragStart", dragMove: "dragMove", dragEnd: "dragEnd" }, host: { properties: { "attr.data-dragging": "dragging() ? '' : null", "attr.data-disabled": "disabled() ? '' : null", "style.touch-action": "touchAction()" } }, providers: [{ provide: FOR_DRAGGABLE_CONTEXT, useExisting: ForFreeDrag }], exportAs: ["forFreeDrag"], ngImport: i0 });
1098
+ }
1099
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForFreeDrag, decorators: [{
1100
+ type: Directive,
1101
+ args: [{
1102
+ selector: '[forFreeDrag]',
1103
+ exportAs: 'forFreeDrag',
1104
+ providers: [{ provide: FOR_DRAGGABLE_CONTEXT, useExisting: ForFreeDrag }],
1105
+ host: {
1106
+ '[attr.data-dragging]': "dragging() ? '' : null",
1107
+ '[attr.data-disabled]': "disabled() ? '' : null",
1108
+ '[style.touch-action]': 'touchAction()',
1109
+ },
1110
+ }]
1111
+ }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rootElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootElement", required: false }] }], boundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundary", required: false }] }], lockAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockAxis", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }, { type: i0.Output, args: ["positionChange"] }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragMove: [{ type: i0.Output, args: ["dragMove"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }] } });
1112
+
1113
+ /**
1114
+ * Optional drag handle inside a `[forDraggable]`. When at least one handle is present on an item, a pointer
1115
+ * drag may only start from within a handle (keyboard dragging is unaffected). Apply on a child element.
1116
+ */
1117
+ class ForDragHandle {
1118
+ #context = injectDraggableContext('ForDragHandle');
1119
+ #host = inject(ElementRef);
1120
+ constructor() {
1121
+ registerHandle(this.#host.nativeElement, (el) => this.#context.registerHandle(el), (el) => this.#context.unregisterHandle(el));
1122
+ }
1123
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragHandle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1124
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDragHandle, isStandalone: true, selector: "[forDragHandle]", host: { properties: { "style.touch-action": "'none'", "attr.data-drag-handle": "''" } }, exportAs: ["forDragHandle"], ngImport: i0 });
1125
+ }
1126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDragHandle, decorators: [{
1127
+ type: Directive,
1128
+ args: [{
1129
+ selector: '[forDragHandle]',
1130
+ exportAs: 'forDragHandle',
1131
+ host: {
1132
+ '[style.touch-action]': "'none'",
1133
+ '[attr.data-drag-handle]': "''",
1134
+ },
1135
+ }]
1136
+ }], ctorParameters: () => [] });
1137
+
1138
+ /**
1139
+ * Returns a copy of `array` with the item at `from` moved to `to`. Indices are clamped to
1140
+ * the array bounds; an out-of-range or no-op move returns a shallow copy unchanged.
1141
+ */
1142
+ function moveItemInArray(array, from, to) {
1143
+ const len = array.length;
1144
+ if (len === 0) {
1145
+ return [];
1146
+ }
1147
+ const clampedFrom = Math.max(0, Math.min(len - 1, from));
1148
+ const clampedTo = Math.max(0, Math.min(len - 1, to));
1149
+ const copy = [...array];
1150
+ if (clampedFrom === clampedTo) {
1151
+ return copy;
1152
+ }
1153
+ const [item] = copy.splice(clampedFrom, 1);
1154
+ copy.splice(clampedTo, 0, item);
1155
+ return copy;
1156
+ }
1157
+ /**
1158
+ * Moves the item at `fromIndex` of `from` into `to` at `toIndex`, returning new arrays for
1159
+ * both. Does not mutate the inputs. Indices are clamped (`toIndex` may equal `to.length`,
1160
+ * i.e. append).
1161
+ */
1162
+ function transferArrayItem(from, to, fromIndex, toIndex) {
1163
+ if (from.length === 0) {
1164
+ return { from: [], to: [...to] };
1165
+ }
1166
+ const clampedFrom = Math.max(0, Math.min(from.length - 1, fromIndex));
1167
+ const clampedTo = Math.max(0, Math.min(to.length, toIndex));
1168
+ const nextFrom = [...from];
1169
+ const [item] = nextFrom.splice(clampedFrom, 1);
1170
+ const nextTo = [...to];
1171
+ nextTo.splice(clampedTo, 0, item);
1172
+ return { from: nextFrom, to: nextTo };
1173
+ }
1174
+
1175
+ /**
1176
+ * Generated bundle index. Do not edit.
1177
+ */
1178
+
1179
+ export { FOR_DRAGGABLE_CONTEXT, FOR_DRAG_DROP_CONTEXT, FOR_DRAG_DROP_DEFAULTS, FOR_DROP_LIST_DEFAULT_ORIENTATION, FOR_DROP_LIST_GROUP, ForDragHandle, ForDragPlaceholder, ForDragPreview, ForDraggable, ForDropList, ForDropListGroup, ForFreeDrag, moveItemInArray, provideForDragDropDefaults, transferArrayItem };
1180
+ //# sourceMappingURL=forty-cdk-drag-drop.mjs.map