ng-primitives 0.122.0 → 0.123.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.
- package/fesm2022/ng-primitives-combobox.mjs +10 -1
- package/fesm2022/ng-primitives-combobox.mjs.map +1 -1
- package/fesm2022/ng-primitives-date-picker.mjs +1 -1
- package/fesm2022/ng-primitives-date-picker.mjs.map +1 -1
- package/fesm2022/ng-primitives-internal.mjs +184 -3
- package/fesm2022/ng-primitives-internal.mjs.map +1 -1
- package/fesm2022/ng-primitives-menu.mjs +131 -19
- package/fesm2022/ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/ng-primitives-portal.mjs +74 -12
- package/fesm2022/ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/ng-primitives-select.mjs +10 -1
- package/fesm2022/ng-primitives-select.mjs.map +1 -1
- package/fesm2022/ng-primitives-tooltip.mjs +19 -118
- package/fesm2022/ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/ng-primitives-utils.mjs +40 -23
- package/fesm2022/ng-primitives-utils.mjs.map +1 -1
- package/package.json +1 -1
- package/schematics/ng-generate/templates/toggle-group/toggle-group.__fileSuffix@dasherize__.ts.template +2 -2
- package/types/ng-primitives-internal.d.ts +95 -3
- package/types/ng-primitives-menu.d.ts +42 -10
- package/types/ng-primitives-portal.d.ts +31 -1
- package/types/ng-primitives-tooltip.d.ts +6 -9
- package/types/ng-primitives-utils.d.ts +12 -9
|
@@ -93,6 +93,13 @@ function coerceOffset(value) {
|
|
|
93
93
|
class NgpOverlayCooldownManager {
|
|
94
94
|
constructor() {
|
|
95
95
|
this.lastCloseTimestamps = new Map();
|
|
96
|
+
/**
|
|
97
|
+
* Active overlays per type, stored as a stack ordered oldest-first / topmost-last.
|
|
98
|
+
* Most types only ever hold a single overlay, but when an overlay is nested inside
|
|
99
|
+
* another of the same type (e.g. a popover whose trigger lives inside another
|
|
100
|
+
* popover) the child is pushed on top of its ancestor instead of evicting it.
|
|
101
|
+
* Closing the child then restores the ancestor as the active overlay.
|
|
102
|
+
*/
|
|
96
103
|
this.activeOverlays = new Map();
|
|
97
104
|
}
|
|
98
105
|
/**
|
|
@@ -117,23 +124,50 @@ class NgpOverlayCooldownManager {
|
|
|
117
124
|
}
|
|
118
125
|
/**
|
|
119
126
|
* Register an overlay as active for its type.
|
|
120
|
-
*
|
|
127
|
+
*
|
|
128
|
+
* Any existing overlay of the same type is closed immediately so that only one
|
|
129
|
+
* overlay of each type is open at a time - *unless* it is an ancestor of the
|
|
130
|
+
* overlay being registered. A nested overlay (its trigger rendered inside an
|
|
131
|
+
* ancestor overlay's content) is stacked on top of its ancestor instead of
|
|
132
|
+
* evicting it, allowing legitimate nesting to coexist while sibling overlays
|
|
133
|
+
* still replace one another.
|
|
134
|
+
*
|
|
121
135
|
* @param overlayType The type identifier for the overlay group
|
|
122
136
|
* @param overlay The overlay instance
|
|
123
137
|
* @param cooldown The cooldown duration - if > 0, enables instant transitions
|
|
124
138
|
*/
|
|
125
139
|
registerActive(overlayType, overlay, cooldown) {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
const stack = this.activeOverlays.get(overlayType) ?? [];
|
|
141
|
+
if (stack.includes(overlay)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Evict overlays from the top of the stack until we reach the overlay itself
|
|
145
|
+
// or one of its ancestors (or the stack empties). Ancestors are preserved so
|
|
146
|
+
// nested same-type overlays can coexist; peers are closed immediately.
|
|
147
|
+
while (stack.length > 0) {
|
|
148
|
+
const top = stack[stack.length - 1];
|
|
149
|
+
if (overlay.isDescendantOf?.(top)) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (top.isDescendantOf?.(overlay)) {
|
|
153
|
+
const firstDescendantIndex = stack.findIndex(entry => entry.isDescendantOf?.(overlay));
|
|
154
|
+
stack.splice(firstDescendantIndex, 0, overlay);
|
|
155
|
+
this.activeOverlays.set(overlayType, stack);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
stack.pop();
|
|
130
159
|
// Enable instant transition only if cooldown is active
|
|
131
160
|
if (cooldown > 0) {
|
|
132
|
-
|
|
161
|
+
top.instantTransition?.set(true);
|
|
133
162
|
}
|
|
134
|
-
|
|
163
|
+
top.hideImmediate();
|
|
135
164
|
}
|
|
136
|
-
|
|
165
|
+
// Push as the topmost active overlay, avoiding a duplicate entry if it is
|
|
166
|
+
// already there (e.g. re-registering after a cancelled destruction).
|
|
167
|
+
if (stack[stack.length - 1] !== overlay) {
|
|
168
|
+
stack.push(overlay);
|
|
169
|
+
}
|
|
170
|
+
this.activeOverlays.set(overlayType, stack);
|
|
137
171
|
}
|
|
138
172
|
/**
|
|
139
173
|
* Unregister an overlay when it closes.
|
|
@@ -141,7 +175,15 @@ class NgpOverlayCooldownManager {
|
|
|
141
175
|
* @param overlay The overlay instance to remove
|
|
142
176
|
*/
|
|
143
177
|
unregisterActive(overlayType, overlay) {
|
|
144
|
-
|
|
178
|
+
const stack = this.activeOverlays.get(overlayType);
|
|
179
|
+
if (!stack) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const index = stack.indexOf(overlay);
|
|
183
|
+
if (index !== -1) {
|
|
184
|
+
stack.splice(index, 1);
|
|
185
|
+
}
|
|
186
|
+
if (stack.length === 0) {
|
|
145
187
|
this.activeOverlays.delete(overlayType);
|
|
146
188
|
}
|
|
147
189
|
}
|
|
@@ -151,7 +193,8 @@ class NgpOverlayCooldownManager {
|
|
|
151
193
|
* @returns true if there's an active overlay, false otherwise
|
|
152
194
|
*/
|
|
153
195
|
hasActiveOverlay(overlayType) {
|
|
154
|
-
|
|
196
|
+
const stack = this.activeOverlays.get(overlayType);
|
|
197
|
+
return stack !== undefined && stack.length > 0;
|
|
155
198
|
}
|
|
156
199
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpOverlayCooldownManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
157
200
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpOverlayCooldownManager, providedIn: 'root' }); }
|
|
@@ -1396,6 +1439,25 @@ class NgpOverlay {
|
|
|
1396
1439
|
unregisterChildOverlay(child) {
|
|
1397
1440
|
this.childOverlays.delete(child);
|
|
1398
1441
|
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Determine whether this overlay is a descendant of the given overlay - i.e.
|
|
1444
|
+
* its trigger is rendered within the other overlay's content. Walks the parent
|
|
1445
|
+
* overlay chain established through dependency injection.
|
|
1446
|
+
*
|
|
1447
|
+
* Used by the cooldown manager to avoid evicting an ancestor overlay when a
|
|
1448
|
+
* nested overlay of the same type is activated.
|
|
1449
|
+
* @internal
|
|
1450
|
+
*/
|
|
1451
|
+
isDescendantOf(other) {
|
|
1452
|
+
let current = this.parentOverlay;
|
|
1453
|
+
while (current) {
|
|
1454
|
+
if (current === other) {
|
|
1455
|
+
return true;
|
|
1456
|
+
}
|
|
1457
|
+
current = current.parentOverlay;
|
|
1458
|
+
}
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1399
1461
|
/**
|
|
1400
1462
|
* Check if the event path includes any child overlay elements (recursively).
|
|
1401
1463
|
* @internal
|
|
@@ -1556,6 +1618,7 @@ class NgpOverlay {
|
|
|
1556
1618
|
this.position.set({ x: position.x, y: position.y });
|
|
1557
1619
|
// Update final placement signal
|
|
1558
1620
|
this.finalPlacement.set(position.placement);
|
|
1621
|
+
this.transformOrigin.set(this.getTransformOrigin(position.placement));
|
|
1559
1622
|
// Update arrow position if available
|
|
1560
1623
|
if (this.arrowElement) {
|
|
1561
1624
|
this.arrowPosition.set({
|
|
@@ -1630,8 +1693,7 @@ class NgpOverlay {
|
|
|
1630
1693
|
/**
|
|
1631
1694
|
* Get the transform origin for the overlay
|
|
1632
1695
|
*/
|
|
1633
|
-
getTransformOrigin() {
|
|
1634
|
-
const placement = this.config.placement?.() ?? 'top';
|
|
1696
|
+
getTransformOrigin(placement = this.config.placement?.() ?? 'top') {
|
|
1635
1697
|
const basePlacement = placement.split('-')[0]; // Extract "top", "bottom", etc.
|
|
1636
1698
|
const alignment = placement.split('-')[1]; // Extract "start" or "end"
|
|
1637
1699
|
const map = {
|