ngx-kit 7.1.0 → 7.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 (73) hide show
  1. package/angle-selector/README.md +34 -34
  2. package/box-shadow/README.md +33 -33
  3. package/color-picker/README.md +52 -52
  4. package/{shared → core}/README.md +117 -117
  5. package/data-table/README.md +120 -120
  6. package/date-picker/README.md +74 -74
  7. package/dialog/README.md +122 -92
  8. package/drag-resize/README.md +53 -55
  9. package/fesm2022/ngx-kit-angle-selector.mjs +9 -26
  10. package/fesm2022/ngx-kit-angle-selector.mjs.map +1 -1
  11. package/fesm2022/ngx-kit-box-shadow.mjs +330 -356
  12. package/fesm2022/ngx-kit-box-shadow.mjs.map +1 -1
  13. package/fesm2022/ngx-kit-color-picker.mjs +132 -272
  14. package/fesm2022/ngx-kit-color-picker.mjs.map +1 -1
  15. package/fesm2022/{ngx-kit-shared.mjs → ngx-kit-core.mjs} +908 -277
  16. package/fesm2022/ngx-kit-core.mjs.map +1 -0
  17. package/fesm2022/ngx-kit-data-table.mjs +1 -1
  18. package/fesm2022/ngx-kit-data-table.mjs.map +1 -1
  19. package/fesm2022/ngx-kit-date-picker.mjs +34 -22
  20. package/fesm2022/ngx-kit-date-picker.mjs.map +1 -1
  21. package/fesm2022/ngx-kit-dialog.mjs +430 -331
  22. package/fesm2022/ngx-kit-dialog.mjs.map +1 -1
  23. package/fesm2022/ngx-kit-drag-resize.mjs +193 -140
  24. package/fesm2022/ngx-kit-drag-resize.mjs.map +1 -1
  25. package/fesm2022/ngx-kit-drawer-menu.mjs +3 -3
  26. package/fesm2022/ngx-kit-drawer-menu.mjs.map +1 -1
  27. package/fesm2022/ngx-kit-dropzone.mjs.map +1 -1
  28. package/fesm2022/ngx-kit-form-field.mjs +9 -3
  29. package/fesm2022/ngx-kit-form-field.mjs.map +1 -1
  30. package/fesm2022/ngx-kit-gallery.mjs +3 -3
  31. package/fesm2022/ngx-kit-gallery.mjs.map +1 -1
  32. package/fesm2022/ngx-kit-gradient-picker.mjs +20 -38
  33. package/fesm2022/ngx-kit-gradient-picker.mjs.map +1 -1
  34. package/fesm2022/ngx-kit-grid-layout.mjs +32 -30
  35. package/fesm2022/ngx-kit-grid-layout.mjs.map +1 -1
  36. package/fesm2022/ngx-kit-image-editor.mjs +3 -3
  37. package/fesm2022/ngx-kit-image-editor.mjs.map +1 -1
  38. package/fesm2022/ngx-kit-menu.mjs +1 -1
  39. package/fesm2022/ngx-kit-menu.mjs.map +1 -1
  40. package/fesm2022/ngx-kit-message.mjs +2 -2
  41. package/fesm2022/ngx-kit-message.mjs.map +1 -1
  42. package/fesm2022/ngx-kit-notify.mjs +1 -1
  43. package/fesm2022/ngx-kit-notify.mjs.map +1 -1
  44. package/fesm2022/ngx-kit-time-picker.mjs +1 -1
  45. package/fesm2022/ngx-kit-time-picker.mjs.map +1 -1
  46. package/fesm2022/ngx-kit-virtual-scroll.mjs +2 -2
  47. package/fesm2022/ngx-kit-virtual-scroll.mjs.map +1 -1
  48. package/gallery/README.md +71 -71
  49. package/gradient-picker/README.md +41 -41
  50. package/grid-layout/README.md +64 -64
  51. package/image-editor/README.md +44 -44
  52. package/menu/README.md +85 -85
  53. package/message/README.md +70 -70
  54. package/notify/README.md +56 -56
  55. package/package.json +5 -9
  56. package/time-picker/README.md +89 -89
  57. package/types/ngx-kit-angle-selector.d.ts +13 -16
  58. package/types/ngx-kit-box-shadow.d.ts +37 -95
  59. package/types/ngx-kit-color-picker.d.ts +61 -58
  60. package/types/{ngx-kit-shared.d.ts → ngx-kit-core.d.ts} +194 -34
  61. package/types/ngx-kit-data-table.d.ts +2 -2
  62. package/types/ngx-kit-date-picker.d.ts +15 -12
  63. package/types/ngx-kit-dialog.d.ts +208 -107
  64. package/types/ngx-kit-drag-resize.d.ts +74 -47
  65. package/types/ngx-kit-form-field.d.ts +26 -23
  66. package/types/ngx-kit-gradient-picker.d.ts +4 -7
  67. package/types/ngx-kit-menu.d.ts +1 -1
  68. package/types/ngx-kit-time-picker.d.ts +1 -1
  69. package/virtual-scroll/README.md +199 -199
  70. package/fesm2022/ngx-kit-shared.mjs.map +0 -1
  71. package/fesm2022/ngx-kit-webflow.mjs +0 -709
  72. package/fesm2022/ngx-kit-webflow.mjs.map +0 -1
  73. package/types/ngx-kit-webflow.d.ts +0 -286
@@ -1,709 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { signal, Injectable, computed, inject, ElementRef, Renderer2, NgZone, EventEmitter, HostBinding, Output, Input, Directive, ChangeDetectionStrategy, Component } from '@angular/core';
3
-
4
- /**
5
- * DragDropService
6
- * -------------------------------------------------------------
7
- * وضعیت سراسریِ «چه چیزی الان در حال درگ است» را نگه می‌دارد.
8
- * فقط signal، بدون هیچ Subject/Observable اضافه — سبک و قابل‌پیش‌بینی.
9
- */
10
- class DragDropService {
11
- /** id آیتمی که هم‌اکنون درگ می‌شود، یا null */
12
- activeId = signal(null, /* @ts-ignore */
13
- ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
14
- /** آخرین نتیجهٔ محاسبه‌شدهٔ drop توسط DropPositionService */
15
- pendingDrop = signal(null, /* @ts-ignore */
16
- ...(ngDevMode ? [{ debugName: "pendingDrop" }] : /* istanbul ignore next */ []));
17
- startDrag(id) {
18
- this.activeId.set(id);
19
- this.pendingDrop.set(null);
20
- }
21
- setPendingDrop(result) {
22
- this.pendingDrop.set(result);
23
- }
24
- endDrag() {
25
- const result = this.pendingDrop();
26
- this.activeId.set(null);
27
- this.pendingDrop.set(null);
28
- return result;
29
- }
30
- get isDragging() {
31
- return this.activeId() !== null;
32
- }
33
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DragDropService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
34
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DragDropService, providedIn: 'root' });
35
- }
36
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DragDropService, decorators: [{
37
- type: Injectable,
38
- args: [{ providedIn: 'root' }]
39
- }] });
40
-
41
- /**
42
- * DropPositionService
43
- * -------------------------------------------------------------
44
- * قلب الگوریتمِ «دقیقاً مثل Webflow» اینجاست.
45
- *
46
- * نکتهٔ کلیدی پرفورمنسی: این سرویس هیچ‌وقت روی همهٔ آیتم‌های لیست حلقه نمی‌زند.
47
- * فقط با استفاده از event delegation (یک لیستنر روی کانتینر) و event.target
48
- * دقیقاً همان یک المنتی که ماوس رویش است پیدا می‌شود -> O(1) صرف‌نظر از
49
- * تعداد آیتم‌ها (چه ۱۰ تا باشند چه ۱۰,۰۰۰ تا).
50
- *
51
- * منطق تشخیص zone (همانی که Webflow استفاده می‌کند):
52
- * ۱. جهت چیدمان پدرِ آیتم هدف را می‌خوانیم (row یا column؛ از flex-direction
53
- * یا داده‌شده به‌صورت دستی).
54
- * ۲. موقعیت نسبی ماوس داخل rect آیتم هدف را به درصد تبدیل می‌کنیم.
55
- * ۳. اگر آیتم هدف خودش یک container (قابل‌نست) باشد و ماوس در «هستهٔ
56
- * مرکزی» rect باشد (مثلاً ۲۵٪ تا ۷۵٪ در هر دو محور) -> zone = inside.
57
- * ۴. در غیر این صورت، محور اصلیِ چیدمان (که هم‌ردیفی معنی می‌دهد) با
58
- * نوار نازک لبه (٪ آستانه) بررسی می‌شود:
59
- * - چیدمان row: نزدیک لبهٔ چپ/راست -> before/after (کنار هم)
60
- * نزدیک لبهٔ بالا/پایین -> above/below (ردیف جدید)
61
- * - چیدمان column: برعکسِ بالا
62
- */
63
- const EDGE_THRESHOLD = 0.33; // ۳۳٪ نزدیک هر لبه => درج در همان محور
64
- const NEST_CORE = 0.25; // فاصله از هر لبه برای اینکه «هسته» حساب شود
65
- class DropPositionService {
66
- _indicator = signal({
67
- visible: false,
68
- orientation: null,
69
- x: 0,
70
- y: 0,
71
- length: 0,
72
- highlightRect: null,
73
- }, /* @ts-ignore */
74
- ...(ngDevMode ? [{ debugName: "_indicator" }] : /* istanbul ignore next */ []));
75
- /** استیت فقط‌خواندنیِ اندیکاتور برای بایند در تمپلیت */
76
- indicator = computed(() => this._indicator(), /* @ts-ignore */
77
- ...(ngDevMode ? [{ debugName: "indicator" }] : /* istanbul ignore next */ []));
78
- lastTargetId = null;
79
- lastZone = null;
80
- /**
81
- * فراخوانی در هر pointermove حین درگ.
82
- * targetEl باید همان element.closest('[data-flow-id]') باشد که در
83
- * دایرکتیو کانتینر با event delegation پیدا شده — نه یک لوپ روی آیتم‌ها.
84
- */
85
- evaluate(params) {
86
- const { targetEl, targetId, isContainer, parentAxis, clientX, clientY } = params;
87
- const rect = targetEl.getBoundingClientRect();
88
- const rx = clamp((clientX - rect.left) / rect.width, 0, 1);
89
- const ry = clamp((clientY - rect.top) / rect.height, 0, 1);
90
- let zone;
91
- const insideCore = isContainer &&
92
- rx > NEST_CORE && rx < 1 - NEST_CORE &&
93
- ry > NEST_CORE && ry < 1 - NEST_CORE;
94
- if (insideCore) {
95
- zone = 'inside';
96
- }
97
- else if (parentAxis === 'row') {
98
- if (rx < EDGE_THRESHOLD)
99
- zone = 'before';
100
- else if (rx > 1 - EDGE_THRESHOLD)
101
- zone = 'after';
102
- else
103
- zone = ry < 0.5 ? 'above' : 'below';
104
- }
105
- else {
106
- if (ry < EDGE_THRESHOLD)
107
- zone = 'above';
108
- else if (ry > 1 - EDGE_THRESHOLD)
109
- zone = 'below';
110
- else
111
- zone = rx < 0.5 ? 'before' : 'after';
112
- }
113
- this.lastTargetId = targetId;
114
- this.lastZone = zone;
115
- this.paintIndicator(rect, zone);
116
- return { targetId, zone };
117
- }
118
- /** رسم خط/کادر راهنما - فقط نوشتن به یک signal، بدون لمس بقیهٔ DOM */
119
- paintIndicator(rect, zone) {
120
- const asRect = {
121
- top: rect.top, left: rect.left, width: rect.width, height: rect.height,
122
- right: rect.right, bottom: rect.bottom,
123
- };
124
- if (zone === 'inside') {
125
- this._indicator.set({
126
- visible: true,
127
- orientation: null,
128
- x: 0, y: 0, length: 0,
129
- highlightRect: asRect,
130
- });
131
- return;
132
- }
133
- if (zone === 'above' || zone === 'below') {
134
- this._indicator.set({
135
- visible: true,
136
- orientation: 'h',
137
- x: rect.left,
138
- y: zone === 'above' ? rect.top : rect.bottom,
139
- length: rect.width,
140
- highlightRect: null,
141
- });
142
- }
143
- else {
144
- this._indicator.set({
145
- visible: true,
146
- orientation: 'v',
147
- x: zone === 'before' ? rect.left : rect.right,
148
- y: rect.top,
149
- length: rect.height,
150
- highlightRect: null,
151
- });
152
- }
153
- }
154
- clear() {
155
- this.lastTargetId = null;
156
- this.lastZone = null;
157
- this._indicator.set({
158
- visible: false, orientation: null, x: 0, y: 0, length: 0, highlightRect: null,
159
- });
160
- }
161
- get current() {
162
- if (!this.lastTargetId || !this.lastZone)
163
- return null;
164
- return { targetId: this.lastTargetId, zone: this.lastZone };
165
- }
166
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropPositionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
167
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropPositionService, providedIn: 'root' });
168
- }
169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropPositionService, decorators: [{
170
- type: Injectable,
171
- args: [{ providedIn: 'root' }]
172
- }] });
173
- function clamp(v, min, max) {
174
- return Math.max(min, Math.min(max, v));
175
- }
176
-
177
- /**
178
- * ngxDraggable
179
- * -------------------------------------------------------------
180
- * تنها دایرکتیو مسئول درگ. کل منطق حرکت با CSS transform (translate3d)
181
- * روی خودِ عنصر درگ‌شونده انجام می‌شود — نه با جابجایی واقعی در DOM لیست.
182
- * جابجاییِ واقعیِ داده فقط یک‌بار، در لحظهٔ drop، از طریق خروجی
183
- * (flowDrop) اتفاق می‌افتد و آن‌وقت مصرف‌کننده (کامپوننت کانتینر) با
184
- * *ngFor + trackBy مدل را آپدیت می‌کند.
185
- *
186
- * دلیل مقیاس‌پذیری با هزاران آیتم:
187
- * - در حین pointermove هیچ آیتم دیگری لمس/محاسبه نمی‌شود.
188
- * - listener مربوط به تشخیص هدف روی کانتینر (event delegation) است،
189
- * نه روی تک‌تک آیتم‌ها (این بخش در ContainerDropzoneDirective است).
190
- * - آپدیت transform با requestAnimationFrame throttle می‌شود تا از
191
- * چندبار layout/paint در هر فریم جلوگیری شود.
192
- */
193
- class DraggableDirective {
194
- el = inject((ElementRef)).nativeElement;
195
- renderer = inject(Renderer2);
196
- zone = inject(NgZone);
197
- dragDrop = inject(DragDropService);
198
- dropPos = inject(DropPositionService);
199
- /** شناسهٔ یکتای بلاک — برای شناسایی هنگام درگ لازم است */
200
- ngxDraggableId;
201
- /** غیرفعال کردن موقت درگ (مثلاً حین ادیت متن داخل بلاک) */
202
- ngxDraggableDisabled = false;
203
- /** هندلِ اختصاصی؛ اگر ندهی، کل عنصر هندل می‌شود */
204
- ngxDragHandleSelector = null;
205
- flowDragStart = new EventEmitter();
206
- flowDragEnd = new EventEmitter();
207
- dragging = signal(false, /* @ts-ignore */
208
- ...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
209
- get draggingClass() { return this.dragging(); }
210
- get touchAction() { return 'none'; }
211
- startX = 0;
212
- startY = 0;
213
- originLeft = 0;
214
- originTop = 0;
215
- rafId = null;
216
- pendingTransform = null;
217
- cleanupFns = [];
218
- constructor() {
219
- this.zone.runOutsideAngular(() => {
220
- this.cleanupFns.push(this.renderer.listen(this.el, 'pointerdown', (e) => this.onPointerDown(e)));
221
- });
222
- }
223
- onPointerDown(e) {
224
- if (this.ngxDraggableDisabled)
225
- return;
226
- if (e.button !== 0)
227
- return;
228
- if (this.ngxDragHandleSelector) {
229
- const handle = this.el.querySelector(this.ngxDragHandleSelector);
230
- if (!handle || !handle.contains(e.target))
231
- return;
232
- }
233
- e.preventDefault();
234
- e.stopPropagation();
235
- const rect = this.el.getBoundingClientRect();
236
- this.startX = e.clientX;
237
- this.startY = e.clientY;
238
- this.originLeft = rect.left;
239
- this.originTop = rect.top;
240
- this.el.setPointerCapture(e.pointerId);
241
- this.dragging.set(true);
242
- this.dragDrop.startDrag(this.ngxDraggableId);
243
- this.zone.run(() => {
244
- this.flowDragStart.emit({
245
- id: this.ngxDraggableId,
246
- pointerId: e.pointerId,
247
- originRect: {
248
- top: rect.top, left: rect.left, width: rect.width, height: rect.height,
249
- right: rect.right, bottom: rect.bottom,
250
- },
251
- });
252
- });
253
- this.renderer.addClass(this.el, 'ngx-drag-ghost');
254
- const move = (ev) => this.onPointerMove(ev);
255
- const up = (ev) => this.onPointerUp(ev, move, up);
256
- this.el.addEventListener('pointermove', move);
257
- this.el.addEventListener('pointerup', up);
258
- this.el.addEventListener('pointercancel', up);
259
- }
260
- onPointerMove(e) {
261
- const dx = e.clientX - this.startX;
262
- const dy = e.clientY - this.startY;
263
- this.pendingTransform = { x: dx, y: dy };
264
- if (this.rafId === null) {
265
- this.rafId = requestAnimationFrame(() => {
266
- this.rafId = null;
267
- if (!this.pendingTransform)
268
- return;
269
- // تنها لمسِ DOM در کل چرخهٔ درگ: نوشتن transform روی خودِ عنصر.
270
- this.renderer.setStyle(this.el, 'transform', `translate3d(${this.pendingTransform.x}px, ${this.pendingTransform.y}px, 0)`);
271
- });
272
- }
273
- // نتیجهٔ zone فعلی (اگر کانتینر آن را محاسبه کرده) در سرویس در دسترس است.
274
- }
275
- onPointerUp(e, move, up) {
276
- this.el.removeEventListener('pointermove', move);
277
- this.el.removeEventListener('pointerup', up);
278
- this.el.removeEventListener('pointercancel', up);
279
- if (this.rafId !== null) {
280
- cancelAnimationFrame(this.rafId);
281
- this.rafId = null;
282
- }
283
- this.renderer.removeStyle(this.el, 'transform');
284
- this.renderer.removeClass(this.el, 'ngx-drag-ghost');
285
- this.dragging.set(false);
286
- const result = this.dragDrop.endDrag();
287
- this.dropPos.clear();
288
- this.zone.run(() => {
289
- this.flowDragEnd.emit({ id: this.ngxDraggableId, drop: result });
290
- });
291
- }
292
- ngOnDestroy() {
293
- this.cleanupFns.forEach((fn) => fn());
294
- if (this.rafId !== null)
295
- cancelAnimationFrame(this.rafId);
296
- }
297
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DraggableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
298
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.4", type: DraggableDirective, isStandalone: true, selector: "[ngxDraggable]", inputs: { ngxDraggableId: "ngxDraggableId", ngxDraggableDisabled: "ngxDraggableDisabled", ngxDragHandleSelector: "ngxDragHandleSelector" }, outputs: { flowDragStart: "flowDragStart", flowDragEnd: "flowDragEnd" }, host: { properties: { "class.ngx-dragging": "this.draggingClass", "style.touchAction": "this.touchAction" } }, exportAs: ["ngxDraggable"], ngImport: i0 });
299
- }
300
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DraggableDirective, decorators: [{
301
- type: Directive,
302
- args: [{
303
- selector: '[ngxDraggable]',
304
- standalone: true,
305
- exportAs: 'ngxDraggable',
306
- }]
307
- }], ctorParameters: () => [], propDecorators: { ngxDraggableId: [{
308
- type: Input,
309
- args: [{ required: true }]
310
- }], ngxDraggableDisabled: [{
311
- type: Input
312
- }], ngxDragHandleSelector: [{
313
- type: Input
314
- }], flowDragStart: [{
315
- type: Output
316
- }], flowDragEnd: [{
317
- type: Output
318
- }], draggingClass: [{
319
- type: HostBinding,
320
- args: ['class.ngx-dragging']
321
- }], touchAction: [{
322
- type: HostBinding,
323
- args: ['style.touchAction']
324
- }] } });
325
-
326
- /**
327
- * ngxResizable
328
- * -------------------------------------------------------------
329
- * تنها دایرکتیو مسئول ریسایز. مستقل از DraggableDirective.
330
- * روی عنصر اصلی قرار می‌گیرد و به همراه ۸ هندل (که خودت در تمپلیت با
331
- * class مشخص می‌کنی، مثلاً <span class="rz-handle" data-handle="se">)
332
- * کار می‌کند.
333
- *
334
- * فقط CSS width/height/left/top آپدیت می‌شود (از طریق خروجی flowResize)
335
- * و مصرف‌کننده تصمیم می‌گیرد این مقادیر را چطور به مدل/استایل بایند کند —
336
- * دایرکتیو خودش مستقیماً مدل داده را تغییر نمی‌دهد.
337
- */
338
- class ResizableDirective {
339
- el = inject((ElementRef)).nativeElement;
340
- renderer = inject(Renderer2);
341
- zone = inject(NgZone);
342
- ngxResizableId;
343
- ngxResizableMinWidth = 24;
344
- ngxResizableMinHeight = 24;
345
- ngxResizableDisabled = false;
346
- /** انتخاب‌گر هندل‌ها؛ باید attribute یا data-handle داشته باشند */
347
- ngxResizeHandleSelector = '[data-handle]';
348
- flowResizeStart = new EventEmitter();
349
- flowResizeMove = new EventEmitter();
350
- flowResizeEnd = new EventEmitter();
351
- resizing = signal(false, /* @ts-ignore */
352
- ...(ngDevMode ? [{ debugName: "resizing" }] : /* istanbul ignore next */ []));
353
- startX = 0;
354
- startY = 0;
355
- startW = 0;
356
- startH = 0;
357
- handle = 'se';
358
- rafId = null;
359
- pending = null;
360
- unlisten = null;
361
- constructor() {
362
- this.zone.runOutsideAngular(() => {
363
- this.unlisten = this.renderer.listen(this.el, 'pointerdown', (e) => this.onDown(e));
364
- });
365
- }
366
- onDown(e) {
367
- if (this.ngxResizableDisabled)
368
- return;
369
- const handleEl = e.target?.closest(this.ngxResizeHandleSelector);
370
- if (!handleEl || !this.el.contains(handleEl))
371
- return;
372
- e.preventDefault();
373
- e.stopPropagation();
374
- this.handle = handleEl.getAttribute('data-handle') || 'se';
375
- const rect = this.el.getBoundingClientRect();
376
- this.startX = e.clientX;
377
- this.startY = e.clientY;
378
- this.startW = rect.width;
379
- this.startH = rect.height;
380
- e.target.setPointerCapture(e.pointerId);
381
- this.resizing.set(true);
382
- this.zone.run(() => this.flowResizeStart.emit({ id: this.ngxResizableId, handle: this.handle }));
383
- const move = (ev) => this.onMove(ev);
384
- const up = (ev) => this.onUp(ev, move, up);
385
- handleEl.addEventListener('pointermove', move);
386
- handleEl.addEventListener('pointerup', up);
387
- handleEl.addEventListener('pointercancel', up);
388
- }
389
- onMove(e) {
390
- const dx = e.clientX - this.startX;
391
- const dy = e.clientY - this.startY;
392
- const h = this.handle;
393
- let w = this.startW;
394
- let ht = this.startH;
395
- let ox = 0, oy = 0;
396
- if (h.includes('e'))
397
- w = this.startW + dx;
398
- if (h.includes('w')) {
399
- w = this.startW - dx;
400
- ox = dx;
401
- }
402
- if (h.includes('s'))
403
- ht = this.startH + dy;
404
- if (h.includes('n')) {
405
- ht = this.startH - dy;
406
- oy = dy;
407
- }
408
- w = Math.max(this.ngxResizableMinWidth, w);
409
- ht = Math.max(this.ngxResizableMinHeight, ht);
410
- this.pending = { w, h: ht, ox, oy };
411
- if (this.rafId === null) {
412
- this.rafId = requestAnimationFrame(() => {
413
- this.rafId = null;
414
- if (!this.pending)
415
- return;
416
- this.zone.run(() => {
417
- this.flowResizeMove.emit({
418
- id: this.ngxResizableId, handle: this.handle,
419
- width: this.pending.w, height: this.pending.h,
420
- offsetX: this.pending.ox, offsetY: this.pending.oy,
421
- });
422
- });
423
- });
424
- }
425
- }
426
- onUp(e, move, up) {
427
- const handleEl = e.target;
428
- handleEl.removeEventListener('pointermove', move);
429
- handleEl.removeEventListener('pointerup', up);
430
- handleEl.removeEventListener('pointercancel', up);
431
- if (this.rafId !== null) {
432
- cancelAnimationFrame(this.rafId);
433
- this.rafId = null;
434
- }
435
- this.resizing.set(false);
436
- if (this.pending) {
437
- this.zone.run(() => {
438
- this.flowResizeEnd.emit({
439
- id: this.ngxResizableId, handle: this.handle,
440
- width: this.pending.w, height: this.pending.h,
441
- offsetX: this.pending.ox, offsetY: this.pending.oy,
442
- });
443
- });
444
- }
445
- this.pending = null;
446
- }
447
- ngOnDestroy() {
448
- this.unlisten?.();
449
- if (this.rafId !== null)
450
- cancelAnimationFrame(this.rafId);
451
- }
452
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ResizableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
453
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.4", type: ResizableDirective, isStandalone: true, selector: "[ngxResizable]", inputs: { ngxResizableId: "ngxResizableId", ngxResizableMinWidth: "ngxResizableMinWidth", ngxResizableMinHeight: "ngxResizableMinHeight", ngxResizableDisabled: "ngxResizableDisabled", ngxResizeHandleSelector: "ngxResizeHandleSelector" }, outputs: { flowResizeStart: "flowResizeStart", flowResizeMove: "flowResizeMove", flowResizeEnd: "flowResizeEnd" }, exportAs: ["ngxResizable"], ngImport: i0 });
454
- }
455
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ResizableDirective, decorators: [{
456
- type: Directive,
457
- args: [{
458
- selector: '[ngxResizable]',
459
- standalone: true,
460
- exportAs: 'ngxResizable',
461
- }]
462
- }], ctorParameters: () => [], propDecorators: { ngxResizableId: [{
463
- type: Input,
464
- args: [{ required: true }]
465
- }], ngxResizableMinWidth: [{
466
- type: Input
467
- }], ngxResizableMinHeight: [{
468
- type: Input
469
- }], ngxResizableDisabled: [{
470
- type: Input
471
- }], ngxResizeHandleSelector: [{
472
- type: Input
473
- }], flowResizeStart: [{
474
- type: Output
475
- }], flowResizeMove: [{
476
- type: Output
477
- }], flowResizeEnd: [{
478
- type: Output
479
- }] } });
480
-
481
- /**
482
- * ngxDropzoneContainer
483
- * -------------------------------------------------------------
484
- * روی هر کانتینری که فرزندانش قابل‌درگ‌اند‌دراپ هستند قرار می‌گیرد
485
- * (چه ریشهٔ صفحه، چه یک بلاک تو‌در‌تو).
486
- *
487
- * فقط یک pointermove-listener سراسری (روی document، حین یک درگ فعال)
488
- * دارد و با event.target.closest('[data-flow-id]') هدف را پیدا می‌کند.
489
- * این یعنی صرف‌نظر از اینکه چند هزار فرزند وجود دارد، هزینهٔ هر حرکتِ
490
- * ماوس ثابت (O(1)) است — نه O(n).
491
- *
492
- * هر فرزند مستقیمِ قابل‌درگ باید attribute زیر را داشته باشد
493
- * (به‌صورت خودکار توسط DraggableDirective/تمپلیت ست می‌شود):
494
- * [attr.data-flow-id]="item.id"
495
- * [attr.data-flow-container]="isContainer ? '' : null"
496
- */
497
- class DropzoneContainerDirective {
498
- el = inject((ElementRef)).nativeElement;
499
- zone = inject(NgZone);
500
- renderer = inject(Renderer2);
501
- dragDrop = inject(DragDropService);
502
- dropPos = inject(DropPositionService);
503
- /** جهت چیدمان فرزندان مستقیم این کانتینر */
504
- ngxDropzoneAxis = 'row';
505
- /** آیا خودِ این کانتینر می‌تواند میزبان nest شدن باشد */
506
- ngxDropzoneNestable = true;
507
- unlistenMove = null;
508
- unlistenLeave = null;
509
- ngOnInit() {
510
- this.zone.runOutsideAngular(() => {
511
- this.unlistenMove = this.renderer.listen(this.el, 'pointermove', (e) => {
512
- if (!this.dragDrop.isDragging)
513
- return;
514
- this.handleMove(e);
515
- });
516
- });
517
- }
518
- handleMove(e) {
519
- const targetEl = e.target?.closest('[data-flow-id]');
520
- if (!targetEl || !this.el.contains(targetEl) || targetEl === this.el)
521
- return;
522
- const targetId = targetEl.getAttribute('data-flow-id');
523
- const activeId = this.dragDrop.activeId();
524
- if (!targetId || targetId === activeId)
525
- return; // نمی‌توان روی خودش دراپ کرد
526
- const isContainer = this.ngxDropzoneNestable && targetEl.hasAttribute('data-flow-container');
527
- const result = this.dropPos.evaluate({
528
- targetEl,
529
- targetId,
530
- isContainer,
531
- parentAxis: this.ngxDropzoneAxis,
532
- clientX: e.clientX,
533
- clientY: e.clientY,
534
- });
535
- this.dragDrop.setPendingDrop(result);
536
- }
537
- ngOnDestroy() {
538
- this.unlistenMove?.();
539
- this.unlistenLeave?.();
540
- }
541
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropzoneContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
542
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.4", type: DropzoneContainerDirective, isStandalone: true, selector: "[ngxDropzoneContainer]", inputs: { ngxDropzoneAxis: "ngxDropzoneAxis", ngxDropzoneNestable: "ngxDropzoneNestable" }, exportAs: ["ngxDropzoneContainer"], ngImport: i0 });
543
- }
544
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: DropzoneContainerDirective, decorators: [{
545
- type: Directive,
546
- args: [{
547
- selector: '[ngxDropzoneContainer]',
548
- standalone: true,
549
- exportAs: 'ngxDropzoneContainer',
550
- }]
551
- }], propDecorators: { ngxDropzoneAxis: [{
552
- type: Input
553
- }], ngxDropzoneNestable: [{
554
- type: Input
555
- }] } });
556
-
557
- /**
558
- * FlowDropIndicatorComponent
559
- * -------------------------------------------------------------
560
- * یک‌بار در ریشهٔ صفحه‌ساز قرار می‌گیرد (position: fixed).
561
- * تنها المنتی است که در طول درگ مدام آپدیت می‌شود؛ بقیهٔ آیتم‌های
562
- * لیست دست‌نخورده باقی می‌مانند. کاملاً با computed از signal سرویس
563
- * تغذیه می‌شود، بدون هیچ منطق اضافه در کامپوننت.
564
- */
565
- class FlowDropIndicatorComponent {
566
- dropPos = inject(DropPositionService);
567
- indicator = computed(() => this.dropPos.indicator(), /* @ts-ignore */
568
- ...(ngDevMode ? [{ debugName: "indicator" }] : /* istanbul ignore next */ []));
569
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: FlowDropIndicatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
570
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: FlowDropIndicatorComponent, isStandalone: true, selector: "flow-drop-indicator", ngImport: i0, template: `
571
- @if (indicator().visible) {
572
- @if (indicator().highlightRect; as r) {
573
- <div class="flow-nest-highlight"
574
- [style.top.px]="r.top" [style.left.px]="r.left"
575
- [style.width.px]="r.width" [style.height.px]="r.height">
576
- </div>
577
- } @else if (indicator().orientation === 'h') {
578
- <div class="flow-indicator flow-indicator--h"
579
- [style.top.px]="indicator().y" [style.left.px]="indicator().x"
580
- [style.width.px]="indicator().length">
581
- </div>
582
- } @else if (indicator().orientation === 'v') {
583
- <div class="flow-indicator flow-indicator--v"
584
- [style.top.px]="indicator().y" [style.left.px]="indicator().x"
585
- [style.height.px]="indicator().length">
586
- </div>
587
- }
588
- }
589
- `, isInline: true, styles: [":host{position:fixed;inset:0;pointer-events:none;z-index:9999}.flow-indicator{position:fixed;background:var(--flow-accent, #146ef5);border-radius:2px;pointer-events:none}.flow-indicator--h{height:3px;transform:translateY(-1.5px)}.flow-indicator--v{width:3px;transform:translate(-1.5px)}.flow-nest-highlight{position:fixed;border:2px solid var(--flow-accent, #146ef5);background:color-mix(in srgb,var(--flow-accent, #146ef5) 10%,transparent);border-radius:4px;pointer-events:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
590
- }
591
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: FlowDropIndicatorComponent, decorators: [{
592
- type: Component,
593
- args: [{ selector: 'flow-drop-indicator', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
594
- @if (indicator().visible) {
595
- @if (indicator().highlightRect; as r) {
596
- <div class="flow-nest-highlight"
597
- [style.top.px]="r.top" [style.left.px]="r.left"
598
- [style.width.px]="r.width" [style.height.px]="r.height">
599
- </div>
600
- } @else if (indicator().orientation === 'h') {
601
- <div class="flow-indicator flow-indicator--h"
602
- [style.top.px]="indicator().y" [style.left.px]="indicator().x"
603
- [style.width.px]="indicator().length">
604
- </div>
605
- } @else if (indicator().orientation === 'v') {
606
- <div class="flow-indicator flow-indicator--v"
607
- [style.top.px]="indicator().y" [style.left.px]="indicator().x"
608
- [style.height.px]="indicator().length">
609
- </div>
610
- }
611
- }
612
- `, styles: [":host{position:fixed;inset:0;pointer-events:none;z-index:9999}.flow-indicator{position:fixed;background:var(--flow-accent, #146ef5);border-radius:2px;pointer-events:none}.flow-indicator--h{height:3px;transform:translateY(-1.5px)}.flow-indicator--v{width:3px;transform:translate(-1.5px)}.flow-nest-highlight{position:fixed;border:2px solid var(--flow-accent, #146ef5);background:color-mix(in srgb,var(--flow-accent, #146ef5) 10%,transparent);border-radius:4px;pointer-events:none}\n"] }]
613
- }] });
614
-
615
- /**
616
- * FlowTreeService
617
- * -------------------------------------------------------------
618
- * مدل درختیِ صفحه (دقیقاً همان چیزی که Webflow "Element Panel" اسمش
619
- * می‌گذارد). تغییر مدل فقط در لحظهٔ drop رخ می‌دهد (نه در هر
620
- * pointermove) — به همین دلیل حتی با هزاران بلاک، re-render فقط
621
- * یک‌بار در پایان درگ اتفاق می‌افتد.
622
- *
623
- * پیاده‌سازی immutable-ish: فقط مسیر تغییر‌یافته کپی می‌شود، نه کل
624
- * درخت (structural sharing) تا با درخت‌های بزرگ هم CPU/GC اذیت نشود.
625
- */
626
- class FlowTreeService {
627
- _root = signal({
628
- id: 'root', type: 'root', axis: 'column', isContainer: true, children: [],
629
- }, /* @ts-ignore */
630
- ...(ngDevMode ? [{ debugName: "_root" }] : /* istanbul ignore next */ []));
631
- root = computed(() => this._root(), /* @ts-ignore */
632
- ...(ngDevMode ? [{ debugName: "root" }] : /* istanbul ignore next */ []));
633
- setRoot(node) {
634
- this._root.set(node);
635
- }
636
- applyDrop(draggedId, drop) {
637
- const currentRoot = this._root();
638
- const dragged = findAndRemove(currentRoot, draggedId);
639
- if (!dragged.node)
640
- return;
641
- const afterRemoval = dragged.tree;
642
- const inserted = insertRelativeTo(afterRemoval, drop.targetId, drop.zone, dragged.node);
643
- if (inserted)
644
- this._root.set(inserted);
645
- }
646
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: FlowTreeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
647
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: FlowTreeService, providedIn: 'root' });
648
- }
649
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: FlowTreeService, decorators: [{
650
- type: Injectable,
651
- args: [{ providedIn: 'root' }]
652
- }] });
653
- function findAndRemove(node, id) {
654
- let removed = null;
655
- const idx = node.children.findIndex((c) => c.id === id);
656
- if (idx !== -1) {
657
- removed = node.children[idx];
658
- const children = [...node.children.slice(0, idx), ...node.children.slice(idx + 1)];
659
- return { tree: { ...node, children }, node: removed };
660
- }
661
- let changed = false;
662
- const newChildren = node.children.map((c) => {
663
- if (removed)
664
- return c;
665
- const res = findAndRemove(c, id);
666
- if (res.node) {
667
- removed = res.node;
668
- changed = true;
669
- return res.tree;
670
- }
671
- return c;
672
- });
673
- return { tree: changed ? { ...node, children: newChildren } : node, node: removed };
674
- }
675
- function insertRelativeTo(node, targetId, zone, toInsert) {
676
- const idx = node.children.findIndex((c) => c.id === targetId);
677
- if (idx !== -1) {
678
- if (zone === 'inside') {
679
- const target = node.children[idx];
680
- const updatedTarget = { ...target, children: [...target.children, toInsert] };
681
- const children = [...node.children];
682
- children[idx] = updatedTarget;
683
- return { ...node, children };
684
- }
685
- const insertAt = zone === 'before' || zone === 'above' ? idx : idx + 1;
686
- const children = [...node.children.slice(0, insertAt), toInsert, ...node.children.slice(insertAt)];
687
- return { ...node, children };
688
- }
689
- for (let i = 0; i < node.children.length; i++) {
690
- const res = insertRelativeTo(node.children[i], targetId, zone, toInsert);
691
- if (res) {
692
- const children = [...node.children];
693
- children[i] = res;
694
- return { ...node, children };
695
- }
696
- }
697
- return null;
698
- }
699
-
700
- /**
701
- * ngx-flowbuilder — مدل‌های پایه
702
- */
703
-
704
- /**
705
- * Generated bundle index. Do not edit.
706
- */
707
-
708
- export { DragDropService, DraggableDirective, DropPositionService, DropzoneContainerDirective, FlowDropIndicatorComponent, FlowTreeService, ResizableDirective };
709
- //# sourceMappingURL=ngx-kit-webflow.mjs.map