ngx-kit 6.2.0 → 7.0.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 (71) hide show
  1. package/README.md +3 -3
  2. package/angle-selector/README.md +34 -34
  3. package/box-shadow/README.md +33 -33
  4. package/color-picker/README.md +52 -49
  5. package/data-table/README.md +120 -113
  6. package/date-picker/README.md +74 -69
  7. package/dialog/README.md +92 -92
  8. package/drag-resize/README.md +55 -0
  9. package/drawer-menu/README.md +125 -77
  10. package/dropzone/README.md +422 -422
  11. package/fesm2022/ngx-kit-angle-selector.mjs +2 -2
  12. package/fesm2022/ngx-kit-angle-selector.mjs.map +1 -1
  13. package/fesm2022/ngx-kit-box-shadow.mjs +2 -2
  14. package/fesm2022/ngx-kit-box-shadow.mjs.map +1 -1
  15. package/fesm2022/ngx-kit-color-picker.mjs +265 -73
  16. package/fesm2022/ngx-kit-color-picker.mjs.map +1 -1
  17. package/fesm2022/ngx-kit-data-table.mjs +2 -2
  18. package/fesm2022/ngx-kit-data-table.mjs.map +1 -1
  19. package/fesm2022/ngx-kit-date-picker.mjs +11 -11
  20. package/fesm2022/ngx-kit-date-picker.mjs.map +1 -1
  21. package/fesm2022/ngx-kit-dialog.mjs +3 -3
  22. package/fesm2022/ngx-kit-dialog.mjs.map +1 -1
  23. package/fesm2022/ngx-kit-drag-resize.mjs +1664 -0
  24. package/fesm2022/ngx-kit-drag-resize.mjs.map +1 -0
  25. package/fesm2022/ngx-kit-drawer-menu.mjs +424 -158
  26. package/fesm2022/ngx-kit-drawer-menu.mjs.map +1 -1
  27. package/fesm2022/ngx-kit-dropzone.mjs +2 -2
  28. package/fesm2022/ngx-kit-dropzone.mjs.map +1 -1
  29. package/fesm2022/ngx-kit-form-field.mjs +3 -5
  30. package/fesm2022/ngx-kit-form-field.mjs.map +1 -1
  31. package/fesm2022/ngx-kit-gallery.mjs +2 -2
  32. package/fesm2022/ngx-kit-gallery.mjs.map +1 -1
  33. package/fesm2022/ngx-kit-gradient-picker.mjs.map +1 -1
  34. package/fesm2022/ngx-kit-grid-layout.mjs +927 -0
  35. package/fesm2022/ngx-kit-grid-layout.mjs.map +1 -0
  36. package/fesm2022/ngx-kit-image-editor.mjs +2 -2
  37. package/fesm2022/ngx-kit-image-editor.mjs.map +1 -1
  38. package/fesm2022/ngx-kit-menu.mjs +8 -8
  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 +2 -2
  43. package/fesm2022/ngx-kit-notify.mjs.map +1 -1
  44. package/fesm2022/ngx-kit-shared.mjs +54 -3
  45. package/fesm2022/ngx-kit-shared.mjs.map +1 -1
  46. package/fesm2022/ngx-kit-time-picker.mjs +2 -2
  47. package/fesm2022/ngx-kit-time-picker.mjs.map +1 -1
  48. package/fesm2022/ngx-kit-virtual-scroll.mjs +392 -0
  49. package/fesm2022/ngx-kit-virtual-scroll.mjs.map +1 -0
  50. package/fesm2022/ngx-kit-webflow.mjs +709 -0
  51. package/fesm2022/ngx-kit-webflow.mjs.map +1 -0
  52. package/gallery/README.md +71 -71
  53. package/gradient-picker/README.md +41 -41
  54. package/grid-layout/README.md +64 -0
  55. package/image-editor/README.md +12 -12
  56. package/menu/README.md +85 -85
  57. package/message/README.md +70 -70
  58. package/notify/README.md +56 -58
  59. package/package.json +17 -1
  60. package/shared/README.md +117 -117
  61. package/time-picker/README.md +89 -95
  62. package/types/ngx-kit-color-picker.d.ts +72 -16
  63. package/types/ngx-kit-date-picker.d.ts +2 -1
  64. package/types/ngx-kit-drag-resize.d.ts +368 -0
  65. package/types/ngx-kit-drawer-menu.d.ts +148 -88
  66. package/types/ngx-kit-grid-layout.d.ts +310 -0
  67. package/types/ngx-kit-image-editor.d.ts +1 -1
  68. package/types/ngx-kit-shared.d.ts +12 -1
  69. package/types/ngx-kit-virtual-scroll.d.ts +179 -0
  70. package/types/ngx-kit-webflow.d.ts +286 -0
  71. package/virtual-scroll/README.md +199 -0
@@ -0,0 +1,310 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { AfterViewInit, OnDestroy, OnInit } from '@angular/core';
3
+ import * as i1 from 'ngx-kit/drag-resize';
4
+
5
+ type GridFlow = 'free' | 'horizontal' | 'vertical';
6
+ type CompactMode = 'none' | 'vertical' | 'horizontal' | 'both';
7
+ interface IGridBackgroundCfg {
8
+ show?: 'never' | 'always' | 'whenDragging';
9
+ borderColor?: string;
10
+ gapColor?: string;
11
+ rowColor?: string;
12
+ columnColor?: string;
13
+ borderWidth?: number;
14
+ }
15
+ interface IGridLayoutOptions {
16
+ cols: number;
17
+ rowHeight: number | 'fit';
18
+ gap?: number;
19
+ padding?: number;
20
+ flow?: GridFlow;
21
+ compact?: CompactMode;
22
+ allowOverlap?: boolean;
23
+ pushItems?: boolean;
24
+ /** When two items would swap places, prefer swapping over cascading a push. */
25
+ swap?: boolean;
26
+ dragThreshold?: number;
27
+ animate?: boolean;
28
+ /**
29
+ * `true` / `false` force a direction; `'auto'` (default) reads the host element's
30
+ * computed `direction`, so the grid follows the surrounding page automatically.
31
+ */
32
+ rtl?: boolean | 'auto';
33
+ /** Caps the number of rows the grid can grow to. `undefined` = unbounded. */
34
+ maxRows?: number;
35
+ gridBackgroundConfig?: IGridBackgroundCfg;
36
+ }
37
+ declare class GridLayoutOptions implements IGridLayoutOptions {
38
+ cols: number;
39
+ rowHeight: number | 'fit';
40
+ gap: number;
41
+ padding: number;
42
+ flow: GridFlow;
43
+ compact: CompactMode;
44
+ allowOverlap: boolean;
45
+ pushItems: boolean;
46
+ swap: boolean;
47
+ dragThreshold: number;
48
+ animate: boolean;
49
+ rtl: boolean | "auto";
50
+ maxRows?: number;
51
+ gridBackgroundConfig: IGridBackgroundCfg;
52
+ }
53
+
54
+ declare class GridItemConfig {
55
+ x: number;
56
+ y: number;
57
+ w: number;
58
+ h: number;
59
+ minW?: number;
60
+ minH?: number;
61
+ maxW?: number;
62
+ maxH?: number;
63
+ /** Item can't be dragged or resized, and never moves during compaction/push. */
64
+ static?: boolean;
65
+ /** Overrides the layout-level draggable/resizable flags for this item only. */
66
+ isDraggable?: boolean;
67
+ isResizable?: boolean;
68
+ constructor(x?: number, y?: number, w?: number, h?: number, s?: boolean);
69
+ }
70
+ declare const normalizeGridItem: (value?: Partial<GridItemConfig>) => GridItemConfig;
71
+ /**
72
+ * Value equality for two (already-normalized) configs.
73
+ *
74
+ * This is what breaks the register -> settle -> new-object -> re-register
75
+ * feedback loop: signals compare by *reference*, so anything that recomputes
76
+ * a fresh config object on every change-detection pass (a `computed()` layout,
77
+ * an inline object literal in a template, etc.) will always look "changed" to
78
+ * an `effect()` even when nothing meaningful did. Callers must compare by
79
+ * value before writing back to a signal, otherwise the write itself produces
80
+ * a new reference that trips the same effect again — forever.
81
+ */
82
+ declare function gridItemConfigsEqual(a: GridItemConfig, b: GridItemConfig): boolean;
83
+
84
+ interface LayoutOutput extends GridItemConfig {
85
+ id?: string;
86
+ }
87
+
88
+ interface LayoutNode {
89
+ id: string;
90
+ config: GridItemConfig;
91
+ }
92
+ /** Axis-aligned bounding-box overlap test in grid-cell space. */
93
+ declare function collides(a: GridItemConfig, b: GridItemConfig): boolean;
94
+ /** First item (other than `self`) whose cells overlap `config`, or null. */
95
+ declare function getFirstCollision<T extends LayoutNode>(items: readonly T[], selfId: string, config: GridItemConfig): T | null;
96
+ /** All items whose cells overlap `config`, excluding `selfId`. */
97
+ declare function getAllCollisions<T extends LayoutNode>(items: readonly T[], selfId: string, config: GridItemConfig): T[];
98
+
99
+ interface MoveOptions {
100
+ cols: number;
101
+ compact: CompactMode;
102
+ allowOverlap: boolean;
103
+ maxRows?: number;
104
+ }
105
+ /**
106
+ * Moves `id` to grid cell (x, y) and, unless overlap is explicitly allowed,
107
+ * cascades any resulting collisions by pushing the colliding items out of the
108
+ * way (downward for vertical-flow grids, rightward for horizontal-flow ones).
109
+ * Static items are treated as fixed obstacles: they block, but never move.
110
+ * Pure function — returns a new array, never mutates its input.
111
+ */
112
+ declare function moveItem<T extends LayoutNode>(items: readonly T[], id: string, x: number, y: number, opts: MoveOptions, depth?: number): T[];
113
+ /**
114
+ * When the actively-moved item directly overlaps exactly one same-size item,
115
+ * exchanging their positions reads much better than a cascading push. The
116
+ * caller (service) decides when to offer this vs. falling back to `moveItem`.
117
+ */
118
+ declare function trySwap<T extends LayoutNode>(items: readonly T[], id: string, previous: GridItemConfig, next: GridItemConfig): T[] | null;
119
+ /**
120
+ * Removes gaps by pulling every non-static item as far toward the start of the
121
+ * grid as it can go without overlapping another item, in reading order.
122
+ */
123
+ declare function compact<T extends LayoutNode>(items: readonly T[], mode: CompactMode): T[];
124
+ /** Highest occupied row (0-based, exclusive) across all items — drives the grid's auto-height. */
125
+ declare function maxOccupiedRow(items: readonly LayoutNode[]): number;
126
+
127
+ /**
128
+ * All pixel <-> grid-cell math lives here, in one RTL-aware place.
129
+ *
130
+ * Grid coordinates are always *logical* (reading-order): x = 0 is the first
131
+ * column in the direction text flows — the left edge in LTR, the right edge
132
+ * in RTL. Every physical (CSS `left`/pixel) calculation goes through here so
133
+ * the rest of the codebase never has to think about direction again.
134
+ */
135
+ interface GridMetrics {
136
+ /** Inner content width available for columns (container width minus padding). */
137
+ contentWidth: number;
138
+ colWidth: number;
139
+ rowHeight: number;
140
+ gap: number;
141
+ padding: number;
142
+ }
143
+ declare function computeMetrics(containerWidth: number, options: IGridLayoutOptions): GridMetrics;
144
+ /** Physical CSS `left` (px, relative to the grid surface's content box) for a logical column. */
145
+ declare function colToLeft(colIndex: number, spanCols: number, m: GridMetrics, rtl: boolean): number;
146
+ declare function rowToTop(rowIndex: number, m: GridMetrics): number;
147
+ declare function spanToWidthPx(spanCols: number, m: GridMetrics): number;
148
+ declare function spanToHeightPx(spanRows: number, m: GridMetrics): number;
149
+ /** Physical left px (as above) -> nearest logical column index. Inverse of `colToLeft`. */
150
+ declare function leftToCol(leftPx: number, spanCols: number, m: GridMetrics, rtl: boolean): number;
151
+ declare function topToRow(topPx: number, m: GridMetrics): number;
152
+ /** Places an item's pixel box (left/top/width/height) inside the grid surface, RTL-aware. */
153
+ declare function placeItem(config: GridItemConfig, m: GridMetrics, rtl: boolean): {
154
+ left: number;
155
+ top: number;
156
+ width: number;
157
+ height: number;
158
+ };
159
+
160
+ interface GridItemState {
161
+ id: string;
162
+ config: GridItemConfig;
163
+ element?: HTMLElement;
164
+ dragging?: boolean;
165
+ resizing?: boolean;
166
+ }
167
+ declare class GridLayoutService {
168
+ readonly options: _angular_core.WritableSignal<GridLayoutOptions>;
169
+ readonly items: _angular_core.WritableSignal<readonly GridItemState[]>;
170
+ readonly editMode: _angular_core.WritableSignal<boolean>;
171
+ readonly activeId: _angular_core.WritableSignal<string | null>;
172
+ readonly isInteracting: _angular_core.Signal<boolean>;
173
+ readonly height: _angular_core.Signal<number>;
174
+ readonly layout: _angular_core.Signal<LayoutOutput[]>;
175
+ readonly columns: _angular_core.Signal<number>;
176
+ /** Resolves `rtl: 'auto'` against the attached element's *actual* computed direction. */
177
+ readonly rtl: _angular_core.Signal<boolean>;
178
+ private rtlTick;
179
+ private layoutListener?;
180
+ private startConfig;
181
+ private startItems;
182
+ private previewItems;
183
+ private _element;
184
+ private placeholder?;
185
+ constructor();
186
+ connect(onLayout: (layout: LayoutOutput[]) => void): void;
187
+ setOptions(value: Partial<IGridLayoutOptions>): void;
188
+ setEditMode(value: boolean): void;
189
+ setItems(items: GridItemState[]): void;
190
+ /**
191
+ * Registers a new item, or refreshes the DOM element reference of one that's
192
+ * already tracked. Adding an item can shift the whole layout (compaction /
193
+ * push), so that path settles. Merely re-supplying the same element on an
194
+ * already-known id has no geometric effect, so it is a deliberate no-op —
195
+ * skipping it is what keeps a per-item `effect()` that calls this on every
196
+ * change-detection pass from re-triggering `settle()` indefinitely.
197
+ */
198
+ registerItem(item: GridItemState): void;
199
+ /**
200
+ * Updates an item's config. Only writes the signal and re-settles the layout
201
+ * when the *value* actually changed. Without this guard, any caller that
202
+ * supplies a structurally-equal but referentially-new config object (e.g. a
203
+ * template bound to a `computed()` layout) would cause an unconditional
204
+ * `items.set()` on every run — which produces new output objects, which
205
+ * feed back into that same new config reference, forever.
206
+ */
207
+ updateItemConfig(id: string, config: GridItemConfig): void;
208
+ unregisterItem(id: string): void;
209
+ /** Called once the grid surface element exists (e.g. from `ngAfterViewInit`). */
210
+ attachElement(el: HTMLElement): void;
211
+ /** Recomputes pixel positions after the surface resizes. Call from a `ResizeObserver`. */
212
+ refreshMetrics(): void;
213
+ isItemDraggable(item: GridItemState): boolean;
214
+ isItemResizable(item: GridItemState): boolean;
215
+ begin(id: string): void;
216
+ move(id: string): void;
217
+ resize(id: string, out: {
218
+ width: number;
219
+ height: number;
220
+ moveLeft: number;
221
+ moveTop: number;
222
+ }): void;
223
+ end(id: string): void;
224
+ /** Moves an item by whole grid cells — used by arrow-key handling in the item component. */
225
+ moveByCells(id: string, dCols: number, dRows: number): void;
226
+ /** Resizes an item by whole grid cells — used by shift+arrow handling in the item component. */
227
+ resizeByCells(id: string, dCols: number, dRows: number): void;
228
+ private preview;
229
+ private screenToGrid;
230
+ private screenResizeToGrid;
231
+ private clamp;
232
+ private clampSize;
233
+ /** Applies pushes on drag-end and, when configured, removes gaps via compaction. */
234
+ private settle;
235
+ private applyCss;
236
+ private metrics;
237
+ private resolveRtl;
238
+ private calculateHeight;
239
+ private emit;
240
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<GridLayoutService, never>;
241
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<GridLayoutService>;
242
+ }
243
+
244
+ declare class NgxGridLayoutComponent implements AfterViewInit, OnDestroy {
245
+ readonly options: _angular_core.InputSignal<IGridLayoutOptions>;
246
+ readonly editMode: _angular_core.InputSignal<boolean>;
247
+ readonly layoutChange: _angular_core.OutputEmitterRef<LayoutOutput[]>;
248
+ /** Explicit direction override. Leave unset to inherit the page/host direction automatically. */
249
+ readonly dir: _angular_core.InputSignal<"ltr" | "rtl" | undefined>;
250
+ readonly dirOverride: _angular_core.InputSignal<"ltr" | "rtl" | undefined>;
251
+ readonly service: GridLayoutService;
252
+ private readonly el;
253
+ private surface?;
254
+ private resizeObserver?;
255
+ constructor();
256
+ ngAfterViewInit(): void;
257
+ ngOnDestroy(): void;
258
+ setItems(items: GridItemState[]): void;
259
+ update(options: Partial<IGridLayoutOptions>): void;
260
+ private setCss;
261
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxGridLayoutComponent, never>;
262
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgxGridLayoutComponent, "ngx-grid-layout", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "editMode": { "alias": "editMode"; "required": false; "isSignal": true; }; "dir": { "alias": "dir"; "required": false; "isSignal": true; }; }, { "layoutChange": "layoutChange"; }, never, ["*"], true, never>;
263
+ }
264
+
265
+ declare class NgxGridItemComponent implements OnDestroy, OnInit {
266
+ /** Bound as `[id]` in templates (matches the demo app's existing usage). */
267
+ readonly itemId: _angular_core.InputSignal<string | undefined>;
268
+ readonly config: _angular_core.InputSignal<GridItemConfig>;
269
+ readonly layoutChange: _angular_core.OutputEmitterRef<GridItemConfig>;
270
+ readonly dragging: _angular_core.WritableSignal<boolean>;
271
+ readonly resizing: _angular_core.WritableSignal<boolean>;
272
+ readonly active: _angular_core.Signal<boolean>;
273
+ readonly transitionStyle: _angular_core.Signal<"none" | "left .18s ease, top .18s ease, width .18s ease, height .18s ease">;
274
+ readonly interactive: _angular_core.Signal<boolean>;
275
+ private readonly service;
276
+ private readonly el;
277
+ private readonly drag;
278
+ private readonly resize;
279
+ readonly resizable: _angular_core.Signal<boolean>;
280
+ constructor();
281
+ ngOnInit(): void;
282
+ ngOnDestroy(): void;
283
+ onDragStart: () => void;
284
+ onDragMove: () => void;
285
+ onDragEnd: () => void;
286
+ onResizeStart: () => void;
287
+ onResizeMove: (v: {
288
+ width: number;
289
+ height: number;
290
+ moveLeft: number;
291
+ moveTop: number;
292
+ }) => void;
293
+ onResizeEnd: () => void;
294
+ /** Arrow keys move the item by one cell; Shift+arrow resizes it by one cell. */
295
+ onKeyDown(e: KeyboardEvent): void;
296
+ private finishInteraction;
297
+ /** Converts the item's min/max cell counts (config) into px for the resize directive. */
298
+ private applySizeConstraints;
299
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxGridItemComponent, never>;
300
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgxGridItemComponent, "ngx-grid-item", never, { "itemId": { "alias": "id"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; }, { "layoutChange": "layoutChange"; }, never, ["*"], true, [{ directive: typeof i1.NgxDraggable; inputs: { "dragHandle": "dragHandle"; "lockAxis": "lockAxis"; }; outputs: {}; }, { directive: typeof i1.NgxResizable; inputs: {}; outputs: {}; }]>;
301
+ }
302
+
303
+ declare class NgxGridLayoutModule {
304
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxGridLayoutModule, never>;
305
+ static ɵmod: _angular_core.ɵɵNgModuleDeclaration<NgxGridLayoutModule, never, [typeof NgxGridLayoutComponent, typeof NgxGridItemComponent], [typeof NgxGridLayoutComponent, typeof NgxGridItemComponent]>;
306
+ static ɵinj: _angular_core.ɵɵInjectorDeclaration<NgxGridLayoutModule>;
307
+ }
308
+
309
+ export { GridItemConfig, GridLayoutOptions, GridLayoutService, NgxGridItemComponent, NgxGridLayoutComponent, NgxGridLayoutModule, colToLeft, collides, compact, computeMetrics, getAllCollisions, getFirstCollision, gridItemConfigsEqual, leftToCol, maxOccupiedRow, moveItem, normalizeGridItem, placeItem, rowToTop, spanToHeightPx, spanToWidthPx, topToRow, trySwap };
310
+ export type { CompactMode, GridFlow, GridItemState, GridMetrics, IGridBackgroundCfg, IGridLayoutOptions, LayoutNode, LayoutOutput, MoveOptions };
@@ -96,7 +96,7 @@ declare const DEFAULT_NGX_IMAGE_EDITOR_LOCALIZATION: NgxImageEditorLocalization;
96
96
  * شدن‌شون می‌شد).
97
97
  */
98
98
  declare class NgxImageEditorComponent {
99
- source: _angular_core.InputSignal<string | File | Blob | null>;
99
+ source: _angular_core.InputSignal<string | Blob | File | null>;
100
100
  /** قفل نسبتِ کراپ (مثلاً ۱ برای آواتار مربعی)؛ undefined یعنی آزاد */
101
101
  aspectRatio: _angular_core.InputSignal<number | undefined>;
102
102
  outputMaxWidth: _angular_core.InputSignal<number | undefined>;
@@ -210,6 +210,17 @@ declare function randomStrnig(extraLength?: number, useNumeric?: boolean): strin
210
210
 
211
211
  declare function makeConfetti(doc: Document, wrap: HTMLElement): void;
212
212
 
213
+ declare function generateSequentialGuid(): string;
214
+
215
+ /**
216
+ * Performs a deep merge of objects and returns new object. Does not modify
217
+ * objects (immutable) and merges arrays via concatenation.
218
+ *
219
+ * @param {...object} objects - Objects to merge
220
+ * @returns {object} New object with merged key/values
221
+ */
222
+ declare function mergeDeep(...objects: any[]): any;
223
+
213
224
  declare class BrowserService {
214
225
  private platformId;
215
226
  constructor(platformId: object);
@@ -331,5 +342,5 @@ interface OverlayInstance {
331
342
  cleanup: () => void;
332
343
  }
333
344
 
334
- export { BrowserService, DIALOG_OVERLAY_CLASSNAME, DirectionService, FOCUSABLE_SELECTOR, OverlayRef, OverlayService, RangeSliderComponent, SaturationComponent, SliderComponent, ValueModel, WINDOW, getOffsetPosition, makeConfetti, mergeConfig, randomStrnig, startDragSession };
345
+ export { BrowserService, DIALOG_OVERLAY_CLASSNAME, DirectionService, FOCUSABLE_SELECTOR, OverlayRef, OverlayService, RangeSliderComponent, SaturationComponent, SliderComponent, ValueModel, WINDOW, generateSequentialGuid, getOffsetPosition, makeConfetti, mergeConfig, mergeDeep, randomStrnig, startDragSession };
335
346
  export type { Alignment, BaseOverlayOptions, Direction, DragSessionCallbacks, IPosition, IValue, OverlayInstance, OverlayOptions, Placement, Point, TemplateOptions };
@@ -0,0 +1,179 @@
1
+ import * as _angular_core from '@angular/core';
2
+
3
+ /**
4
+ * A "line" index range that is currently rendered.
5
+ * A "line" is one item for a plain list, or one row/column of N items
6
+ * for an auto-detected wrapping grid. `end` is exclusive.
7
+ */
8
+ interface NgxVirtualScrollRange {
9
+ start: number;
10
+ end: number;
11
+ }
12
+ declare function rangesEqual(a: NgxVirtualScrollRange, b: NgxVirtualScrollRange): boolean;
13
+
14
+ /**
15
+ * The scroll axis along which content is virtualized.
16
+ * This is never provided by the consumer — it is always derived from the
17
+ * real, rendered layout of the projected content (see `measureLayout`).
18
+ */
19
+ type NgxVirtualScrollAxis = 'vertical' | 'horizontal';
20
+ /**
21
+ * Result of measuring the real DOM layout of a small probe batch of items.
22
+ *
23
+ * - `crossCount === 1` -> a plain list (the common case: vertical stack of
24
+ * rows, or a horizontal row of columns).
25
+ * - `crossCount > 1` -> a wrapping grid (e.g. cards laid out with
26
+ * `display: flex; flex-wrap: wrap`): `crossCount` items share a line
27
+ * before wrapping to the next one. The grid still scrolls along a single
28
+ * `axis`, while the cross axis lays out natively (this is how "mixed"
29
+ * horizontal + vertical layouts are supported without a 2D data model).
30
+ */
31
+ interface NgxVirtualScrollMeasurement {
32
+ axis: NgxVirtualScrollAxis;
33
+ crossCount: number;
34
+ /** Size (px) of one line along `axis`, including margins/border/shadow. */
35
+ lineSize: number;
36
+ }
37
+ /** Escape hatch for consumers who need full control over layout detection. */
38
+ type NgxVirtualScrollMeasureFn = (probeElements: readonly Element[]) => NgxVirtualScrollMeasurement | null;
39
+
40
+ /**
41
+ * Headless, signal-based virtual scroll viewport.
42
+ *
43
+ * There is no `itemSize` and no `orientation` input by design: both are
44
+ * derived by measuring the real, rendered bounding boxes of a small probe
45
+ * batch (see `measure-layout.ts`), so margins/border/box-shadow on your own
46
+ * item styles are always accounted for correctly, and mixed layouts
47
+ * (vertical list of wrapping rows, i.e. a card grid) are supported out of
48
+ * the box.
49
+ *
50
+ * The component does not render your items itself — it exposes reactive
51
+ * signals (`visibleItems`, `range`, `baseIndex`, `axis`, ...) that you
52
+ * consume with a native `@for` block in your own template. This keeps the
53
+ * repeat/track logic entirely under your control (custom track expressions,
54
+ * `@empty` blocks, etc.) instead of hiding it behind a structural directive.
55
+ *
56
+ * @example
57
+ * ```html
58
+ * <ngx-virtual-scroll-viewport #vs="ngxVirtualScrollViewport" [items]="rows" style="height: 480px">
59
+ * @for (row of vs.visibleItems(); track row.id; let i = $index) {
60
+ * <div class="row">{{ vs.baseIndex() + i }} - {{ row.name }}</div>
61
+ * }
62
+ * </ngx-virtual-scroll-viewport>
63
+ * ```
64
+ */
65
+ declare class NgxVirtualScrollViewport<T = unknown> {
66
+ /** The full, unvirtualized data set. */
67
+ readonly items: _angular_core.InputSignal<readonly T[]>;
68
+ /** Minimum buffer (px) left before the render range is recomputed. */
69
+ readonly minBufferPx: _angular_core.InputSignal<number>;
70
+ /** Extra buffer (px) rendered outside the visible area on each side. */
71
+ readonly maxBufferPx: _angular_core.InputSignal<number>;
72
+ /** Advanced escape hatch: replace the built-in layout probe entirely. */
73
+ readonly measureFn: _angular_core.InputSignal<NgxVirtualScrollMeasureFn | undefined>;
74
+ /**
75
+ * Safe ceiling (px) for the scrollable axis's real DOM size. Only relevant
76
+ * for very large data sets — see `DEFAULT_MAX_AXIS_SIZE_PX`. You should
77
+ * not need to change this; it's exposed only for advanced cases (e.g. you
78
+ * know your app never targets older WebKit and want to raise the ceiling).
79
+ */
80
+ readonly maxAxisSizePx: _angular_core.InputSignal<number>;
81
+ /** Emits the index of the first fully-visible item as the user scrolls. */
82
+ readonly scrolledIndexChange: _angular_core.OutputEmitterRef<number>;
83
+ /** Emits whenever the rendered line range changes. */
84
+ readonly rangeChange: _angular_core.OutputEmitterRef<NgxVirtualScrollRange>;
85
+ private readonly scrollableRef;
86
+ private readonly wrapperRef;
87
+ private readonly destroyRef;
88
+ private readonly measurement;
89
+ private readonly scrollOffset;
90
+ private readonly viewportSize;
91
+ /** Whether the user is actively scrolling (used only to toggle `will-change`). */
92
+ readonly isScrolling: _angular_core.WritableSignal<boolean>;
93
+ /** Currently rendered line range. */
94
+ readonly range: _angular_core.WritableSignal<NgxVirtualScrollRange>;
95
+ readonly axis: _angular_core.Signal<NgxVirtualScrollAxis>;
96
+ readonly crossCount: _angular_core.Signal<number>;
97
+ readonly lineSize: _angular_core.Signal<number>;
98
+ readonly isMeasured: _angular_core.Signal<boolean>;
99
+ readonly lineCount: _angular_core.Signal<number>;
100
+ /** Real, uncompressed total size along the scroll axis — can exceed the browser's element-size cap. */
101
+ readonly naturalTotalSize: _angular_core.Signal<number>;
102
+ /**
103
+ * Size actually applied to the spacer element, clamped to `maxAxisSizePx`.
104
+ * Equal to `naturalTotalSize()` for any data set small enough to matter in
105
+ * practice — the compression only ever engages for extreme list sizes.
106
+ */
107
+ readonly totalSize: _angular_core.Signal<number>;
108
+ /**
109
+ * Ratio between the (possibly capped) DOM scroll space and the real,
110
+ * uncompressed content size. `1` unless `naturalTotalSize()` exceeds
111
+ * `maxAxisSizePx()`, in which case it's `< 1` and the DOM scrollbar
112
+ * represents the whole data set in a compressed coordinate space.
113
+ */
114
+ readonly scaleFactor: _angular_core.Signal<number>;
115
+ /** Absolute index of the first item in `visibleItems()`, for display purposes. */
116
+ readonly baseIndex: _angular_core.Signal<number>;
117
+ /** Slice of `items()` that should actually be rendered right now. */
118
+ readonly visibleItems: _angular_core.Signal<readonly T[]>;
119
+ private readonly renderedBlockSize;
120
+ private readonly contentOffset;
121
+ protected readonly wrapperTransform: _angular_core.Signal<string>;
122
+ protected readonly spacerWidth: _angular_core.Signal<number>;
123
+ protected readonly spacerHeight: _angular_core.Signal<number>;
124
+ private resizeObserver?;
125
+ private scrollingIdleTimer?;
126
+ private scrollRaf;
127
+ constructor();
128
+ /** Programmatically scroll to a given item index. */
129
+ scrollToIndex(index: number, behavior?: ScrollBehavior): void;
130
+ /**
131
+ * Programmatically scroll to a given pixel offset along the scroll axis.
132
+ * `offset` is expressed in real, uncompressed content px (i.e. the same
133
+ * space as `index * lineSize()`) — internally converted to the
134
+ * (possibly compressed) DOM scroll coordinate via `scaleFactor()`.
135
+ */
136
+ scrollToOffset(offset: number, behavior?: ScrollBehavior): void;
137
+ /** Force a re-measure, e.g. after the container was resized by external layout logic. */
138
+ checkViewportSize(): void;
139
+ private setupListeners;
140
+ private readonly onScroll;
141
+ private markScrolling;
142
+ private measureViewportSize;
143
+ /**
144
+ * Recomputes the rendered line range. Mirrors the CDK "fixed size"
145
+ * strategy: only recompute once the scroll offset gets within
146
+ * `minBufferPx` of the current range's edge, instead of on every pixel.
147
+ *
148
+ * All math here happens in *real* (uncompressed) content px, obtained by
149
+ * dividing the raw DOM scroll offset/viewport size by `scaleFactor()`.
150
+ * This is what makes the very last items reachable even when the total
151
+ * content size has been compressed into a browser-safe DOM range: as
152
+ * `domScrollOffset` sweeps its full (capped) range, `realOffset` still
153
+ * sweeps the entire, real `naturalTotalSize()`.
154
+ */
155
+ private recomputeRange;
156
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxVirtualScrollViewport<any>, never>;
157
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgxVirtualScrollViewport<any>, "ngx-virtual-scroll-viewport, ngx-virtual-scroll", ["ngxVirtualScrollViewport"], { "items": { "alias": "items"; "required": false; "isSignal": true; }; "minBufferPx": { "alias": "minBufferPx"; "required": false; "isSignal": true; }; "maxBufferPx": { "alias": "maxBufferPx"; "required": false; "isSignal": true; }; "measureFn": { "alias": "measureFn"; "required": false; "isSignal": true; }; "maxAxisSizePx": { "alias": "maxAxisSizePx"; "required": false; "isSignal": true; }; }, { "scrolledIndexChange": "scrolledIndexChange"; "rangeChange": "rangeChange"; }, never, ["*"], true, never>;
158
+ }
159
+
160
+ /**
161
+ * Pure, framework-agnostic layout probe.
162
+ *
163
+ * Reads the real bounding boxes of a small batch of already-rendered
164
+ * elements and infers, purely from actual layout:
165
+ * - the scroll axis (vertical vs horizontal)
166
+ * - how many items share a single line (1 for a plain list, N for a
167
+ * wrapping grid)
168
+ * - the real size of one line, INCLUDING margins/border/box-shadow,
169
+ * because it is derived from the delta between two real sibling
170
+ * positions in normal document flow rather than from a hardcoded
171
+ * itemSize input.
172
+ *
173
+ * No orientation/itemSize is ever supplied by the consumer — this is the
174
+ * single source of truth for both.
175
+ */
176
+ declare function measureLayout(probeElements: readonly Element[]): NgxVirtualScrollMeasurement | null;
177
+
178
+ export { NgxVirtualScrollViewport, measureLayout, rangesEqual };
179
+ export type { NgxVirtualScrollAxis, NgxVirtualScrollMeasureFn, NgxVirtualScrollMeasurement, NgxVirtualScrollRange };