@ngutil/floating 0.0.27
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 +23 -0
- package/esm2022/floating/floating-ref.mjs +81 -0
- package/esm2022/floating/floating.service.mjs +123 -0
- package/esm2022/floating/index.mjs +4 -0
- package/esm2022/floating/traits/_base.mjs +3 -0
- package/esm2022/floating/traits/index.mjs +3 -0
- package/esm2022/floating/traits/position-calc.mjs +17 -0
- package/esm2022/floating/traits/position.mjs +71 -0
- package/esm2022/index.mjs +3 -0
- package/esm2022/layer/backdrop-ref.mjs +50 -0
- package/esm2022/layer/child-ref.mjs +44 -0
- package/esm2022/layer/container-ref.mjs +37 -0
- package/esm2022/layer/index.mjs +5 -0
- package/esm2022/layer/layer.service.mjs +131 -0
- package/esm2022/layer/portal-ref.mjs +52 -0
- package/esm2022/ngutil-floating.mjs +5 -0
- package/fesm2022/ngutil-floating.mjs +588 -0
- package/fesm2022/ngutil-floating.mjs.map +1 -0
- package/floating/floating-ref.d.ts +53 -0
- package/floating/floating.service.d.ts +54 -0
- package/floating/index.d.ts +3 -0
- package/floating/traits/_base.d.ts +6 -0
- package/floating/traits/index.d.ts +2 -0
- package/floating/traits/position-calc.d.ts +26 -0
- package/floating/traits/position.d.ts +52 -0
- package/index.d.ts +2 -0
- package/layer/backdrop-ref.d.ts +30 -0
- package/layer/child-ref.d.ts +22 -0
- package/layer/container-ref.d.ts +17 -0
- package/layer/index.d.ts +4 -0
- package/layer/layer.service.d.ts +28 -0
- package/layer/portal-ref.d.ts +32 -0
- package/package.json +33 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { ElementRef, Injector, ComponentFactoryResolver, ViewContainerRef, InjectionToken, inject, Directive, Inject, Optional, Injectable, TemplateRef } from '@angular/core';
|
|
3
|
+
import { Observable, combineLatest, of, ReplaySubject, filter, shareReplay, takeUntil, map, EMPTY, merge } from 'rxjs';
|
|
4
|
+
import { DimensionWatcher, RectWatcher } from '@ngutil/style';
|
|
5
|
+
import { StateChain, toSorted } from '@ngutil/common';
|
|
6
|
+
import { CoverService } from '@ngutil/graphics';
|
|
7
|
+
import { DomPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
|
|
8
|
+
|
|
9
|
+
class FloatingTrait {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function computePosition({ floating, anchor, placement, options }) {
|
|
13
|
+
// TODO: jelenleg csak center van
|
|
14
|
+
const maxWidth = placement.width;
|
|
15
|
+
const maxHeight = placement.height;
|
|
16
|
+
const cf = {
|
|
17
|
+
current: {
|
|
18
|
+
x: (maxWidth - floating.width) / 2,
|
|
19
|
+
y: (maxHeight - floating.height) / 2,
|
|
20
|
+
width: Math.min(maxHeight, floating.width),
|
|
21
|
+
height: Math.min(maxHeight, floating.height)
|
|
22
|
+
},
|
|
23
|
+
max: { width: maxWidth, height: maxHeight },
|
|
24
|
+
min: { width: 0, height: 0 }
|
|
25
|
+
};
|
|
26
|
+
return { floating: cf };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class FloatingAnchorRef extends ElementRef {
|
|
30
|
+
}
|
|
31
|
+
class FloatingPlacementRef extends ElementRef {
|
|
32
|
+
}
|
|
33
|
+
class PositionTrait extends FloatingTrait {
|
|
34
|
+
constructor(options) {
|
|
35
|
+
super();
|
|
36
|
+
this.options = options;
|
|
37
|
+
this.name = "position";
|
|
38
|
+
if (!options.anchor) {
|
|
39
|
+
options.anchor = { ref: "viewport", align: "center middle" };
|
|
40
|
+
}
|
|
41
|
+
if (!options.placement) {
|
|
42
|
+
options.placement = { ref: "viewport" };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
connect(floatingRef) {
|
|
46
|
+
return new Observable((dest) => {
|
|
47
|
+
const injector = floatingRef.container.injector;
|
|
48
|
+
const dimWatcher = injector.get(DimensionWatcher);
|
|
49
|
+
const rectWatcher = injector.get(RectWatcher);
|
|
50
|
+
const watches = {
|
|
51
|
+
floating: dimWatcher.watch(floatingRef.container, "border-box"),
|
|
52
|
+
anchor: refWatcher(rectWatcher, this.options.anchor.ref, floatingRef),
|
|
53
|
+
placement: refWatcher(rectWatcher, this.options.placement.ref, floatingRef)
|
|
54
|
+
};
|
|
55
|
+
return combineLatest(watches).subscribe(({ floating, anchor, placement }) => {
|
|
56
|
+
const res = new FloatingPosition(this.options, floating, anchor, placement);
|
|
57
|
+
res.apply(floatingRef);
|
|
58
|
+
dest.next(res);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function refWatcher(rectWatcher, ref, floatingRef) {
|
|
64
|
+
if (ref === "layer") {
|
|
65
|
+
return rectWatcher.watch(floatingRef.layerSvc.root, "border-box");
|
|
66
|
+
}
|
|
67
|
+
else if (ref === "viewport" || ref instanceof Window) {
|
|
68
|
+
return rectWatcher.watch(window, "border-box");
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return rectWatcher.watch(ref, "border-box");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function position(options) {
|
|
75
|
+
return new PositionTrait(options);
|
|
76
|
+
}
|
|
77
|
+
class FloatingPosition {
|
|
78
|
+
constructor(options, floating, anchor, placement) {
|
|
79
|
+
this.options = options;
|
|
80
|
+
this.floating = floating;
|
|
81
|
+
this.anchor = anchor;
|
|
82
|
+
this.placement = placement;
|
|
83
|
+
this.computed = computePosition({ floating, anchor, placement, options });
|
|
84
|
+
}
|
|
85
|
+
apply(floatingRef) {
|
|
86
|
+
if (this.computed == null) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const floatingEl = floatingRef.container.nativeElement;
|
|
90
|
+
floatingEl.style.left = `${this.computed.floating.current.x}px`;
|
|
91
|
+
floatingEl.style.top = `${this.computed.floating.current.y}px`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// TODO: disposing, disposed
|
|
96
|
+
class ChildRef extends ElementRef {
|
|
97
|
+
/**
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
set zIndex(val) {
|
|
101
|
+
if (this._zIndex !== val) {
|
|
102
|
+
this._zIndex = val;
|
|
103
|
+
this.nativeElement.style.zIndex = String(val);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
get zIndex() {
|
|
107
|
+
return this._zIndex;
|
|
108
|
+
}
|
|
109
|
+
constructor(nativeElement) {
|
|
110
|
+
super(nativeElement);
|
|
111
|
+
this.state = new StateChain({
|
|
112
|
+
showing: {},
|
|
113
|
+
shown: {},
|
|
114
|
+
disposing: { cancellable: false },
|
|
115
|
+
disposed: { cancellable: false, order: "sequential" }
|
|
116
|
+
});
|
|
117
|
+
this._zIndex = -1;
|
|
118
|
+
this.state.on("disposed", () => this.destroy());
|
|
119
|
+
// this.state.current$.subscribe(state => {
|
|
120
|
+
// console.log(this, state)
|
|
121
|
+
// })
|
|
122
|
+
}
|
|
123
|
+
dispose() {
|
|
124
|
+
if (this.state == null) {
|
|
125
|
+
return of(null);
|
|
126
|
+
}
|
|
127
|
+
return this.state.run(["disposing", "disposed"]);
|
|
128
|
+
}
|
|
129
|
+
destroy() {
|
|
130
|
+
this.nativeElement.parentElement?.removeChild(this.nativeElement);
|
|
131
|
+
this.state.destroy();
|
|
132
|
+
delete this.state;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class ContainerRef extends ChildRef {
|
|
137
|
+
constructor(options) {
|
|
138
|
+
super(createElement(options));
|
|
139
|
+
this.options = options;
|
|
140
|
+
this.injectorName = "ContainerRef";
|
|
141
|
+
const providers = options.providers || [];
|
|
142
|
+
this.injector = Injector.create({
|
|
143
|
+
providers: [...this.getProviders(), ...providers],
|
|
144
|
+
parent: options.injector,
|
|
145
|
+
name: this.injectorName
|
|
146
|
+
});
|
|
147
|
+
this.state.on("disposed", () => {
|
|
148
|
+
delete this.options;
|
|
149
|
+
delete this.injector;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
getProviders() {
|
|
153
|
+
return [
|
|
154
|
+
{ provide: ChildRef, useValue: this },
|
|
155
|
+
{ provide: ContainerRef, useValue: this }
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function createElement(options) {
|
|
160
|
+
const div = document.createElement("div");
|
|
161
|
+
div.style.position = "absolute";
|
|
162
|
+
div.style.top = "0";
|
|
163
|
+
div.style.left = "0";
|
|
164
|
+
div.style.width = "max-content";
|
|
165
|
+
if (options.classes) {
|
|
166
|
+
div.classList.add(...options.classes);
|
|
167
|
+
}
|
|
168
|
+
return div;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
class BackdropRef extends ChildRef {
|
|
172
|
+
static from(cover, injector, options) {
|
|
173
|
+
const ref = new BackdropRef(document.createElement("div"), cover, injector, options);
|
|
174
|
+
// TODO: kérdéses
|
|
175
|
+
// options.under.state.control(ref.state)
|
|
176
|
+
return ref;
|
|
177
|
+
}
|
|
178
|
+
#coverSub;
|
|
179
|
+
set visible(visible) {
|
|
180
|
+
if (this.#visible !== visible) {
|
|
181
|
+
this.#visible = visible;
|
|
182
|
+
this.nativeElement.style.visibility = visible ? "visible" : "hidden";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
get visible() {
|
|
186
|
+
return this.#visible;
|
|
187
|
+
}
|
|
188
|
+
#visible = true;
|
|
189
|
+
constructor(nativeElement, coverSvc, injector, options) {
|
|
190
|
+
super(nativeElement);
|
|
191
|
+
this.coverSvc = coverSvc;
|
|
192
|
+
this.injector = injector;
|
|
193
|
+
this.options = options;
|
|
194
|
+
nativeElement.style.position = "absolute";
|
|
195
|
+
nativeElement.style.top =
|
|
196
|
+
nativeElement.style.right =
|
|
197
|
+
nativeElement.style.bottom =
|
|
198
|
+
nativeElement.style.left =
|
|
199
|
+
"0px";
|
|
200
|
+
this.under = options.under;
|
|
201
|
+
if (options.type === "solid") {
|
|
202
|
+
this.#coverSub = this.coverSvc.solid({ container: nativeElement, color: options.color }).subscribe();
|
|
203
|
+
this.group = `${options.color === "transparent" ? "transparent" : "solid"}`;
|
|
204
|
+
}
|
|
205
|
+
else if (options.type === "crop") {
|
|
206
|
+
this.#coverSub = this.coverSvc
|
|
207
|
+
.crop({ container: nativeElement, color: options.color, crop: options.crop })
|
|
208
|
+
.subscribe();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
show() {
|
|
212
|
+
return this.state.run(["showing", "shown"]);
|
|
213
|
+
}
|
|
214
|
+
destroy() {
|
|
215
|
+
this.#coverSub?.unsubscribe();
|
|
216
|
+
super.destroy();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
class PortalRef extends ContainerRef {
|
|
221
|
+
constructor(options) {
|
|
222
|
+
super(options);
|
|
223
|
+
this.injectorName = "PortalRef";
|
|
224
|
+
this.outlet = new DomPortalOutlet(this.nativeElement, undefined, undefined, this.injector);
|
|
225
|
+
this.state.on("disposed", () => {
|
|
226
|
+
this.outlet.dispose();
|
|
227
|
+
delete this.outlet;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
getProviders() {
|
|
231
|
+
return [...super.getProviders(), { provide: PortalRef, useValue: this }];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
class ComponentPortalRef extends PortalRef {
|
|
235
|
+
constructor(component, options) {
|
|
236
|
+
super(options);
|
|
237
|
+
this.component = component;
|
|
238
|
+
this.injectorName = "ComponentPortalRef";
|
|
239
|
+
const resolver = this.injector.get(ComponentFactoryResolver);
|
|
240
|
+
const vcr = this.injector.get(ViewContainerRef);
|
|
241
|
+
this.portal = new ComponentPortal(component, options.viewContainerRef || vcr, this.injector, resolver);
|
|
242
|
+
this.outlet.attach(this.portal);
|
|
243
|
+
this.state.on("disposed", () => {
|
|
244
|
+
this.portal.isAttached && this.portal.detach();
|
|
245
|
+
delete this.portal;
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
getProviders() {
|
|
249
|
+
return [...super.getProviders(), { provide: ComponentPortalRef, useValue: this }];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
class TemplatePortalRef extends PortalRef {
|
|
253
|
+
constructor(template, options) {
|
|
254
|
+
super(options);
|
|
255
|
+
this.template = template;
|
|
256
|
+
this.injectorName = "TemplatePortalRef";
|
|
257
|
+
this.portal = new TemplatePortal(template, options.viewContainerRef, options.context);
|
|
258
|
+
this.outlet.attach(this.portal);
|
|
259
|
+
this.state.on("disposed", () => {
|
|
260
|
+
this.portal.isAttached && this.portal.detach();
|
|
261
|
+
delete this.portal;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
getProviders() {
|
|
265
|
+
return [...super.getProviders(), { provide: TemplatePortalRef, useValue: this }];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const LAYER_ZINDEX_START = new InjectionToken("LAYER_ZINDEX_START");
|
|
270
|
+
// TODO: ELEVATION_STEP config with injection
|
|
271
|
+
// TODO: ELEVATION_START config with injection
|
|
272
|
+
class LayerService {
|
|
273
|
+
#cover;
|
|
274
|
+
#injector;
|
|
275
|
+
// readonly #el = this.root.nativeElement
|
|
276
|
+
#children;
|
|
277
|
+
#zIndexStart;
|
|
278
|
+
// readonly #backdrop: Map<>
|
|
279
|
+
constructor(zIndexStart) {
|
|
280
|
+
this.#cover = inject(CoverService);
|
|
281
|
+
this.#injector = inject(Injector);
|
|
282
|
+
this.root = inject(ElementRef);
|
|
283
|
+
// readonly #el = this.root.nativeElement
|
|
284
|
+
this.#children = [];
|
|
285
|
+
if (zIndexStart != null) {
|
|
286
|
+
this.#zIndexStart = zIndexStart;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.#zIndexStart = 10000;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
newComponentPortal(component, options) {
|
|
293
|
+
if (!options.injector) {
|
|
294
|
+
options = { ...options, injector: this.#injector };
|
|
295
|
+
}
|
|
296
|
+
return this.append(new ComponentPortalRef(component, options));
|
|
297
|
+
}
|
|
298
|
+
newTemplatePortal(tpl, options) {
|
|
299
|
+
if (!options.injector) {
|
|
300
|
+
options = { ...options, injector: this.#injector };
|
|
301
|
+
}
|
|
302
|
+
return this.append(new TemplatePortalRef(tpl, options));
|
|
303
|
+
}
|
|
304
|
+
newContainer(options) {
|
|
305
|
+
if (!options.injector) {
|
|
306
|
+
options = { ...options, injector: this.#injector };
|
|
307
|
+
}
|
|
308
|
+
return this.append(new ContainerRef(options));
|
|
309
|
+
}
|
|
310
|
+
newBackdrop(options) {
|
|
311
|
+
return this.append(BackdropRef.from(this.#cover, this.#injector, options));
|
|
312
|
+
}
|
|
313
|
+
append(ref) {
|
|
314
|
+
if (!this.#children.includes(ref)) {
|
|
315
|
+
this.#children.push(ref);
|
|
316
|
+
this.#update();
|
|
317
|
+
this.root.nativeElement.appendChild(ref.nativeElement);
|
|
318
|
+
ref.state.on("disposed", () => this.#remove(ref));
|
|
319
|
+
}
|
|
320
|
+
return ref;
|
|
321
|
+
}
|
|
322
|
+
#remove(ref) {
|
|
323
|
+
const idx = this.#children.indexOf(ref);
|
|
324
|
+
if (idx > -1) {
|
|
325
|
+
this.#children.splice(idx, 1);
|
|
326
|
+
this.#update();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
#update() {
|
|
330
|
+
const children = toSorted(this.#children, sortByBackdrop);
|
|
331
|
+
let zIndex = this.#zIndexStart;
|
|
332
|
+
for (const child of children) {
|
|
333
|
+
child.zIndex = zIndex;
|
|
334
|
+
zIndex += 1;
|
|
335
|
+
}
|
|
336
|
+
children.sort(sortByZIndexDesc);
|
|
337
|
+
let hasBackdrop = false;
|
|
338
|
+
for (const child of children) {
|
|
339
|
+
if (child instanceof BackdropRef && child.options.type === "solid") {
|
|
340
|
+
child.visible = !hasBackdrop;
|
|
341
|
+
hasBackdrop = true;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: LayerService, deps: [{ token: LAYER_ZINDEX_START, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
346
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.6", type: LayerService, ngImport: i0 }); }
|
|
347
|
+
}
|
|
348
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: LayerService, decorators: [{
|
|
349
|
+
type: Directive
|
|
350
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
351
|
+
type: Inject,
|
|
352
|
+
args: [LAYER_ZINDEX_START]
|
|
353
|
+
}, {
|
|
354
|
+
type: Optional
|
|
355
|
+
}] }] });
|
|
356
|
+
class RootLayer extends LayerService {
|
|
357
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: RootLayer, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
358
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.6", type: RootLayer, isStandalone: true, selector: "body", providers: [{ provide: LayerService, useExisting: RootLayer }], exportAs: ["nuRootLayer"], usesInheritance: true, ngImport: i0 }); }
|
|
359
|
+
}
|
|
360
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: RootLayer, decorators: [{
|
|
361
|
+
type: Directive,
|
|
362
|
+
args: [{
|
|
363
|
+
selector: "body",
|
|
364
|
+
exportAs: "nuRootLayer",
|
|
365
|
+
standalone: true,
|
|
366
|
+
providers: [{ provide: LayerService, useExisting: RootLayer }]
|
|
367
|
+
}]
|
|
368
|
+
}] });
|
|
369
|
+
class IndividualLayer extends LayerService {
|
|
370
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: IndividualLayer, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
371
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.6", type: IndividualLayer, isStandalone: true, providers: [{ provide: LayerService, useExisting: IndividualLayer }], usesInheritance: true, ngImport: i0 }); }
|
|
372
|
+
}
|
|
373
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: IndividualLayer, decorators: [{
|
|
374
|
+
type: Directive,
|
|
375
|
+
args: [{
|
|
376
|
+
standalone: true,
|
|
377
|
+
providers: [{ provide: LayerService, useExisting: IndividualLayer }]
|
|
378
|
+
}]
|
|
379
|
+
}] });
|
|
380
|
+
function sortByBackdrop(a, b) {
|
|
381
|
+
if (a instanceof BackdropRef && a.under === b) {
|
|
382
|
+
return -1;
|
|
383
|
+
}
|
|
384
|
+
else if (b instanceof BackdropRef && b.under === a) {
|
|
385
|
+
return 1;
|
|
386
|
+
}
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
function sortByZIndexDesc(a, b) {
|
|
390
|
+
return b.zIndex - a.zIndex;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const TRAITS = new InjectionToken("TRAITS");
|
|
394
|
+
class FloatingRef {
|
|
395
|
+
#traits;
|
|
396
|
+
#untilCleanup;
|
|
397
|
+
#untilDisposed;
|
|
398
|
+
constructor(layerSvc, container, traits) {
|
|
399
|
+
this.layerSvc = layerSvc;
|
|
400
|
+
this.container = container;
|
|
401
|
+
this.channel = new ReplaySubject(1);
|
|
402
|
+
this.state = new StateChain({
|
|
403
|
+
init: {},
|
|
404
|
+
showing: {},
|
|
405
|
+
shown: {},
|
|
406
|
+
closing: { cancellable: false, order: "sequential" },
|
|
407
|
+
disposing: { cancellable: false },
|
|
408
|
+
disposed: { cancellable: false, order: "sequential" },
|
|
409
|
+
cleanup: { cancellable: false, order: "sequential" }
|
|
410
|
+
});
|
|
411
|
+
this.#traits = {};
|
|
412
|
+
this.#untilCleanup = this.state.current$.pipe(filter(state => state === "cleanup"), shareReplay(1));
|
|
413
|
+
this.#untilDisposed = this.state.current$.pipe(filter(state => state === "cleanup"), shareReplay(1));
|
|
414
|
+
this.#traits = traits;
|
|
415
|
+
this.traitState$ = this.#traitState().pipe(shareReplay(1));
|
|
416
|
+
const sub = this.state.current$.subscribe(state => {
|
|
417
|
+
this.channel.next({ type: state });
|
|
418
|
+
});
|
|
419
|
+
this.state.on("init", () => {
|
|
420
|
+
this.traitState$.pipe(takeUntil(this.#untilCleanup)).subscribe();
|
|
421
|
+
});
|
|
422
|
+
this.state.on("disposed", () => {
|
|
423
|
+
sub.unsubscribe();
|
|
424
|
+
});
|
|
425
|
+
this.state.control(container.state);
|
|
426
|
+
}
|
|
427
|
+
show() {
|
|
428
|
+
return this.state.run(["init", "showing", "shown"]);
|
|
429
|
+
}
|
|
430
|
+
hide() {
|
|
431
|
+
return this.state.run(["disposing", "disposed", "cleanup"]);
|
|
432
|
+
}
|
|
433
|
+
close() {
|
|
434
|
+
return this.state.run(["closing", "disposing", "disposed", "cleanup"]);
|
|
435
|
+
}
|
|
436
|
+
watchTrait(name) {
|
|
437
|
+
return this.traitState$.pipe(takeUntil(this.#untilDisposed), filter(event => event.name === name), map(event => event.data), shareReplay(1));
|
|
438
|
+
}
|
|
439
|
+
#traitState() {
|
|
440
|
+
const src = [];
|
|
441
|
+
for (const [k, v] of Object.entries(this.#traits)) {
|
|
442
|
+
src.push(v.connect(this).pipe(takeUntil(this.#untilCleanup), map(result => {
|
|
443
|
+
return { name: k, data: result };
|
|
444
|
+
})));
|
|
445
|
+
}
|
|
446
|
+
if (src.length === 0) {
|
|
447
|
+
return EMPTY;
|
|
448
|
+
}
|
|
449
|
+
else if (src.length === 1) {
|
|
450
|
+
return src[0];
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
return merge(...src);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingRef, deps: [{ token: LayerService }, { token: ContainerRef }, { token: TRAITS }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
457
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingRef }); }
|
|
458
|
+
}
|
|
459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingRef, decorators: [{
|
|
460
|
+
type: Injectable
|
|
461
|
+
}], ctorParameters: () => [{ type: LayerService }, { type: ContainerRef }, { type: undefined, decorators: [{
|
|
462
|
+
type: Inject,
|
|
463
|
+
args: [TRAITS]
|
|
464
|
+
}] }] });
|
|
465
|
+
|
|
466
|
+
// export type FloatingTrait = (...args: any[]) => (traits: object) => Observable<object>
|
|
467
|
+
class FloatingFactory {
|
|
468
|
+
constructor(layer) {
|
|
469
|
+
this.layer = layer;
|
|
470
|
+
this.traits = {};
|
|
471
|
+
this.subscribe = (...args) => this.show().subscribe(...args);
|
|
472
|
+
}
|
|
473
|
+
trait(...traits) {
|
|
474
|
+
for (const trait of traits) {
|
|
475
|
+
if (Array.isArray(trait)) {
|
|
476
|
+
this.trait(...trait);
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
this.traits[trait.name] = trait;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return this;
|
|
483
|
+
}
|
|
484
|
+
show() {
|
|
485
|
+
return new Observable((dest) => {
|
|
486
|
+
let disposed = false;
|
|
487
|
+
const ref = this.create();
|
|
488
|
+
const channelSub = ref.channel.subscribe(event => {
|
|
489
|
+
dest.next(event);
|
|
490
|
+
if (event.type === "disposed") {
|
|
491
|
+
disposed = true;
|
|
492
|
+
dest.complete();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
const showSub = ref.show().subscribe();
|
|
496
|
+
return () => {
|
|
497
|
+
showSub.unsubscribe();
|
|
498
|
+
channelSub.unsubscribe();
|
|
499
|
+
if (!disposed) {
|
|
500
|
+
const dispose$ = ref.channel.subscribe(event => {
|
|
501
|
+
if (event.type === "disposed") {
|
|
502
|
+
hideSub.unsubscribe();
|
|
503
|
+
dispose$.unsubscribe();
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
const hideSub = ref.hide().subscribe();
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
providers(providers) {
|
|
512
|
+
if (!providers) {
|
|
513
|
+
providers = [];
|
|
514
|
+
}
|
|
515
|
+
providers = [
|
|
516
|
+
...providers,
|
|
517
|
+
{ provide: TRAITS, useValue: this.traits },
|
|
518
|
+
{ provide: LayerService, useValue: this.layer },
|
|
519
|
+
FloatingRef
|
|
520
|
+
];
|
|
521
|
+
return providers;
|
|
522
|
+
}
|
|
523
|
+
position(options) {
|
|
524
|
+
return this.trait(position(options));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
class FloatingTemplateFactory extends FloatingFactory {
|
|
528
|
+
constructor(layer, tpl, options) {
|
|
529
|
+
super(layer);
|
|
530
|
+
this.tpl = tpl;
|
|
531
|
+
this.options = options;
|
|
532
|
+
}
|
|
533
|
+
create() {
|
|
534
|
+
const options = { ...this.options };
|
|
535
|
+
options.providers = this.providers(options.providers);
|
|
536
|
+
const container = this.layer.newTemplatePortal(this.tpl, options);
|
|
537
|
+
return container.injector.get(FloatingRef);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
class FloatingComponentFactory extends FloatingFactory {
|
|
541
|
+
constructor(layer, component, options) {
|
|
542
|
+
super(layer);
|
|
543
|
+
this.component = component;
|
|
544
|
+
this.options = options;
|
|
545
|
+
}
|
|
546
|
+
create() {
|
|
547
|
+
const options = { ...this.options };
|
|
548
|
+
options.providers = this.providers(options.providers);
|
|
549
|
+
const container = this.layer.newComponentPortal(this.component, options);
|
|
550
|
+
return container.injector.get(FloatingRef);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* @example
|
|
555
|
+
* ```typescript
|
|
556
|
+
* class SomeComponent {}
|
|
557
|
+
*
|
|
558
|
+
* class SomeList {
|
|
559
|
+
* readonly floating = inject(FloatingService)
|
|
560
|
+
*
|
|
561
|
+
* showComponent() {
|
|
562
|
+
* this.floating.from(SomeComponent).traits(position(), backdrop()).subscribe()
|
|
563
|
+
* }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
566
|
+
class FloatingService {
|
|
567
|
+
#layer = inject(LayerService);
|
|
568
|
+
from(value, opts) {
|
|
569
|
+
if (value instanceof TemplateRef) {
|
|
570
|
+
return new FloatingTemplateFactory(this.#layer, value, opts);
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
return new FloatingComponentFactory(this.#layer, value, opts);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
577
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingService }); }
|
|
578
|
+
}
|
|
579
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: FloatingService, decorators: [{
|
|
580
|
+
type: Injectable
|
|
581
|
+
}] });
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Generated bundle index. Do not edit.
|
|
585
|
+
*/
|
|
586
|
+
|
|
587
|
+
export { BackdropRef, ChildRef, ComponentPortalRef, FloatingAnchorRef, FloatingComponentFactory, FloatingFactory, FloatingPlacementRef, FloatingPosition, FloatingRef, FloatingService, FloatingTemplateFactory, IndividualLayer, LAYER_ZINDEX_START, LayerService, PortalRef, PositionTrait, RootLayer, TRAITS, TemplatePortalRef, computePosition, position };
|
|
588
|
+
//# sourceMappingURL=ngutil-floating.mjs.map
|