@ngneers/controls 0.0.1-next.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 (186) hide show
  1. package/fesm2022/ngneers-controls-accordion.mjs +234 -0
  2. package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
  3. package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
  4. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
  5. package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
  6. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-api.mjs +347 -0
  8. package/fesm2022/ngneers-controls-api.mjs.map +1 -0
  9. package/fesm2022/ngneers-controls-avatar.mjs +77 -0
  10. package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
  11. package/fesm2022/ngneers-controls-base.mjs +620 -0
  12. package/fesm2022/ngneers-controls-base.mjs.map +1 -0
  13. package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
  14. package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
  15. package/fesm2022/ngneers-controls-button-group.mjs +110 -0
  16. package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-button.mjs +83 -0
  18. package/fesm2022/ngneers-controls-button.mjs.map +1 -0
  19. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
  20. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
  21. package/fesm2022/ngneers-controls-calendar.mjs +801 -0
  22. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
  23. package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
  24. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
  25. package/fesm2022/ngneers-controls-chip.mjs +55 -0
  26. package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
  27. package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
  28. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
  29. package/fesm2022/ngneers-controls-defer.mjs +72 -0
  30. package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
  31. package/fesm2022/ngneers-controls-dialog.mjs +407 -0
  32. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
  33. package/fesm2022/ngneers-controls-directives.mjs +498 -0
  34. package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
  35. package/fesm2022/ngneers-controls-drawer.mjs +271 -0
  36. package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
  37. package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
  38. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
  39. package/fesm2022/ngneers-controls-errors.mjs +329 -0
  40. package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
  41. package/fesm2022/ngneers-controls-filter.mjs +708 -0
  42. package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
  43. package/fesm2022/ngneers-controls-hint.mjs +153 -0
  44. package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
  45. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
  46. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
  48. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
  49. package/fesm2022/ngneers-controls-i18n.mjs +57 -0
  50. package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
  51. package/fesm2022/ngneers-controls-icon.mjs +195 -0
  52. package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-inplace.mjs +150 -0
  54. package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
  55. package/fesm2022/ngneers-controls-input-field.mjs +165 -0
  56. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
  57. package/fesm2022/ngneers-controls-input.mjs +72 -0
  58. package/fesm2022/ngneers-controls-input.mjs.map +1 -0
  59. package/fesm2022/ngneers-controls-item-view.mjs +473 -0
  60. package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
  61. package/fesm2022/ngneers-controls-list-box.mjs +281 -0
  62. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
  63. package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
  64. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
  65. package/fesm2022/ngneers-controls-menu.mjs +371 -0
  66. package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-message.mjs +30 -0
  68. package/fesm2022/ngneers-controls-message.mjs.map +1 -0
  69. package/fesm2022/ngneers-controls-number-input.mjs +366 -0
  70. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
  71. package/fesm2022/ngneers-controls-paginator.mjs +129 -0
  72. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
  73. package/fesm2022/ngneers-controls-popover.mjs +215 -0
  74. package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
  75. package/fesm2022/ngneers-controls-progress.mjs +98 -0
  76. package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
  77. package/fesm2022/ngneers-controls-radio.mjs +152 -0
  78. package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
  79. package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
  80. package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
  81. package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
  82. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
  83. package/fesm2022/ngneers-controls-scroller.mjs +308 -0
  84. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
  85. package/fesm2022/ngneers-controls-select-button.mjs +58 -0
  86. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
  87. package/fesm2022/ngneers-controls-select.mjs +417 -0
  88. package/fesm2022/ngneers-controls-select.mjs.map +1 -0
  89. package/fesm2022/ngneers-controls-slider.mjs +167 -0
  90. package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
  91. package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
  92. package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
  93. package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
  94. package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
  95. package/fesm2022/ngneers-controls-spinner.mjs +184 -0
  96. package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
  97. package/fesm2022/ngneers-controls-splitter.mjs +604 -0
  98. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
  99. package/fesm2022/ngneers-controls-state.mjs +185 -0
  100. package/fesm2022/ngneers-controls-state.mjs.map +1 -0
  101. package/fesm2022/ngneers-controls-switch.mjs +39 -0
  102. package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
  103. package/fesm2022/ngneers-controls-table.mjs +2634 -0
  104. package/fesm2022/ngneers-controls-table.mjs.map +1 -0
  105. package/fesm2022/ngneers-controls-tabs.mjs +336 -0
  106. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
  107. package/fesm2022/ngneers-controls-tag.mjs +30 -0
  108. package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
  109. package/fesm2022/ngneers-controls-toast.mjs +350 -0
  110. package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
  111. package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
  112. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
  113. package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
  114. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
  115. package/fesm2022/ngneers-controls-tree.mjs +599 -0
  116. package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
  117. package/fesm2022/ngneers-controls-upload.mjs +377 -0
  118. package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
  119. package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
  120. package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
  121. package/fesm2022/ngneers-controls-utils.mjs +261 -0
  122. package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
  123. package/fesm2022/ngneers-controls.mjs +6 -0
  124. package/fesm2022/ngneers-controls.mjs.map +1 -0
  125. package/package.json +302 -0
  126. package/types/ngneers-controls-accordion.d.ts +139 -0
  127. package/types/ngneers-controls-api-ng.d.ts +364 -0
  128. package/types/ngneers-controls-api-resize.d.ts +318 -0
  129. package/types/ngneers-controls-api.d.ts +145 -0
  130. package/types/ngneers-controls-avatar.d.ts +54 -0
  131. package/types/ngneers-controls-base.d.ts +277 -0
  132. package/types/ngneers-controls-breadcrumb.d.ts +272 -0
  133. package/types/ngneers-controls-button-group.d.ts +46 -0
  134. package/types/ngneers-controls-button.d.ts +48 -0
  135. package/types/ngneers-controls-calendar.d.ts +466 -0
  136. package/types/ngneers-controls-checkbox.d.ts +40 -0
  137. package/types/ngneers-controls-chip.d.ts +202 -0
  138. package/types/ngneers-controls-default-icons.d.ts +5 -0
  139. package/types/ngneers-controls-defer.d.ts +48 -0
  140. package/types/ngneers-controls-dialog.d.ts +399 -0
  141. package/types/ngneers-controls-directives.d.ts +171 -0
  142. package/types/ngneers-controls-drawer.d.ts +308 -0
  143. package/types/ngneers-controls-edit-inplace.d.ts +263 -0
  144. package/types/ngneers-controls-errors.d.ts +155 -0
  145. package/types/ngneers-controls-filter.d.ts +609 -0
  146. package/types/ngneers-controls-hint.d.ts +88 -0
  147. package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
  148. package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
  149. package/types/ngneers-controls-i18n.d.ts +186 -0
  150. package/types/ngneers-controls-icon.d.ts +68 -0
  151. package/types/ngneers-controls-inplace.d.ts +98 -0
  152. package/types/ngneers-controls-input-field.d.ts +248 -0
  153. package/types/ngneers-controls-input.d.ts +30 -0
  154. package/types/ngneers-controls-item-view.d.ts +215 -0
  155. package/types/ngneers-controls-list-box.d.ts +334 -0
  156. package/types/ngneers-controls-mask-input.d.ts +434 -0
  157. package/types/ngneers-controls-menu.d.ts +192 -0
  158. package/types/ngneers-controls-message.d.ts +21 -0
  159. package/types/ngneers-controls-number-input.d.ts +146 -0
  160. package/types/ngneers-controls-paginator.d.ts +255 -0
  161. package/types/ngneers-controls-popover.d.ts +116 -0
  162. package/types/ngneers-controls-progress.d.ts +49 -0
  163. package/types/ngneers-controls-radio.d.ts +91 -0
  164. package/types/ngneers-controls-roving-focus.d.ts +107 -0
  165. package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
  166. package/types/ngneers-controls-scroller.d.ts +151 -0
  167. package/types/ngneers-controls-select-button.d.ts +51 -0
  168. package/types/ngneers-controls-select.d.ts +409 -0
  169. package/types/ngneers-controls-slider.d.ts +57 -0
  170. package/types/ngneers-controls-snackbar.d.ts +330 -0
  171. package/types/ngneers-controls-spin-buttons.d.ts +69 -0
  172. package/types/ngneers-controls-spinner.d.ts +117 -0
  173. package/types/ngneers-controls-splitter.d.ts +465 -0
  174. package/types/ngneers-controls-state.d.ts +102 -0
  175. package/types/ngneers-controls-switch.d.ts +18 -0
  176. package/types/ngneers-controls-table.d.ts +3127 -0
  177. package/types/ngneers-controls-tabs.d.ts +118 -0
  178. package/types/ngneers-controls-tag.d.ts +21 -0
  179. package/types/ngneers-controls-toast.d.ts +298 -0
  180. package/types/ngneers-controls-toggle-button.d.ts +48 -0
  181. package/types/ngneers-controls-tooltip.d.ts +189 -0
  182. package/types/ngneers-controls-tree.d.ts +409 -0
  183. package/types/ngneers-controls-upload.d.ts +347 -0
  184. package/types/ngneers-controls-utils-ng.d.ts +129 -0
  185. package/types/ngneers-controls-utils.d.ts +130 -0
  186. package/types/ngneers-controls.d.ts +2 -0
@@ -0,0 +1,712 @@
1
+ import { signal, computed, afterRenderEffect, untracked } from '@angular/core';
2
+
3
+ // --- Size parsing ---
4
+ function isResizeSize(value) {
5
+ if (typeof value !== 'string')
6
+ return false;
7
+ const m = value.match(/^(.+)(px|fr|%)$/);
8
+ if (!m || !m[1])
9
+ return false;
10
+ if (isNaN(parseFloat(m[1])))
11
+ return false;
12
+ return true;
13
+ }
14
+ function getResizeSizeUnit(value) {
15
+ if (value.endsWith('px'))
16
+ return 'px';
17
+ if (value.endsWith('fr'))
18
+ return 'fr';
19
+ return '%';
20
+ }
21
+ function getResizeSizeValue(value) {
22
+ const unit = getResizeSizeUnit(value);
23
+ return parseFloat(value.slice(0, -unit.length));
24
+ }
25
+ function expandResizeSize(value) {
26
+ return {
27
+ value: getResizeSizeValue(value),
28
+ unit: getResizeSizeUnit(value),
29
+ };
30
+ }
31
+ function collapseResizeSize(size) {
32
+ return `${size.value}${size.unit}`;
33
+ }
34
+ // --- Limit parsing ---
35
+ function isResizeLimit(value) {
36
+ if (typeof value !== 'string')
37
+ return false;
38
+ const m = value.match(/^(.+)(px|%)$/);
39
+ if (!m || !m[1])
40
+ return false;
41
+ if (isNaN(parseFloat(m[1])))
42
+ return false;
43
+ return true;
44
+ }
45
+ function getResizeLimitUnit(value) {
46
+ return value.endsWith('px') ? 'px' : '%';
47
+ }
48
+ function getResizeLimitValue(value) {
49
+ return value.endsWith('px') ? parseFloat(value.slice(0, -2)) : parseFloat(value.slice(0, -1));
50
+ }
51
+ function getResizeLimitInPx(value, containerSize) {
52
+ const unit = getResizeLimitUnit(value);
53
+ const sizeValue = getResizeLimitValue(value);
54
+ return unit === 'px' ? sizeValue : (sizeValue / 100) * containerSize;
55
+ }
56
+ function expandResizeLimit(value) {
57
+ return {
58
+ value: getResizeLimitValue(value),
59
+ unit: getResizeLimitUnit(value),
60
+ };
61
+ }
62
+ function collapseResizeLimit(size) {
63
+ return `${size.value}${size.unit}`;
64
+ }
65
+ // --- Pixel resolution ---
66
+ /**
67
+ * Resolves a size to its pixel equivalent given the current fraction factors and container size.
68
+ */
69
+ function resolveSizeToPx(size, pxPerFr, containerSize) {
70
+ switch (size.unit) {
71
+ case 'px':
72
+ return size.value;
73
+ case 'fr':
74
+ return pxPerFr * size.value;
75
+ case '%':
76
+ return (size.value / 100) * containerSize;
77
+ }
78
+ }
79
+ /**
80
+ * Converts a pixel delta to the appropriate value delta for the given unit.
81
+ */
82
+ function pxDeltaToUnitDelta(pxDelta, unit, frPerPx, percentPerPx) {
83
+ switch (unit) {
84
+ case 'px':
85
+ return pxDelta;
86
+ case 'fr':
87
+ return pxDelta * frPerPx;
88
+ case '%':
89
+ return pxDelta * percentPerPx;
90
+ }
91
+ }
92
+
93
+ const LAST_CALC_SIZE_SYMBOL = Symbol('lastCalcSize');
94
+ /**
95
+ * A generic, control-agnostic resize engine for CSS grid layouts.
96
+ *
97
+ * Handles drag-based resizing of a list of {@link ResizableItem}s with support for:
98
+ * - **Size units**: `px`, `fr`, `%`
99
+ * - **Distribution modes**:
100
+ * - `adjacent` — transfers space between the two items neighbouring the divider (zero-sum).
101
+ * - `proportional` — only the dragged item changes to `px`; other items keep their `fr`/`%`
102
+ * units and CSS grid redistributes the remaining space. Optionally locks the resized item
103
+ * to `px` via {@link ResizeEngineConfig.lockSizes}.
104
+ * - `push` — the dragged item grows/shrinks independently; the container total changes
105
+ * (requires `containerConstrained = false`).
106
+ * - **Commit modes** (for `adjacent`/`push`): `preserve`, `bakeAll`, `bakeAffected`
107
+ * - **Min-size floor** ({@link ResizeEngineConfig.minItemSizePx}): an absolute `px` floor
108
+ * enforced even when a relative min-size resolves smaller. In `proportional` mode, emitted
109
+ * as CSS `minmax(floor, size)` so the grid natively prevents columns from shrinking below it.
110
+ *
111
+ * Consumers (splitter, table) provide items and configuration via signals.
112
+ * The engine outputs a ready-to-use `grid-template-columns`/`grid-template-rows` string
113
+ * via {@link gridTemplateSizes}.
114
+ */
115
+ class ResizeEngine {
116
+ items;
117
+ containerSize;
118
+ gapSizes;
119
+ distributionMode;
120
+ containerConstrained;
121
+ lockSizes;
122
+ minItemSizePx;
123
+ resolveItemSizes;
124
+ constructor(config) {
125
+ this.items = config.items;
126
+ this.containerSize = config.containerSize;
127
+ this.gapSizes = config.gapSizes;
128
+ this.distributionMode = config.distributionMode;
129
+ this.containerConstrained = config.containerConstrained;
130
+ this.lockSizes = config.lockSizes ?? signal(false);
131
+ this.minItemSizePx = config.minItemSizePx ?? 0;
132
+ this.resolveItemSizes = config.resolveItemSizes;
133
+ // Signal fires when any item size is not calculated (external change detected)
134
+ const hasUncalculatedSizes = computed(() => this.items().some(item => !this.isItemSizeCalculated(item)), { equal: (_, b) => b === false });
135
+ // Ensure min/max limits are respected on external changes and container resize
136
+ afterRenderEffect(() => {
137
+ hasUncalculatedSizes();
138
+ this.containerSize();
139
+ untracked(() => {
140
+ // Skip during active drag — sizes are managed by the drag handler
141
+ if (this.dragContext())
142
+ return;
143
+ this.ensureMinMaxSizes();
144
+ });
145
+ });
146
+ }
147
+ // --- Public signals ---
148
+ /** The active drag state, or `null` when idle. */
149
+ dragContext = signal(null, /* @ts-ignore */
150
+ ...(ngDevMode ? [{ debugName: "dragContext" }] : /* istanbul ignore next */ []));
151
+ /** Whether a drag operation is currently in progress. */
152
+ isDragging = computed(() => this.dragContext() !== null, /* @ts-ignore */
153
+ ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
154
+ /**
155
+ * Whether any completed (non-cancelled) drag has occurred in this engine's lifetime.
156
+ *
157
+ * Used by `push` mode to defer resolving `fr`/`%` → `px` until the first drag.
158
+ * Before the first drag, original units are preserved so CSS grid can distribute
159
+ * space without rounding-induced overflow.
160
+ */
161
+ hasBeenResized = signal(false, /* @ts-ignore */
162
+ ...(ngDevMode ? [{ debugName: "hasBeenResized" }] : /* istanbul ignore next */ []));
163
+ /**
164
+ * CSS grid template string with item sizes interleaved with gap sizes.
165
+ *
166
+ * Example output: `"100px 4px minmax(50px, 2fr) 4px minmax(50px, 1fr)"`.
167
+ *
168
+ * **Output strategy per distribution mode:**
169
+ * - `adjacent` — resolves all sizes to `px`. CSS grid cannot constrain adjacent
170
+ * resizing correctly with `fr` units.
171
+ * - `proportional` — keeps `fr`/`%` as-is, wrapped in `minmax(floor, size)` when
172
+ * {@link minItemSizePx} > 0. This lets CSS grid redistribute remaining space
173
+ * while enforcing the minimum floor natively. Locked (`px`) columns pass through.
174
+ * - `push` — keeps original units until the first completed drag, then resolves
175
+ * everything to `px` so the total can exceed the container and trigger scrolling.
176
+ */
177
+ gridTemplateSizes = computed(() => {
178
+ const items = this.items();
179
+ const gaps = this.gapSizes();
180
+ if (items.length === 0)
181
+ return 'none';
182
+ const containerSize = this.containerSize();
183
+ const constrained = this.containerConstrained();
184
+ const mode = this.distributionMode();
185
+ // Resolve strategy:
186
+ // - adjacent (constrained): resolve ALL to px — CSS grid can't constrain adjacent resizing with fr.
187
+ // - push (unconstrained): keep original units until first movement bakes to px, then resolve for scrolling.
188
+ // - proportional: NEVER resolve fr→px. Use minmax(floor, size) so CSS grid handles min widths
189
+ // natively, both during and after drag. This allows columns that still have room to shrink
190
+ // while already-at-minimum columns stay at the floor.
191
+ const shouldResolve = containerSize > 0 &&
192
+ ((constrained && mode === 'adjacent') || (!constrained && this.hasBeenResized()));
193
+ const useMinmax = mode === 'proportional' && this.minItemSizePx > 0;
194
+ const frFactors = shouldResolve ? this.getCurrentFractionFactors() : null;
195
+ const result = [];
196
+ for (let i = 0; i < items.length; i++) {
197
+ const size = items[i].size();
198
+ const expanded = expandResizeSize(size);
199
+ if (shouldResolve && expanded.unit !== 'px') {
200
+ result.push(`${resolveSizeToPx(expanded, frFactors.pxPerFr, containerSize)}px`);
201
+ }
202
+ else if (useMinmax && expanded.unit !== 'px') {
203
+ result.push(`minmax(${this.minItemSizePx}px, ${size})`);
204
+ }
205
+ else {
206
+ result.push(size);
207
+ }
208
+ if (i < gaps.length) {
209
+ result.push(`${gaps[i] || 0}px`);
210
+ }
211
+ }
212
+ return result.join(' ');
213
+ }, /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "gridTemplateSizes" }] : /* istanbul ignore next */ []));
215
+ /**
216
+ * The total gap size in pixels.
217
+ */
218
+ totalGapSize = computed(() => this.gapSizes().reduce((acc, size) => acc + size, 0), /* @ts-ignore */
219
+ ...(ngDevMode ? [{ debugName: "totalGapSize" }] : /* istanbul ignore next */ []));
220
+ /**
221
+ * Aggregated item sizes by unit.
222
+ */
223
+ totalItemSizes = computed(() => this.items().reduce((acc, item) => {
224
+ const { unit, value } = expandResizeSize(item.size());
225
+ acc[unit] += value;
226
+ return acc;
227
+ }, { px: 0, fr: 0, '%': 0 }), /* @ts-ignore */
228
+ ...(ngDevMode ? [{ debugName: "totalItemSizes" }] : /* istanbul ignore next */ []));
229
+ /**
230
+ * Minimum total size of all items combined (as a CSS calc expression).
231
+ */
232
+ minTotalSize = computed(() => this.computeLimitTotalSize('minSize'), /* @ts-ignore */
233
+ ...(ngDevMode ? [{ debugName: "minTotalSize" }] : /* istanbul ignore next */ []));
234
+ /**
235
+ * Maximum total size of all items combined (as a CSS calc expression).
236
+ */
237
+ maxTotalSize = computed(() => this.computeLimitTotalSize('maxSize'), /* @ts-ignore */
238
+ ...(ngDevMode ? [{ debugName: "maxTotalSize" }] : /* istanbul ignore next */ []));
239
+ // --- Drag operations ---
240
+ /**
241
+ * Begins a drag at the given divider. Original sizes are saved for reverting
242
+ * on no-op clicks or cancellation. In push mode, baking to `px` is deferred
243
+ * to the first {@link drag} call to avoid a visual shift on mousedown.
244
+ */
245
+ startDrag(dividerIndex, startPositionPx) {
246
+ const items = this.items();
247
+ if (dividerIndex < 0 || items.length < 2 || dividerIndex >= items.length)
248
+ return;
249
+ const preBakeSizes = items.map(item => item.size());
250
+ this.dragContext.set({
251
+ dividerIndex,
252
+ startPosition: startPositionPx,
253
+ preBakeSizes,
254
+ items: items.map(item => this.createContextItem(item)),
255
+ fractionFactors: this.getCurrentFractionFactors(),
256
+ percentPerPx: this.getCurrentPercentPerPx(),
257
+ baked: this.containerConstrained(),
258
+ });
259
+ }
260
+ /** Updates item sizes during an active drag based on the current pointer position. */
261
+ drag(dividerIndex, currentPositionPx) {
262
+ const ctx = this.dragContext();
263
+ if (!ctx || ctx.dividerIndex !== dividerIndex)
264
+ return;
265
+ // In push mode, bake all items to px on the first movement so non-dragged
266
+ // items keep their absolute widths. Deferred from startDrag to avoid a
267
+ // visual column shift on mousedown (CSS Grid fr→px rounding mismatch).
268
+ if (!ctx.baked) {
269
+ this.bakeAllItemsToPx();
270
+ ctx.items = this.items().map(item => this.createContextItem(item));
271
+ ctx.fractionFactors = this.getCurrentFractionFactors();
272
+ ctx.percentPerPx = this.getCurrentPercentPerPx();
273
+ ctx.baked = true;
274
+ }
275
+ const pxDelta = currentPositionPx - ctx.startPosition;
276
+ this.applyDelta(dividerIndex, pxDelta, ctx.items, ctx.fractionFactors, ctx.percentPerPx);
277
+ }
278
+ /**
279
+ * Ends a drag. If cancelled or no movement occurred, sizes revert to their
280
+ * pre-drag values (including undoing any push-mode baking). Otherwise,
281
+ * post-drag finalization runs (commit mode or proportional locking).
282
+ */
283
+ endDrag(dividerIndex, cancel) {
284
+ const ctx = this.dragContext();
285
+ if (!ctx || ctx.dividerIndex !== dividerIndex)
286
+ return;
287
+ const hasMoved = ctx.items.some(ci => {
288
+ const current = expandResizeSize(ci.item.size());
289
+ return current.value !== ci.startSize.value || current.unit !== ci.startSize.unit;
290
+ });
291
+ if (cancel || !hasMoved) {
292
+ // Reset to pre-bake sizes (restores original fr/% units on no-op clicks)
293
+ ctx.items.forEach((ci, i) => ci.item.size.set(ctx.preBakeSizes[i]));
294
+ }
295
+ else {
296
+ this.finalizeResize(ctx);
297
+ this.hasBeenResized.set(true);
298
+ }
299
+ this.dragContext.set(null);
300
+ }
301
+ /**
302
+ * Applies a single instantaneous move (e.g. keyboard step) at the given divider.
303
+ * Requires that the relevant items already have engine-calculated sizes.
304
+ */
305
+ moveDivider(dividerIndex, pxDelta) {
306
+ const items = this.items();
307
+ if (dividerIndex < 0 || items.length < 2 || dividerIndex >= items.length)
308
+ return;
309
+ // Check that relevant items have calculated sizes
310
+ if (!this.isItemSizeCalculated(items[dividerIndex]))
311
+ return;
312
+ if (dividerIndex < items.length - 1 && !this.isItemSizeCalculated(items[dividerIndex + 1]))
313
+ return;
314
+ const contextItems = items.map(item => this.createContextItem(item));
315
+ this.applyDelta(dividerIndex, pxDelta, contextItems, this.getCurrentFractionFactors(), this.getCurrentPercentPerPx());
316
+ }
317
+ /**
318
+ * Clamps all items to their min/max constraints, respecting {@link minItemSizePx}.
319
+ *
320
+ * Called automatically via `afterRenderEffect` when item sizes change externally
321
+ * or the container resizes. Skipped during an active drag (sizes are managed
322
+ * by the drag handler). For `fr`-sized items, clamping is done iteratively
323
+ * with redistribution to maintain total fraction balance.
324
+ */
325
+ ensureMinMaxSizes() {
326
+ const items = this.items();
327
+ if (items.length === 0)
328
+ return;
329
+ const containerSize = this.containerSize();
330
+ const itemSizes = items.map(item => {
331
+ const expanded = expandResizeSize(item.size());
332
+ return {
333
+ item,
334
+ size: { unit: expanded.unit, value: expanded.value },
335
+ minSizePx: Math.max(this.minItemSizePx, getResizeLimitInPx(item.minSize(), containerSize)),
336
+ maxSizePx: getResizeLimitInPx(item.maxSize(), containerSize),
337
+ };
338
+ });
339
+ // Clamp px and % items
340
+ for (const entry of itemSizes) {
341
+ if (entry.size.unit === 'px') {
342
+ entry.size.value = clamp(entry.size.value, entry.minSizePx, entry.maxSizePx);
343
+ }
344
+ else if (entry.size.unit === '%') {
345
+ const pxValue = (entry.size.value / 100) * containerSize;
346
+ if (pxValue < entry.minSizePx) {
347
+ entry.size.value = (entry.minSizePx / containerSize) * 100;
348
+ }
349
+ else if (pxValue > entry.maxSizePx) {
350
+ entry.size.value = (entry.maxSizePx / containerSize) * 100;
351
+ }
352
+ }
353
+ }
354
+ // Calculate fraction factors for fr-sized items
355
+ const totalPx = itemSizes.reduce((acc, { size }) => (size.unit === 'px' ? acc + size.value : acc), 0);
356
+ const totalPercent = itemSizes.reduce((acc, { size }) => (size.unit === '%' ? acc + size.value : acc), 0);
357
+ const totalFr = itemSizes.reduce((acc, { size }) => (size.unit === 'fr' ? acc + size.value : acc), 0);
358
+ const frArea = containerSize - this.totalGapSize() - totalPx - (totalPercent / 100) * containerSize;
359
+ const frPerPx = totalFr / (frArea || 1);
360
+ const pxPerFr = (frArea || 1) / (totalFr || 1);
361
+ // Iteratively clamp fr-sized items and redistribute
362
+ let frPanelsWithoutClamping = itemSizes.filter(p => p.size.unit === 'fr');
363
+ let frToDistribute = undefined;
364
+ while (frPanelsWithoutClamping.length > 0 && frToDistribute !== 0) {
365
+ const frToAddPerPanel = frToDistribute ? frToDistribute / frPanelsWithoutClamping.length : 0;
366
+ const next = [];
367
+ let currentTotalFrSize = totalFr;
368
+ for (const p of frPanelsWithoutClamping) {
369
+ const minSizeFr = p.minSizePx * frPerPx;
370
+ const maxSizeFr = p.maxSizePx * frPerPx;
371
+ p.size.value += frToAddPerPanel;
372
+ if (p.size.value < minSizeFr) {
373
+ currentTotalFrSize = currentTotalFrSize - p.size.value + minSizeFr;
374
+ p.size.value = minSizeFr;
375
+ }
376
+ else if (p.size.value > maxSizeFr) {
377
+ currentTotalFrSize = currentTotalFrSize - p.size.value + maxSizeFr;
378
+ p.size.value = maxSizeFr;
379
+ }
380
+ else {
381
+ next.push(p);
382
+ }
383
+ }
384
+ frToDistribute = totalFr - currentTotalFrSize;
385
+ if (Math.abs(frToDistribute * pxPerFr) < 1) {
386
+ frToDistribute = 0;
387
+ }
388
+ frPanelsWithoutClamping = next;
389
+ }
390
+ // Write back
391
+ for (const { item, size } of itemSizes) {
392
+ this.setItemSize(item, `${size.value}${size.unit}`);
393
+ }
394
+ }
395
+ // --- Item size tracking ---
396
+ /**
397
+ * Sets the item's size and marks it as engine-calculated via an internal symbol.
398
+ * This mark is used by {@link isItemSizeCalculated} to detect external size changes.
399
+ */
400
+ setItemSize(item, size) {
401
+ item.size.set(size);
402
+ item[LAST_CALC_SIZE_SYMBOL] = size;
403
+ }
404
+ /**
405
+ * Returns `true` if the item's current size matches the last value written by the engine.
406
+ * Returns `false` if the size was changed externally (e.g. by an input binding or state restore).
407
+ */
408
+ isItemSizeCalculated(item) {
409
+ return item[LAST_CALC_SIZE_SYMBOL] === item.size();
410
+ }
411
+ // --- Private: delta application ---
412
+ /**
413
+ * Routes a px delta to the appropriate distribution strategy.
414
+ *
415
+ * For last-item edge resizing (dividerIndex === items.length - 1), the delta is
416
+ * remapped to the previous divider with negated direction. This allows the last
417
+ * column's handle in push mode to resize it directly.
418
+ */
419
+ applyDelta(dividerIndex, pxDelta, items, fractionFactors, percentPerPx) {
420
+ const mode = this.distributionMode();
421
+ // Right-edge resize of the last item: remap to the previous divider with negated delta.
422
+ const isLastItemEdge = dividerIndex === items.length - 1;
423
+ const effectiveDividerIndex = isLastItemEdge ? dividerIndex - 1 : dividerIndex;
424
+ const effectiveDelta = isLastItemEdge ? -pxDelta : pxDelta;
425
+ if (mode === 'push' && !this.containerConstrained()) {
426
+ this.applyPushDelta(dividerIndex, pxDelta, items, fractionFactors, percentPerPx);
427
+ }
428
+ else if (mode === 'proportional') {
429
+ this.applyProportionalDelta(effectiveDividerIndex, effectiveDelta, items, fractionFactors);
430
+ }
431
+ else {
432
+ this.applyAdjacentDelta(effectiveDividerIndex, effectiveDelta, items, fractionFactors, percentPerPx);
433
+ }
434
+ }
435
+ /**
436
+ * Adjacent mode: transfers the delta between the two items neighbouring the divider.
437
+ *
438
+ * Grows the left item and shrinks the right item (or vice versa). When an item
439
+ * hits its min/max constraint, the unapplied remainder cascades to the next item
440
+ * in that direction. A second pass resolves any asymmetry between left and right.
441
+ * The total container width is always preserved (zero-sum).
442
+ */
443
+ applyAdjacentDelta(dividerIndex, pxDelta, items, fractionFactors, percentPerPx) {
444
+ const containerSize = this.containerSize();
445
+ const appliedPanelDeltas = Array(items.length).fill(0);
446
+ const recursion = (pxDelta, itemIndex, itemIndexIncrement) => {
447
+ if (itemIndex >= items.length || itemIndex < 0)
448
+ return 0;
449
+ const ci = items[itemIndex];
450
+ const startPx = resolveSizeToPx(ci.startSize, fractionFactors.pxPerFr, containerSize);
451
+ const newPx = startPx + pxDelta;
452
+ const limitPx = getLimitPxForDelta(ci, pxDelta, containerSize, this.minItemSizePx);
453
+ const unappliedDelta = pxDelta < 0
454
+ ? limitPx > newPx
455
+ ? -(limitPx - newPx)
456
+ : 0
457
+ : limitPx < newPx
458
+ ? newPx - limitPx
459
+ : 0;
460
+ const appliedDelta = pxDelta - unappliedDelta;
461
+ appliedPanelDeltas[itemIndex] = appliedDelta;
462
+ return unappliedDelta === 0
463
+ ? pxDelta
464
+ : appliedDelta +
465
+ recursion(unappliedDelta, itemIndex + itemIndexIncrement, itemIndexIncrement);
466
+ };
467
+ let appliedLeft = recursion(pxDelta, dividerIndex, -1);
468
+ let appliedRight = -recursion(-pxDelta, dividerIndex + 1, 1);
469
+ if (appliedLeft !== pxDelta || appliedRight !== pxDelta) {
470
+ const resolvedDelta = pxDelta < 0 ? Math.max(appliedLeft, appliedRight) : Math.min(appliedLeft, appliedRight);
471
+ appliedPanelDeltas.fill(0);
472
+ appliedLeft = recursion(resolvedDelta, dividerIndex, -1);
473
+ appliedRight = -recursion(-resolvedDelta, dividerIndex + 1, 1);
474
+ if (appliedLeft !== resolvedDelta || appliedRight !== resolvedDelta) {
475
+ return;
476
+ }
477
+ }
478
+ this.commitItemDeltas(items, appliedPanelDeltas, fractionFactors, percentPerPx);
479
+ }
480
+ /**
481
+ * Proportional mode: only the item at `dividerIndex` changes size (set to absolute `px`).
482
+ * All other items keep their original units (`fr`, `%`), and CSS grid's `minmax(floor, size)`
483
+ * naturally redistributes the remaining space — columns with room shrink while those
484
+ * already at the floor stay put.
485
+ *
486
+ * Growth is capped so the total never overflows the container: each other `fr` column
487
+ * needs at least {@link minItemSizePx}, and locked `px` columns keep their size.
488
+ *
489
+ * After the drag ends, {@link applyProportionalLocking} determines whether the
490
+ * resized column stays as `px` (locked) or is converted back to `fr` (dynamic).
491
+ */
492
+ applyProportionalDelta(dividerIndex, pxDelta, items, fractionFactors) {
493
+ const containerSize = this.containerSize();
494
+ const floor = this.minItemSizePx;
495
+ const item = items[dividerIndex];
496
+ const startPx = resolveSizeToPx(item.startSize, fractionFactors.pxPerFr, containerSize);
497
+ // Clamp to own min/max
498
+ let newPx = clampToLimits(startPx + pxDelta, item, containerSize, floor);
499
+ // Cap so the total doesn't overflow. With minmax(floor, Xfr), each other
500
+ // column needs at least `floor` px. Locked px columns keep their size.
501
+ if (floor > 0) {
502
+ const totalGaps = this.totalGapSize();
503
+ let otherLockedPx = 0;
504
+ let otherFrCount = 0;
505
+ for (let i = 0; i < items.length; i++) {
506
+ if (i === dividerIndex)
507
+ continue;
508
+ const { unit, value } = expandResizeSize(items[i].item.size());
509
+ if (unit === 'px') {
510
+ otherLockedPx += value;
511
+ }
512
+ else {
513
+ otherFrCount++;
514
+ }
515
+ }
516
+ newPx = Math.min(newPx, containerSize - totalGaps - otherLockedPx - otherFrCount * floor);
517
+ }
518
+ this.setItemSize(item.item, `${Math.max(0, newPx)}px`);
519
+ }
520
+ /**
521
+ * Push mode: only the dragged item changes size. All other items stay at their
522
+ * baked `px` values (set during {@link startDrag}). The container total changes,
523
+ * causing horizontal overflow/scrolling. Only valid when `containerConstrained` is `false`.
524
+ */
525
+ applyPushDelta(dividerIndex, pxDelta, items, fractionFactors, percentPerPx) {
526
+ const containerSize = this.containerSize();
527
+ const appliedPanelDeltas = Array(items.length).fill(0);
528
+ const item = items[dividerIndex];
529
+ const startPx = resolveSizeToPx(item.startSize, fractionFactors.pxPerFr, containerSize);
530
+ appliedPanelDeltas[dividerIndex] =
531
+ clampToLimits(startPx + pxDelta, item, containerSize, this.minItemSizePx) - startPx;
532
+ this.commitItemDeltas(items, appliedPanelDeltas, fractionFactors, percentPerPx);
533
+ }
534
+ // --- Private: post-drag finalization ---
535
+ /**
536
+ * Post-drag finalization. Behaviour depends on {@link lockSizes} and the distribution mode.
537
+ *
538
+ * When `lockSizes` is `false`:
539
+ * - `adjacent` / `push` — no-op; items keep their original units.
540
+ * - `proportional` — the resized column is converted back to `fr` so it stays dynamic.
541
+ *
542
+ * When `lockSizes` is `true`:
543
+ * - `adjacent` / `push` — items whose size changed are converted to absolute `px`.
544
+ * - `proportional` — no-op; the resized column is already `px` from the drag.
545
+ */
546
+ finalizeResize(ctx) {
547
+ const lock = this.lockSizes();
548
+ if (this.distributionMode() === 'proportional') {
549
+ if (!lock)
550
+ this.unlockProportionalColumn(ctx);
551
+ return;
552
+ }
553
+ // Adjacent / push: lock affected items to px when enabled
554
+ if (!lock)
555
+ return;
556
+ const containerSize = this.containerSize();
557
+ const fractionFactors = this.getCurrentFractionFactors();
558
+ for (const ci of ctx.items) {
559
+ const currentSize = expandResizeSize(ci.item.size());
560
+ const sizeChanged = currentSize.value !== ci.startSize.value || currentSize.unit !== ci.startSize.unit;
561
+ if (sizeChanged) {
562
+ const pxValue = resolveSizeToPx(currentSize, fractionFactors.pxPerFr, containerSize);
563
+ this.setItemSize(ci.item, `${Math.max(0, pxValue)}px`);
564
+ }
565
+ }
566
+ }
567
+ /**
568
+ * Converts the resized column back to `fr` after a proportional drag so it
569
+ * continues to flex with the container. Computes a new `fr` value that would
570
+ * resolve to the same pixel width given the current container and other `fr` columns.
571
+ */
572
+ unlockProportionalColumn(ctx) {
573
+ const items = ctx.items;
574
+ const containerSize = this.containerSize();
575
+ const resizedItem = items[ctx.dividerIndex];
576
+ const resizedPx = expandResizeSize(resizedItem.item.size()).value;
577
+ // Sum up all other fr values (from pre-bake, since they weren't changed)
578
+ let otherFrTotal = 0;
579
+ let otherPxTotal = 0;
580
+ for (let i = 0; i < items.length; i++) {
581
+ if (i === ctx.dividerIndex)
582
+ continue;
583
+ const preBake = expandResizeSize(ctx.preBakeSizes[i]);
584
+ if (preBake.unit === 'fr') {
585
+ otherFrTotal += preBake.value;
586
+ }
587
+ else if (preBake.unit === 'px') {
588
+ otherPxTotal += preBake.value;
589
+ }
590
+ }
591
+ const availableForFr = containerSize - this.totalGapSize() - otherPxTotal;
592
+ if (otherFrTotal > 0 && availableForFr > resizedPx) {
593
+ // Solve: newFr / otherFrTotal = resizedPx / (availableForFr - resizedPx)
594
+ const newFr = (resizedPx * otherFrTotal) / (availableForFr - resizedPx);
595
+ this.setItemSize(resizedItem.item, `${Math.max(0, +newFr.toFixed(4))}fr`);
596
+ }
597
+ // If we can't compute a valid fr (edge case), leave as px
598
+ }
599
+ // --- Private: helpers ---
600
+ createContextItem(item) {
601
+ return {
602
+ item,
603
+ startSize: expandResizeSize(item.size()),
604
+ minSize: expandResizeLimit(item.minSize()),
605
+ maxSize: expandResizeLimit(item.maxSize()),
606
+ };
607
+ }
608
+ getCurrentFractionFactors() {
609
+ const totals = this.totalItemSizes();
610
+ const cs = this.containerSize();
611
+ const percentPx = (totals['%'] / 100) * cs;
612
+ const frArea = cs - this.totalGapSize() - totals.px - percentPx || 1;
613
+ const totalFr = totals.fr || 1;
614
+ return {
615
+ frPerPx: totalFr / frArea,
616
+ pxPerFr: frArea / totalFr,
617
+ };
618
+ }
619
+ getCurrentPercentPerPx() {
620
+ return 100 / (this.containerSize() || 1);
621
+ }
622
+ /**
623
+ * Bakes all non-px items to absolute px values. Used by push mode on first drag movement.
624
+ * Prefers DOM-measured sizes (via {@link resolveItemSizes}) over JS-computed conversions
625
+ * to avoid sub-pixel mismatches with CSS Grid's internal track sizing.
626
+ */
627
+ bakeAllItemsToPx() {
628
+ const items = this.items();
629
+ const measured = this.resolveItemSizes?.();
630
+ if (measured && measured.length === items.length) {
631
+ for (let i = 0; i < items.length; i++) {
632
+ this.setItemSize(items[i], `${measured[i]}px`);
633
+ }
634
+ }
635
+ else {
636
+ const frFactors = this.getCurrentFractionFactors();
637
+ const containerSize = this.containerSize();
638
+ for (const item of items) {
639
+ const size = expandResizeSize(item.size());
640
+ if (size.unit !== 'px') {
641
+ this.setItemSize(item, `${resolveSizeToPx(size, frFactors.pxPerFr, containerSize)}px`);
642
+ }
643
+ }
644
+ }
645
+ }
646
+ /**
647
+ * Writes computed px deltas back to items, converting to each item's original unit.
648
+ */
649
+ commitItemDeltas(items, appliedPanelDeltas, fractionFactors, percentPerPx) {
650
+ items.forEach((ci, i) => {
651
+ const pxDelta = appliedPanelDeltas[i] || 0;
652
+ const unitDelta = pxDeltaToUnitDelta(pxDelta, ci.startSize.unit, fractionFactors.frPerPx, percentPerPx);
653
+ const newValue = ci.startSize.value + unitDelta;
654
+ this.setItemSize(ci.item, `${Math.max(0, newValue)}${ci.startSize.unit}`);
655
+ });
656
+ }
657
+ /**
658
+ * Builds a CSS `calc()` expression summing all items' min or max limits.
659
+ * Items already in `px` contribute their current size; `fr`/`%` items contribute
660
+ * their `minSize`/`maxSize` limit. Used by {@link minTotalSize} and {@link maxTotalSize}.
661
+ */
662
+ computeLimitTotalSize(limitProp) {
663
+ const { px, '%': pc } = this.items().reduce((acc, item) => {
664
+ const { unit, value } = expandResizeSize(item.size());
665
+ if (unit === 'px') {
666
+ acc.px += value;
667
+ }
668
+ else {
669
+ const limitStr = item[limitProp]();
670
+ acc[getResizeLimitUnit(limitStr)] += getResizeLimitValue(limitStr);
671
+ }
672
+ return acc;
673
+ }, { px: 0, '%': 0 });
674
+ return `calc(${px + this.totalGapSize()}px + ${pc}%)`;
675
+ }
676
+ }
677
+ // --- Module-level helpers ---
678
+ /** Clamps a value between min and max. */
679
+ function clamp(value, min, max) {
680
+ return Math.max(min, Math.min(max, value));
681
+ }
682
+ /** Resolves an expanded limit to px. */
683
+ function limitToPx(limit, containerSize) {
684
+ return limit.unit === 'px' ? limit.value : (limit.value / 100) * containerSize;
685
+ }
686
+ /**
687
+ * Resolves a min limit to px, enforcing the given absolute floor.
688
+ * Even if a relative limit (e.g. `5%`) resolves to less than the floor, the floor wins.
689
+ */
690
+ function minLimitToPx(limit, containerSize, floorPx) {
691
+ return Math.max(floorPx, limitToPx(limit, containerSize));
692
+ }
693
+ /** Clamps a px value to the item's min/max limits (with floor on min). */
694
+ function clampToLimits(px, ci, containerSize, floorPx) {
695
+ return clamp(px, minLimitToPx(ci.minSize, containerSize, floorPx), limitToPx(ci.maxSize, containerSize));
696
+ }
697
+ /**
698
+ * Returns the relevant limit (min or max) in px for the direction of a delta.
699
+ * Negative delta → min limit (with floor), positive delta → max limit.
700
+ */
701
+ function getLimitPxForDelta(ci, pxDelta, containerSize, floorPx) {
702
+ return pxDelta < 0
703
+ ? minLimitToPx(ci.minSize, containerSize, floorPx)
704
+ : limitToPx(ci.maxSize, containerSize);
705
+ }
706
+
707
+ /**
708
+ * Generated bundle index. Do not edit.
709
+ */
710
+
711
+ export { ResizeEngine, collapseResizeLimit, collapseResizeSize, expandResizeLimit, expandResizeSize, getResizeLimitInPx, getResizeLimitUnit, getResizeLimitValue, getResizeSizeUnit, getResizeSizeValue, isResizeLimit, isResizeSize, pxDeltaToUnitDelta, resolveSizeToPx };
712
+ //# sourceMappingURL=ngneers-controls-api-resize.mjs.map