@aquera/nile-elements 1.4.8-beta-1.0 → 1.4.8-beta-1.2
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/README.md +4 -0
- package/demo/index.html +133 -33
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +98 -71
- package/dist/nile-file-preview/nile-file-preview.css.cjs.js +1 -1
- package/dist/nile-file-preview/nile-file-preview.css.cjs.js.map +1 -1
- package/dist/nile-file-preview/nile-file-preview.css.esm.js +7 -3
- package/dist/nile-file-preview/nile-file-preview.template.cjs.js +1 -1
- package/dist/nile-file-preview/nile-file-preview.template.cjs.js.map +1 -1
- package/dist/nile-file-preview/nile-file-preview.template.esm.js +73 -64
- package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
- package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js.map +1 -1
- package/dist/nile-lite-tooltip/nile-lite-tooltip.esm.js +1 -1
- package/dist/nile-option/nile-option.cjs.js +1 -1
- package/dist/nile-option/nile-option.cjs.js.map +1 -1
- package/dist/nile-option/nile-option.css.cjs.js +1 -1
- package/dist/nile-option/nile-option.css.cjs.js.map +1 -1
- package/dist/nile-option/nile-option.css.esm.js +10 -0
- package/dist/nile-option/nile-option.esm.js +10 -7
- package/dist/nile-side-bar-action/index.cjs.js +1 -1
- package/dist/nile-side-bar-action/index.esm.js +1 -1
- package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js +1 -1
- package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js.map +1 -1
- package/dist/nile-side-bar-action/nile-side-bar-action.esm.js +4 -3
- package/dist/nile-side-bar-action/portal-manager.cjs.js +2 -0
- package/dist/nile-side-bar-action/portal-manager.cjs.js.map +1 -0
- package/dist/nile-side-bar-action/portal-manager.esm.js +1 -0
- package/dist/nile-side-bar-action/portal-utils.cjs.js +2 -0
- package/dist/nile-side-bar-action/portal-utils.cjs.js.map +1 -0
- package/dist/nile-side-bar-action/portal-utils.esm.js +1 -0
- package/dist/src/nile-file-preview/nile-file-preview.css.js +7 -3
- package/dist/src/nile-file-preview/nile-file-preview.css.js.map +1 -1
- package/dist/src/nile-file-preview/nile-file-preview.template.js +46 -37
- package/dist/src/nile-file-preview/nile-file-preview.template.js.map +1 -1
- package/dist/src/nile-lite-tooltip/nile-lite-tooltip.d.ts +2 -0
- package/dist/src/nile-lite-tooltip/nile-lite-tooltip.js +15 -0
- package/dist/src/nile-lite-tooltip/nile-lite-tooltip.js.map +1 -1
- package/dist/src/nile-option/nile-option.css.js +10 -0
- package/dist/src/nile-option/nile-option.css.js.map +1 -1
- package/dist/src/nile-option/nile-option.d.ts +1 -0
- package/dist/src/nile-option/nile-option.js +13 -5
- package/dist/src/nile-option/nile-option.js.map +1 -1
- package/dist/src/nile-side-bar-action/nile-side-bar-action.d.ts +8 -1
- package/dist/src/nile-side-bar-action/nile-side-bar-action.js +66 -12
- package/dist/src/nile-side-bar-action/nile-side-bar-action.js.map +1 -1
- package/dist/src/nile-side-bar-action/portal-manager.d.ts +43 -0
- package/dist/src/nile-side-bar-action/portal-manager.js +374 -0
- package/dist/src/nile-side-bar-action/portal-manager.js.map +1 -0
- package/dist/src/nile-side-bar-action/portal-utils.d.ts +32 -0
- package/dist/src/nile-side-bar-action/portal-utils.js +212 -0
- package/dist/src/nile-side-bar-action/portal-utils.js.map +1 -0
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/nile-file-preview/nile-file-preview.css.ts +7 -3
- package/src/nile-file-preview/nile-file-preview.template.ts +46 -37
- package/src/nile-lite-tooltip/nile-lite-tooltip.ts +15 -0
- package/src/nile-option/nile-option.css.ts +10 -0
- package/src/nile-option/nile-option.ts +11 -5
- package/src/nile-side-bar-action/nile-side-bar-action.ts +74 -15
- package/src/nile-side-bar-action/portal-manager.ts +489 -0
- package/src/nile-side-bar-action/portal-utils.ts +270 -0
- package/vscode-html-custom-data.json +22 -3
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import {
|
|
2
|
+
autoUpdate,
|
|
3
|
+
computePosition,
|
|
4
|
+
flip,
|
|
5
|
+
offset,
|
|
6
|
+
shift,
|
|
7
|
+
size,
|
|
8
|
+
platform,
|
|
9
|
+
type Placement,
|
|
10
|
+
type MiddlewareData,
|
|
11
|
+
type ComputePositionConfig
|
|
12
|
+
} from '@floating-ui/dom';
|
|
13
|
+
import { PortalUtils, PortalContentUtils, PortalEventUtils } from './portal-utils';
|
|
14
|
+
|
|
15
|
+
export class DropdownPortalManager {
|
|
16
|
+
private portalContainer: HTMLElement | null = null;
|
|
17
|
+
private measuredPanelHeight: number | null = null;
|
|
18
|
+
private component: any;
|
|
19
|
+
private clonedPanel: HTMLElement | null = null;
|
|
20
|
+
private cleanupAutoUpdate: (() => void) | null = null;
|
|
21
|
+
private currentPlacement: Placement = 'bottom-start';
|
|
22
|
+
private currentMiddlewareData: MiddlewareData | null = null;
|
|
23
|
+
private boundHandleWindowResize: (() => void) | null = null;
|
|
24
|
+
|
|
25
|
+
constructor(component: any) {
|
|
26
|
+
this.component = component;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private createPortalAppendContainer(): HTMLElement {
|
|
30
|
+
const container = document.createElement('div');
|
|
31
|
+
container.style.position = 'absolute';
|
|
32
|
+
container.style.zIndex = '9999';
|
|
33
|
+
container.style.pointerEvents = 'none';
|
|
34
|
+
container.style.width = 'auto';
|
|
35
|
+
container.style.maxWidth = 'none';
|
|
36
|
+
container.style.minWidth = 'auto';
|
|
37
|
+
container.className = 'nile-dropdown-portal-append';
|
|
38
|
+
return container;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
positionPortalAppend(): void {
|
|
42
|
+
if (!this.portalContainer || !this.component.open) return;
|
|
43
|
+
|
|
44
|
+
this.measurePanelHeight();
|
|
45
|
+
this.computeFloatingUIPosition();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private measurePanelHeight(): void {
|
|
49
|
+
if (this.measuredPanelHeight || !this.portalContainer) return;
|
|
50
|
+
|
|
51
|
+
this.portalContainer.style.position = 'absolute';
|
|
52
|
+
this.portalContainer.style.visibility = 'hidden';
|
|
53
|
+
this.portalContainer.style.top = '0px';
|
|
54
|
+
this.portalContainer.style.left = '0px';
|
|
55
|
+
|
|
56
|
+
this.portalContainer.offsetHeight;
|
|
57
|
+
|
|
58
|
+
this.measuredPanelHeight = this.portalContainer.offsetHeight;
|
|
59
|
+
|
|
60
|
+
this.portalContainer.style.visibility = '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async computeFloatingUIPosition(): Promise<void> {
|
|
64
|
+
if (!this.portalContainer) return;
|
|
65
|
+
|
|
66
|
+
const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
67
|
+
this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
68
|
+
this.component;
|
|
69
|
+
const floatingElement = this.portalContainer;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const { x, y, placement, middlewareData } = await this.calculateFloatingUIPosition(
|
|
73
|
+
referenceElement,
|
|
74
|
+
floatingElement
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
this.applyFloatingUIPosition(floatingElement, referenceElement, x, y, placement, middlewareData);
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.warn('Floating UI positioning failed, falling back to simple positioning:', error);
|
|
81
|
+
this.fallbackPositioning();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private async calculateFloatingUIPosition(
|
|
86
|
+
referenceElement: HTMLElement,
|
|
87
|
+
floatingElement: HTMLElement
|
|
88
|
+
): Promise<{ x: number; y: number; placement: Placement; middlewareData: MiddlewareData }> {
|
|
89
|
+
const boundary = PortalUtils.findBoundaryElements(referenceElement);
|
|
90
|
+
const initialPlacement = this.getInitialPlacement();
|
|
91
|
+
const middleware = this.createFloatingUIMiddleware(boundary);
|
|
92
|
+
|
|
93
|
+
return await computePosition(referenceElement, floatingElement, {
|
|
94
|
+
placement: initialPlacement,
|
|
95
|
+
strategy: 'fixed',
|
|
96
|
+
middleware,
|
|
97
|
+
platform: this.createCustomPlatform()
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getInitialPlacement(): Placement {
|
|
102
|
+
return this.component.placement || 'bottom-start';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private createFloatingUIMiddleware(boundary: Element[] | undefined): ComputePositionConfig['middleware'] {
|
|
106
|
+
const middleware: ComputePositionConfig['middleware'] = [
|
|
107
|
+
offset(this.component.distance || 0),
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// Add size middleware if sync is specified
|
|
111
|
+
if (this.component.sync) {
|
|
112
|
+
middleware.push(
|
|
113
|
+
size({
|
|
114
|
+
apply: this.handleSizeMiddleware.bind(this),
|
|
115
|
+
padding: 10,
|
|
116
|
+
boundary: boundary
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add flip middleware
|
|
122
|
+
middleware.push(
|
|
123
|
+
flip({
|
|
124
|
+
fallbackPlacements: this.getFallbackPlacements(),
|
|
125
|
+
fallbackStrategy: 'bestFit',
|
|
126
|
+
padding: 10,
|
|
127
|
+
boundary: boundary
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Add shift middleware
|
|
132
|
+
middleware.push(
|
|
133
|
+
shift({
|
|
134
|
+
padding: 10,
|
|
135
|
+
crossAxis: true,
|
|
136
|
+
boundary: boundary
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return middleware;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private handleSizeMiddleware({ availableWidth, availableHeight, elements, rects }: {
|
|
144
|
+
availableWidth: number;
|
|
145
|
+
availableHeight: number;
|
|
146
|
+
elements: { floating: HTMLElement };
|
|
147
|
+
rects: { reference: { x: number; y: number; width: number; height: number } };
|
|
148
|
+
}): void {
|
|
149
|
+
const maxHeight = PortalUtils.calculateOptimalHeight(
|
|
150
|
+
rects.reference,
|
|
151
|
+
window.innerHeight,
|
|
152
|
+
this.currentPlacement
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (this.component.sync === 'width' || this.component.sync === 'both') {
|
|
156
|
+
elements.floating.style.width = `${rects.reference.width}px`;
|
|
157
|
+
}
|
|
158
|
+
if (this.component.sync === 'height' || this.component.sync === 'both') {
|
|
159
|
+
elements.floating.style.height = `${rects.reference.height}px`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
elements.floating.style.maxHeight = `${maxHeight}px`;
|
|
163
|
+
elements.floating.style.setProperty('--auto-size-available-width', `${availableWidth}px`);
|
|
164
|
+
elements.floating.style.setProperty('--auto-size-available-height', `${maxHeight}px`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private getFallbackPlacements(): Placement[] {
|
|
168
|
+
const basePlacement = this.component.placement || 'bottom-start';
|
|
169
|
+
const placements: Placement[] = [];
|
|
170
|
+
|
|
171
|
+
if (basePlacement.startsWith('top')) {
|
|
172
|
+
placements.push('bottom-start', 'bottom', 'bottom-end', 'top', 'top-end', 'right-start', 'left-start');
|
|
173
|
+
} else if (basePlacement.startsWith('bottom')) {
|
|
174
|
+
placements.push('top-start', 'top', 'top-end', 'bottom', 'bottom-end', 'right-start', 'left-start');
|
|
175
|
+
} else if (basePlacement.startsWith('left')) {
|
|
176
|
+
placements.push('right-start', 'right', 'right-end', 'left', 'left-end', 'top-start', 'bottom-start');
|
|
177
|
+
} else if (basePlacement.startsWith('right')) {
|
|
178
|
+
placements.push('left-start', 'left', 'left-end', 'right', 'right-end', 'top-start', 'bottom-start');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return placements;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private createCustomPlatform() {
|
|
185
|
+
return platform;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private applyFloatingUIPosition(
|
|
189
|
+
floatingElement: HTMLElement,
|
|
190
|
+
referenceElement: HTMLElement,
|
|
191
|
+
x: number,
|
|
192
|
+
y: number,
|
|
193
|
+
placement: Placement,
|
|
194
|
+
middlewareData: MiddlewareData
|
|
195
|
+
): void {
|
|
196
|
+
const referenceRect = referenceElement.getBoundingClientRect();
|
|
197
|
+
|
|
198
|
+
Object.assign(floatingElement.style, {
|
|
199
|
+
left: `${x}px`,
|
|
200
|
+
top: `${y}px`,
|
|
201
|
+
position: 'fixed',
|
|
202
|
+
pointerEvents: 'auto',
|
|
203
|
+
width: 'auto',
|
|
204
|
+
minWidth: 'auto'
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Apply sync width if needed
|
|
208
|
+
if (this.component.sync === 'width' || this.component.sync === 'both') {
|
|
209
|
+
floatingElement.style.width = `${referenceRect.width}px`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.currentPlacement = placement;
|
|
213
|
+
this.currentMiddlewareData = middlewareData;
|
|
214
|
+
|
|
215
|
+
PortalUtils.applyCollisionData(floatingElement, middlewareData, placement);
|
|
216
|
+
|
|
217
|
+
const placementClass = placement.split('-')[0];
|
|
218
|
+
floatingElement.className = `nile-dropdown-portal-append dropdown--open dropdown__panel--${placementClass}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private fallbackPositioning(): void {
|
|
222
|
+
if (!this.portalContainer) return;
|
|
223
|
+
|
|
224
|
+
const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
225
|
+
this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
226
|
+
this.component;
|
|
227
|
+
const rect = referenceElement.getBoundingClientRect();
|
|
228
|
+
const viewportHeight = window.innerHeight;
|
|
229
|
+
const viewportWidth = window.innerWidth;
|
|
230
|
+
|
|
231
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
232
|
+
const spaceAbove = rect.top;
|
|
233
|
+
const spaceRight = viewportWidth - rect.right;
|
|
234
|
+
const spaceLeft = rect.left;
|
|
235
|
+
|
|
236
|
+
let topPosition: number;
|
|
237
|
+
let leftPosition: number = rect.left;
|
|
238
|
+
let placementClass: string;
|
|
239
|
+
let maxHeight: number;
|
|
240
|
+
|
|
241
|
+
const placement = this.component.placement || 'bottom-start';
|
|
242
|
+
|
|
243
|
+
if (placement.startsWith('bottom')) {
|
|
244
|
+
maxHeight = Math.max(spaceBelow - 20, 100);
|
|
245
|
+
topPosition = rect.bottom + (this.component.distance || 0);
|
|
246
|
+
placementClass = 'bottom';
|
|
247
|
+
} else if (placement.startsWith('top')) {
|
|
248
|
+
maxHeight = Math.max(spaceAbove - 20, 100);
|
|
249
|
+
topPosition = Math.max(rect.top - maxHeight - (this.component.distance || 0), 10);
|
|
250
|
+
placementClass = 'top';
|
|
251
|
+
} else if (placement.startsWith('right')) {
|
|
252
|
+
maxHeight = Math.max(Math.min(spaceRight, spaceBelow, spaceAbove) - 20, 100);
|
|
253
|
+
topPosition = rect.top;
|
|
254
|
+
leftPosition = rect.right + (this.component.distance || 0);
|
|
255
|
+
placementClass = 'right';
|
|
256
|
+
} else {
|
|
257
|
+
maxHeight = Math.max(Math.min(spaceLeft, spaceBelow, spaceAbove) - 20, 100);
|
|
258
|
+
topPosition = rect.top;
|
|
259
|
+
leftPosition = Math.max(rect.left - 200 - (this.component.distance || 0), 10);
|
|
260
|
+
placementClass = 'left';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.portalContainer.style.left = `${leftPosition}px`;
|
|
264
|
+
this.portalContainer.style.top = `${topPosition}px`;
|
|
265
|
+
this.portalContainer.style.maxHeight = `${maxHeight}px`;
|
|
266
|
+
this.portalContainer.style.pointerEvents = 'auto';
|
|
267
|
+
this.portalContainer.style.width = 'auto';
|
|
268
|
+
this.portalContainer.style.minWidth = 'auto';
|
|
269
|
+
|
|
270
|
+
if (this.component.sync === 'width' || this.component.sync === 'both') {
|
|
271
|
+
this.portalContainer.style.width = `${rect.width}px`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.portalContainer.className = `nile-dropdown-portal-append dropdown__panel--${placementClass}`;
|
|
275
|
+
|
|
276
|
+
this.calculateAndSetAutoSizeProperties(rect, topPosition, placementClass);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private calculateAndSetAutoSizeProperties(rect: DOMRect, topPosition: number, placementClass: string): void {
|
|
280
|
+
if (!this.portalContainer) return;
|
|
281
|
+
|
|
282
|
+
const viewportHeight = window.innerHeight;
|
|
283
|
+
const viewportWidth = window.innerWidth;
|
|
284
|
+
|
|
285
|
+
let availableHeight: number;
|
|
286
|
+
let availableWidth: number = viewportWidth - rect.left - 10;
|
|
287
|
+
|
|
288
|
+
if (placementClass === 'top') {
|
|
289
|
+
availableHeight = rect.top - 10;
|
|
290
|
+
} else if (placementClass === 'bottom') {
|
|
291
|
+
availableHeight = viewportHeight - rect.bottom - 10;
|
|
292
|
+
} else if (placementClass === 'left') {
|
|
293
|
+
availableHeight = Math.min(viewportHeight - rect.top, rect.bottom);
|
|
294
|
+
availableWidth = rect.left - 10;
|
|
295
|
+
} else {
|
|
296
|
+
availableHeight = Math.min(viewportHeight - rect.top, rect.bottom);
|
|
297
|
+
availableWidth = viewportWidth - rect.right - 10;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.portalContainer.style.setProperty('--auto-size-available-height', `${Math.max(availableHeight, 100)}px`);
|
|
301
|
+
this.portalContainer.style.setProperty('--auto-size-available-width', `${Math.max(availableWidth, 200)}px`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
updatePortalAppendPosition(): void {
|
|
305
|
+
if (this.component.portal && this.portalContainer) {
|
|
306
|
+
this.positionPortalAppend();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
handleWindowResize(): void {
|
|
311
|
+
if (this.component.portal && this.portalContainer) {
|
|
312
|
+
this.positionPortalAppend();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private setupAutoUpdatePositioning(): void {
|
|
317
|
+
if (!this.portalContainer || !this.component) return;
|
|
318
|
+
|
|
319
|
+
this.cleanupAutoUpdatePositioning();
|
|
320
|
+
|
|
321
|
+
const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
322
|
+
this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
323
|
+
this.component;
|
|
324
|
+
|
|
325
|
+
this.cleanupAutoUpdate = autoUpdate(
|
|
326
|
+
referenceElement,
|
|
327
|
+
this.portalContainer,
|
|
328
|
+
() => {
|
|
329
|
+
this.computeFloatingUIPosition();
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
ancestorScroll: true,
|
|
333
|
+
ancestorResize: true,
|
|
334
|
+
elementResize: true,
|
|
335
|
+
layoutShift: true,
|
|
336
|
+
animationFrame: true
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private cleanupAutoUpdatePositioning(): void {
|
|
342
|
+
if (this.cleanupAutoUpdate) {
|
|
343
|
+
this.cleanupAutoUpdate();
|
|
344
|
+
this.cleanupAutoUpdate = null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private injectStylesToDocument(): void {
|
|
349
|
+
if (!this.portalContainer) return;
|
|
350
|
+
|
|
351
|
+
const styleId = PortalUtils.generateStyleId();
|
|
352
|
+
|
|
353
|
+
if (document.getElementById(styleId)) return;
|
|
354
|
+
|
|
355
|
+
const componentStyles = (this.component.constructor as any).styles;
|
|
356
|
+
if (!componentStyles) return;
|
|
357
|
+
|
|
358
|
+
const styleElement = document.createElement('style');
|
|
359
|
+
styleElement.id = styleId;
|
|
360
|
+
styleElement.textContent = PortalUtils.extractStylesAsCSS(componentStyles);
|
|
361
|
+
|
|
362
|
+
document.head.appendChild(styleElement);
|
|
363
|
+
|
|
364
|
+
(this.portalContainer as any).__injectedStyleId = styleId;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private adoptStylesToPortalAppend(): void {
|
|
368
|
+
if (!this.portalContainer) return;
|
|
369
|
+
this.injectStylesToDocument();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
setupPortalAppend(): void {
|
|
373
|
+
if (!this.component.portal) return;
|
|
374
|
+
|
|
375
|
+
this.component.updateComplete.then(() => {
|
|
376
|
+
setTimeout(() => {
|
|
377
|
+
if (this.component.open) {
|
|
378
|
+
this.clonedPanel = this.createPortalPanel();
|
|
379
|
+
|
|
380
|
+
this.portalContainer = this.createPortalAppendContainer();
|
|
381
|
+
this.portalContainer.appendChild(this.clonedPanel);
|
|
382
|
+
document.body.appendChild(this.portalContainer);
|
|
383
|
+
|
|
384
|
+
this.adoptStylesToPortalAppend();
|
|
385
|
+
|
|
386
|
+
this.clonedPanel.style.display = '';
|
|
387
|
+
this.positionPortalAppend();
|
|
388
|
+
|
|
389
|
+
this.setupPortalEventListeners();
|
|
390
|
+
|
|
391
|
+
this.setupAutoUpdatePositioning();
|
|
392
|
+
|
|
393
|
+
this.boundHandleWindowResize = this.handleWindowResize.bind(this);
|
|
394
|
+
window.addEventListener('resize', this.boundHandleWindowResize);
|
|
395
|
+
}
|
|
396
|
+
}, 10);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private createPortalPanel(): HTMLElement {
|
|
401
|
+
return PortalContentUtils.createPortalPanel(this.component);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private setupPortalEventListeners(): void {
|
|
405
|
+
PortalEventUtils.setupPortalEventListeners(this.clonedPanel!, this.component);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
cleanupPortalAppend(): void {
|
|
409
|
+
this.cleanupAutoUpdatePositioning();
|
|
410
|
+
|
|
411
|
+
if (this.portalContainer && this.portalContainer.parentNode) {
|
|
412
|
+
const injectedStyleId = (this.portalContainer as any).__injectedStyleId;
|
|
413
|
+
if (injectedStyleId) {
|
|
414
|
+
const styleElement = document.getElementById(injectedStyleId);
|
|
415
|
+
if (styleElement) {
|
|
416
|
+
styleElement.remove();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.portalContainer.parentNode.removeChild(this.portalContainer);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (this.boundHandleWindowResize) {
|
|
424
|
+
window.removeEventListener('resize', this.boundHandleWindowResize);
|
|
425
|
+
this.boundHandleWindowResize = null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
this.portalContainer = null;
|
|
429
|
+
this.clonedPanel = null;
|
|
430
|
+
this.measuredPanelHeight = null;
|
|
431
|
+
this.currentPlacement = 'bottom-start';
|
|
432
|
+
this.currentMiddlewareData = null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
get portalContainerElement(): HTMLElement | null {
|
|
436
|
+
return this.portalContainer;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
resetMeasuredHeight(): void {
|
|
440
|
+
this.measuredPanelHeight = null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
forceReposition(): void {
|
|
444
|
+
if (this.portalContainer) {
|
|
445
|
+
this.computeFloatingUIPosition();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
getCurrentPlacement(): Placement {
|
|
450
|
+
return this.currentPlacement;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
getCurrentMiddlewareData(): MiddlewareData | null {
|
|
454
|
+
return this.currentMiddlewareData;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
isUsingFloatingUI(): boolean {
|
|
458
|
+
return this.cleanupAutoUpdate !== null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
isPositioningOptimal(): boolean {
|
|
462
|
+
if (!this.portalContainer || !this.currentMiddlewareData) return true;
|
|
463
|
+
|
|
464
|
+
const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
465
|
+
this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
|
|
466
|
+
this.component;
|
|
467
|
+
const rect = referenceElement.getBoundingClientRect();
|
|
468
|
+
const viewportHeight = window.innerHeight;
|
|
469
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
470
|
+
const spaceAbove = rect.top;
|
|
471
|
+
|
|
472
|
+
const isAbove = this.currentPlacement.startsWith('top');
|
|
473
|
+
const isBelow = this.currentPlacement.startsWith('bottom');
|
|
474
|
+
|
|
475
|
+
if (isAbove && spaceBelow > spaceAbove) return false;
|
|
476
|
+
if (isBelow && spaceAbove > spaceBelow) return false;
|
|
477
|
+
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
updatePortalPanel(): void {
|
|
482
|
+
if (this.portalContainer && this.clonedPanel) {
|
|
483
|
+
PortalContentUtils.updatePortalPanel(this.clonedPanel, this.component);
|
|
484
|
+
this.forceReposition();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|