@ngutil/layout 0.0.3-dev.8 → 0.0.3

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.
@@ -1,115 +1,8 @@
1
+ import { BehaviorSubject, map, shareReplay, ReplaySubject, combineLatest, switchMap, of, Subject, startWith, scan, tap, distinctUntilChanged, finalize } from 'rxjs';
1
2
  import * as i0 from '@angular/core';
2
- import { inject, NgZone, Component, ElementRef, Input, Output, ChangeDetectionStrategy, ContentChild, ContentChildren, NgModule } from '@angular/core';
3
- import { Observable, distinctUntilChanged, shareReplay, BehaviorSubject, map, combineLatest, switchMap, of, Subject, startWith } from 'rxjs';
3
+ import { Component, inject, ElementRef, ViewChild, Input, Output, ChangeDetectionStrategy, ContentChild, ContentChildren, NgModule, Injectable, TemplateRef, Directive, ViewContainerRef, Injector } from '@angular/core';
4
4
  import { NumberWithUnit, Destructible, coerceBoolAttr, FastDOM } from '@ngutil/common';
5
-
6
- const WATCHES = {};
7
- /**
8
- * watchMedia("(display-mode: standalone)")
9
- */
10
- function watchMedia(expr) {
11
- const existing = WATCHES[expr];
12
- if (existing == null) {
13
- return (WATCHES[expr] = _createWatcher(expr));
14
- }
15
- return existing;
16
- }
17
- function _createWatcher(expr) {
18
- const zone = inject(NgZone);
19
- return zone.runOutsideAngular(() => new Observable((sub) => {
20
- const query = window.matchMedia(expr);
21
- const listener = (event) => {
22
- sub.next(event.matches);
23
- };
24
- query.addEventListener("change", listener);
25
- sub.next(query.matches);
26
- return () => {
27
- query.removeEventListener("change", listener);
28
- delete WATCHES[expr];
29
- };
30
- }).pipe(distinctUntilChanged(), shareReplay(1)));
31
- }
32
-
33
- const RESIZE_WATCHES = new Map();
34
- const SCROLL_WATCHES = new Map();
35
- function watchDimension(el, box = "border-box") {
36
- const zone = inject(NgZone);
37
- return box === "scroll-box" ? _watchScroll(zone, el) : _watchResize(zone, el, box);
38
- }
39
- function _watchResize(zone, el, box) {
40
- return _watch(zone, el, RESIZE_WATCHES, () => _createResizeWatcher(zone, el, box));
41
- }
42
- function _watchScroll(zone, el) {
43
- return _watch(zone, el, SCROLL_WATCHES, () => _createScollWatcher(zone, el));
44
- }
45
- function _watch(zone, el, watches, factory) {
46
- const existing = watches.get(el);
47
- if (existing == null) {
48
- const watcher = factory();
49
- watches.set(el, watcher);
50
- return watcher;
51
- }
52
- return existing;
53
- }
54
- function _createResizeWatcher(zone, el, box) {
55
- if (box !== "border-box") {
56
- throw new Error(`Currently not implemented box mode: ${box}`);
57
- }
58
- return zone.runOutsideAngular(() => new Observable((sub) => {
59
- const observer = new ResizeObserver(entries => {
60
- for (const entry of entries) {
61
- if (entry.borderBoxSize) {
62
- sub.next({
63
- width: _number(entry.borderBoxSize[0].inlineSize),
64
- height: _number(entry.borderBoxSize[0].blockSize)
65
- });
66
- }
67
- else {
68
- sub.next({
69
- width: _number(el.offsetWidth),
70
- height: _number(el.offsetHeight)
71
- });
72
- }
73
- }
74
- });
75
- observer.observe(el, { box: box });
76
- return () => {
77
- observer.disconnect();
78
- RESIZE_WATCHES.delete(el);
79
- };
80
- }).pipe(distinctUntilChanged((p, c) => p && c && p.width === c.width && p.height === c.height), shareReplay(1)));
81
- }
82
- function _createScollWatcher(zone, el) {
83
- return zone.runOutsideAngular(() => new Observable((sub) => {
84
- let lastSw = 0;
85
- let lastSh = 0;
86
- const emit = () => {
87
- const sw = el.scrollWidth;
88
- const sh = el.scrollHeight;
89
- if (lastSw !== sw || lastSh !== sh) {
90
- lastSw = sw;
91
- lastSh = sh;
92
- sub.next({ width: _number(lastSw), height: _number(lastSh) });
93
- }
94
- };
95
- const dimSum = _watchResize(zone, el, "border-box").subscribe(emit);
96
- const mutation = new MutationObserver(emit);
97
- mutation.observe(el, {
98
- subtree: true,
99
- childList: true,
100
- attributes: true,
101
- characterData: true
102
- });
103
- return () => {
104
- dimSum.unsubscribe();
105
- mutation.disconnect();
106
- SCROLL_WATCHES.delete(el);
107
- };
108
- }).pipe(shareReplay(1)));
109
- }
110
- function _number(val) {
111
- return new NumberWithUnit(val, "pk");
112
- }
5
+ import { DimensionWatcher } from '@ngutil/style';
113
6
 
114
7
  /**
115
8
  * -----------------------------------------------
@@ -238,29 +131,24 @@ class L9State {
238
131
  return res;
239
132
  }), shareReplay(1));
240
133
  }
241
- update(range, dim) {
242
- range = L9Range.coerce(range);
243
- const dims = { ...this.#dims.value };
244
- // for (const cell of range.horizontals) {
245
- // dims[cell] = dim.width
246
- // }
247
- }
248
134
  }
249
135
 
250
136
  class DockingContentComponent {
251
137
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
252
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.3", type: DockingContentComponent, isStandalone: true, selector: "nu-docking-content", ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [":host{display:flex;flex-flow:column nowrap;align-items:stretch;box-sizing:border-box;flex:1;overflow:auto}\n"] }); }
138
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.3", type: DockingContentComponent, isStandalone: true, selector: "nu-docking-content", exportAs: ["nuDockingContent"], ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [":host{display:flex;flex-flow:column nowrap;align-items:stretch;box-sizing:border-box;flex:1}\n"] }); }
253
139
  }
254
140
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingContentComponent, decorators: [{
255
141
  type: Component,
256
- args: [{ standalone: true, selector: "nu-docking-content", template: `<ng-content></ng-content>`, styles: [":host{display:flex;flex-flow:column nowrap;align-items:stretch;box-sizing:border-box;flex:1;overflow:auto}\n"] }]
142
+ args: [{ standalone: true, selector: "nu-docking-content", exportAs: "nuDockingContent", template: `<ng-content></ng-content>`, styles: [":host{display:flex;flex-flow:column nowrap;align-items:stretch;box-sizing:border-box;flex:1}\n"] }]
257
143
  }] });
258
144
 
259
145
  const DEFAULT_POSITION = L9Range.coerce("left");
146
+ const HIDDEN_SIZE = new NumberWithUnit(0, "px");
147
+ const AUTO_SIZE = NumberWithUnit.coerce("auto");
260
148
  class DockingPanelComponent extends Destructible {
149
+ #dimWatcher;
261
150
  set positionInput(val) {
262
151
  const coerced = L9Range.coerce(val);
263
- console.log("SET POSITION", coerced, this.position.value.isEq(coerced));
264
152
  if (coerced.orient === "rect") {
265
153
  throw new Error(`Invalid position value: ${val}`);
266
154
  }
@@ -301,19 +189,22 @@ class DockingPanelComponent extends Destructible {
301
189
  }
302
190
  #minimizable;
303
191
  #minimizableAuto;
192
+ #contentSize;
304
193
  #autoSize;
305
194
  constructor() {
306
195
  super();
307
196
  this.el = inject((ElementRef));
197
+ this.#dimWatcher = inject(DimensionWatcher);
308
198
  this.position = new BehaviorSubject(DEFAULT_POSITION);
309
- this.state = new BehaviorSubject("invisible");
310
- this.mode = new BehaviorSubject("overlay");
311
- this.#fullSize = new BehaviorSubject(NumberWithUnit.coerce(0));
312
- this.#miniSize = new BehaviorSubject(NumberWithUnit.coerce(0));
199
+ this.state = new BehaviorSubject("full");
200
+ this.mode = new BehaviorSubject("rigid");
201
+ this.#fullSize = new BehaviorSubject(AUTO_SIZE);
202
+ this.#miniSize = new BehaviorSubject(HIDDEN_SIZE);
313
203
  this.#minimizable = false;
314
204
  this.#minimizableAuto = true;
205
+ this.#contentSize = new ReplaySubject(1);
315
206
  this.#autoSize = combineLatest({
316
- dim: watchDimension(this.el.nativeElement, "scroll-box"),
207
+ dim: this.#contentSize,
317
208
  pos: this.position
318
209
  }).pipe(map(({ dim, pos }) => {
319
210
  if (pos.orient === "horizontal") {
@@ -344,7 +235,8 @@ class DockingPanelComponent extends Destructible {
344
235
  state: this.state,
345
236
  mode: this.mode,
346
237
  fullSize: this.fullSize,
347
- miniSize: this.miniSize
238
+ miniSize: this.miniSize,
239
+ contentSize: this.#contentSize
348
240
  });
349
241
  this.d.sub(this.changes).subscribe(changes => {
350
242
  if (this.#minimizableAuto) {
@@ -356,13 +248,51 @@ class DockingPanelComponent extends Destructible {
356
248
  mode: changes.mode,
357
249
  side: changes.position.orient === "horizontal" ? changes.position.cells[0].v : changes.position.cells[0].h
358
250
  });
251
+ const isHorizontal = changes.position.orient === "horizontal";
252
+ let w = null;
253
+ let h = null;
254
+ // TODO: when change state from mini -> hidden, currently wrong behavior
255
+ // the good behavior is to not gain fullSize ang go to hidden
256
+ if (changes.state === "mini") {
257
+ if (isHorizontal) {
258
+ h = changes.miniSize.unit === "auto" ? changes.contentSize.height : changes.miniSize;
259
+ }
260
+ else {
261
+ w = changes.miniSize.unit === "auto" ? changes.contentSize.width : changes.miniSize;
262
+ }
263
+ }
264
+ else {
265
+ if (isHorizontal) {
266
+ h = changes.fullSize.unit === "auto" ? changes.contentSize.height : changes.fullSize;
267
+ }
268
+ else {
269
+ w = changes.fullSize.unit === "auto" ? changes.contentSize.width : changes.fullSize;
270
+ }
271
+ }
272
+ FastDOM.setStyle(this.el.nativeElement, {
273
+ "--docking-panel-w": w != null ? `${w}` : null,
274
+ "--docking-panel-h": h != null ? `${h}` : null,
275
+ "--docking-panel-content-w": changes.contentSize.width,
276
+ "--docking-panel-content-h": changes.contentSize.height
277
+ }, () => FastDOM.setAttributes(this.el.nativeElement, { animate: "" }));
359
278
  });
360
279
  }
280
+ ngAfterViewInit() {
281
+ this.d
282
+ .sub(this.#dimWatcher.watch(this.content, "scroll-box"))
283
+ .pipe(map(dim => {
284
+ return {
285
+ width: new NumberWithUnit(dim.width, "px"),
286
+ height: new NumberWithUnit(dim.height, "px")
287
+ };
288
+ }))
289
+ .subscribe(this.#contentSize);
290
+ }
361
291
  open() {
362
292
  this.state.next("full");
363
293
  }
364
294
  close() {
365
- this.state.next("invisible");
295
+ this.state.next("hidden");
366
296
  }
367
297
  minimize() {
368
298
  if (this.minimizable) {
@@ -370,12 +300,23 @@ class DockingPanelComponent extends Destructible {
370
300
  }
371
301
  }
372
302
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
373
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.3", type: DockingPanelComponent, isStandalone: true, selector: "nu-docking-panel", inputs: { positionInput: ["position", "positionInput"], stateInput: ["state", "stateInput"], modeInput: ["mode", "modeInput"], fullSizeInput: ["fullSize", "fullSizeInput"], miniSizeInput: ["miniSize", "miniSizeInput"], minimizable: "minimizable" }, outputs: { state: "stateChanges" }, exportAs: ["nuDockingPanel"], usesInheritance: true, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [":host{---docking-panel-t: var(--docking-panel-t, auto);---docking-panel-r: var(--docking-panel-r, auto);---docking-panel-b: var(--docking-panel-b, auto);---docking-panel-l: var(--docking-panel-l, auto);---docking-panel-w: var(--docking-panel-w, auto);---docking-panel-h: var(--docking-panel-h, auto);---docking-panel-real-w: var(--docking-panel-real-w, var(---docking-panel-w));---docking-panel-real-h: var(--docking-panel-real-h, var(---docking-panel-h));display:flex;flex-flow:column nowrap;align-items:stretch;position:absolute;box-sizing:border-box;top:var(---docking-panel-t);right:var(---docking-panel-r);bottom:var(---docking-panel-b);left:var(---docking-panel-l);width:var(---docking-panel-w);height:var(---docking-panel-h);transition:transform var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),width var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),height var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}:host[side=top],:host[side=left]{---docking-panel-t-hide: -100%;---docking-panel-t-visible: 0%}:host[side=bottom],:host[side=right]{---docking-panel-t-hide: 100%;---docking-panel-t-visible: 0%}:host[state=invisible][orient=horizontal]{transform:translateY(var(---docking-panel-t-hide))}:host[state=invisible][orient=vertical]{transform:translate(var(---docking-panel-t-hide))}:host:not([state=invisible])[orient=horizontal]{transform:translateY(var(---docking-panel-t-visible))}:host:not([state=invisible])[orient=vertical]{transform:translate(var(---docking-panel-t-visible))}\n"] }); }
303
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.3", type: DockingPanelComponent, isStandalone: true, selector: "nu-docking-panel", inputs: { positionInput: ["position", "positionInput"], stateInput: ["state", "stateInput"], modeInput: ["mode", "modeInput"], fullSizeInput: ["fullSize", "fullSizeInput"], miniSizeInput: ["miniSize", "miniSizeInput"], minimizable: "minimizable" }, outputs: { state: "stateChanges" }, viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true, read: ElementRef, static: true }], exportAs: ["nuDockingPanel"], usesInheritance: true, ngImport: i0, template: `
304
+ <div class="content" #content>
305
+ <ng-content></ng-content>
306
+ </div>
307
+ `, isInline: true, styles: [":host{---docking-panel-t: var(--docking-panel-t, auto);---docking-panel-r: var(--docking-panel-r, auto);---docking-panel-b: var(--docking-panel-b, auto);---docking-panel-l: var(--docking-panel-l, auto);---docking-panel-w: var(--docking-panel-w, auto);---docking-panel-h: var(--docking-panel-h, auto);---docking-panel-content-w: var(--docking-panel-content-w, var(---docking-panel-w));---docking-panel-content-g: var(--docking-panel-content-g, var(---docking-panel-h));display:flex;flex-flow:column nowrap;align-items:stretch;position:absolute;box-sizing:border-box;overflow:hidden;top:var(---docking-panel-t);right:var(---docking-panel-r);bottom:var(---docking-panel-b);left:var(---docking-panel-l);width:var(---docking-panel-w);height:var(---docking-panel-h)}:host[animate]{transition:transform var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),width var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),height var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}:host[side=top],:host[side=left]{---docking-panel-t-hide: -100%;---docking-panel-t-visible: 0%}:host[side=bottom],:host[side=right]{---docking-panel-t-hide: 100%;---docking-panel-t-visible: 0%}:host[state=hidden][orient=horizontal]{transform:translateY(var(---docking-panel-t-hide))}:host[state=hidden][orient=vertical]{transform:translate(var(---docking-panel-t-hide))}:host:not([state=hidden])[orient=horizontal]{transform:translateY(var(---docking-panel-t-visible))}:host:not([state=hidden])[orient=vertical]{transform:translate(var(---docking-panel-t-visible))}\n"] }); }
374
308
  }
375
309
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingPanelComponent, decorators: [{
376
310
  type: Component,
377
- args: [{ standalone: true, selector: "nu-docking-panel", exportAs: "nuDockingPanel", template: `<ng-content></ng-content>`, styles: [":host{---docking-panel-t: var(--docking-panel-t, auto);---docking-panel-r: var(--docking-panel-r, auto);---docking-panel-b: var(--docking-panel-b, auto);---docking-panel-l: var(--docking-panel-l, auto);---docking-panel-w: var(--docking-panel-w, auto);---docking-panel-h: var(--docking-panel-h, auto);---docking-panel-real-w: var(--docking-panel-real-w, var(---docking-panel-w));---docking-panel-real-h: var(--docking-panel-real-h, var(---docking-panel-h));display:flex;flex-flow:column nowrap;align-items:stretch;position:absolute;box-sizing:border-box;top:var(---docking-panel-t);right:var(---docking-panel-r);bottom:var(---docking-panel-b);left:var(---docking-panel-l);width:var(---docking-panel-w);height:var(---docking-panel-h);transition:transform var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),width var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),height var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}:host[side=top],:host[side=left]{---docking-panel-t-hide: -100%;---docking-panel-t-visible: 0%}:host[side=bottom],:host[side=right]{---docking-panel-t-hide: 100%;---docking-panel-t-visible: 0%}:host[state=invisible][orient=horizontal]{transform:translateY(var(---docking-panel-t-hide))}:host[state=invisible][orient=vertical]{transform:translate(var(---docking-panel-t-hide))}:host:not([state=invisible])[orient=horizontal]{transform:translateY(var(---docking-panel-t-visible))}:host:not([state=invisible])[orient=vertical]{transform:translate(var(---docking-panel-t-visible))}\n"] }]
378
- }], ctorParameters: () => [], propDecorators: { positionInput: [{
311
+ args: [{ standalone: true, selector: "nu-docking-panel", exportAs: "nuDockingPanel", template: `
312
+ <div class="content" #content>
313
+ <ng-content></ng-content>
314
+ </div>
315
+ `, styles: [":host{---docking-panel-t: var(--docking-panel-t, auto);---docking-panel-r: var(--docking-panel-r, auto);---docking-panel-b: var(--docking-panel-b, auto);---docking-panel-l: var(--docking-panel-l, auto);---docking-panel-w: var(--docking-panel-w, auto);---docking-panel-h: var(--docking-panel-h, auto);---docking-panel-content-w: var(--docking-panel-content-w, var(---docking-panel-w));---docking-panel-content-g: var(--docking-panel-content-g, var(---docking-panel-h));display:flex;flex-flow:column nowrap;align-items:stretch;position:absolute;box-sizing:border-box;overflow:hidden;top:var(---docking-panel-t);right:var(---docking-panel-r);bottom:var(---docking-panel-b);left:var(---docking-panel-l);width:var(---docking-panel-w);height:var(---docking-panel-h)}:host[animate]{transition:transform var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),width var(---docking-layout-anim-duration) var(---docking-layout-anim-ease),height var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}:host[side=top],:host[side=left]{---docking-panel-t-hide: -100%;---docking-panel-t-visible: 0%}:host[side=bottom],:host[side=right]{---docking-panel-t-hide: 100%;---docking-panel-t-visible: 0%}:host[state=hidden][orient=horizontal]{transform:translateY(var(---docking-panel-t-hide))}:host[state=hidden][orient=vertical]{transform:translate(var(---docking-panel-t-hide))}:host:not([state=hidden])[orient=horizontal]{transform:translateY(var(---docking-panel-t-visible))}:host:not([state=hidden])[orient=vertical]{transform:translate(var(---docking-panel-t-visible))}\n"] }]
316
+ }], ctorParameters: () => [], propDecorators: { content: [{
317
+ type: ViewChild,
318
+ args: ["content", { read: ElementRef, static: true }]
319
+ }], positionInput: [{
379
320
  type: Input,
380
321
  args: ["position"]
381
322
  }], stateInput: [{
@@ -398,29 +339,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImpor
398
339
  args: ["minimizable"]
399
340
  }] } });
400
341
 
401
- const EMBEDDED_ZINDEX = 20;
402
- const OVERLAY_ZINDEX = EMBEDDED_ZINDEX * 2;
403
- // interface PanelRefChanges {
404
- // ref: PanelRef
405
- // changes: DockingPanelChanges
406
- // }
407
- // class PanelRef {
408
- // style: Partial<CSSStyleDeclaration> = {}
409
- // readonly changes: Observable<PanelRefChanges>
410
- // constructor(public readonly panel: DockingPanelDirective) {
411
- // this.changes = panel.changes.pipe(
412
- // map(changes => {
413
- // return { ref: this, changes }
414
- // })
415
- // )
416
- // }
417
- // }
342
+ const RIGID_ZINDEX = 20;
343
+ const OVER_ZINDEX = RIGID_ZINDEX * 2;
418
344
  class DockingLayoutComponent extends Destructible {
419
345
  constructor() {
420
346
  super(...arguments);
421
347
  this.#el = inject((ElementRef));
422
348
  this.contentOnly = false;
423
- this.positionMode = "absolute";
424
349
  this.#reflow = new Subject();
425
350
  }
426
351
  #el;
@@ -428,25 +353,12 @@ class DockingLayoutComponent extends Destructible {
428
353
  ngAfterViewInit() {
429
354
  // eslint-disable-next-line prettier/prettier
430
355
  this.panels = this.dockingPanels.changes.pipe(startWith(null), map(() => this.dockingPanels.toArray()), shareReplay(1));
431
- // this.panels.subscribe(panels => console.log({ panels }))
432
356
  this.d
433
357
  .sub(combineLatest({ panels: this.panels, reflow: this.#reflow.pipe(startWith(null)) }))
434
358
  .pipe(switchMap(({ panels }) => combineLatest(panels.map(panel => panel.changes.pipe(map(changes => {
435
359
  return { panel, changes };
436
360
  }))))))
437
361
  .subscribe(this.#layout.bind(this));
438
- // this.d
439
- // .sub(merge(this.dockingPanels.changes, this.#reflow))
440
- // .pipe(
441
- // startWith(null),
442
- // map(() => this.dockingPanels.map(panel => new PanelRef(panel))),
443
- // switchMap(refs => combineLatest(refs.map(ref => ref.changes))),
444
- // map(changes => {
445
- // this.#layout(changes)
446
- // return changes.map(c => c.ref)
447
- // })
448
- // )
449
- // .subscribe(this.panels)
450
362
  }
451
363
  ngOnChanges(changes) {
452
364
  if ("contentOnly" in changes || "positionMode" in changes) {
@@ -454,13 +366,12 @@ class DockingLayoutComponent extends Destructible {
454
366
  }
455
367
  }
456
368
  #layout(entries) {
457
- console.log("layout", entries);
458
369
  let paddingTop = 0;
459
370
  let paddingRight = 0;
460
371
  let paddingBottom = 0;
461
372
  let paddingLeft = 0;
462
- let embeddedZIndex = EMBEDDED_ZINDEX;
463
- let overlayZIndex = OVERLAY_ZINDEX;
373
+ let rigidZIndex = RIGID_ZINDEX;
374
+ let overZIndex = OVER_ZINDEX;
464
375
  if (this.contentOnly) {
465
376
  // TODO:...
466
377
  }
@@ -473,7 +384,7 @@ class DockingLayoutComponent extends Destructible {
473
384
  ? panelState.miniSize.value
474
385
  : 0;
475
386
  const isHorizontal = panelState.position.orient === "horizontal";
476
- const isEmbedded = panelState.mode === "embedded";
387
+ const isRigid = panelState.mode === "rigid";
477
388
  let panelTop = null;
478
389
  let panelRight = null;
479
390
  let panelBottom = null;
@@ -482,13 +393,13 @@ class DockingLayoutComponent extends Destructible {
482
393
  panelLeft = 0;
483
394
  panelRight = 0;
484
395
  if (panelState.position.cells[0].v === "top") {
485
- if (isEmbedded) {
396
+ if (isRigid) {
486
397
  paddingTop = Math.max(paddingTop, panelSize);
487
398
  }
488
399
  panelTop = 0;
489
400
  }
490
401
  else if (panelState.position.cells[0].v === "bottom") {
491
- if (isEmbedded) {
402
+ if (isRigid) {
492
403
  paddingBottom = Math.max(paddingBottom, panelSize);
493
404
  }
494
405
  panelBottom = 0;
@@ -498,40 +409,26 @@ class DockingLayoutComponent extends Destructible {
498
409
  panelTop = 0;
499
410
  panelBottom = 0;
500
411
  if (panelState.position.cells[0].h === "left") {
501
- if (isEmbedded) {
412
+ if (isRigid) {
502
413
  paddingLeft = Math.max(paddingLeft, panelSize);
503
414
  }
504
415
  panelLeft = 0;
505
416
  }
506
417
  else if (panelState.position.cells[0].h === "right") {
507
- if (isEmbedded) {
418
+ if (isRigid) {
508
419
  paddingRight = Math.max(paddingRight, panelSize);
509
420
  }
510
421
  panelRight = 0;
511
422
  }
512
423
  }
513
- const panelGivenSize = panelState.state === "full"
514
- ? panelState.fullSize
515
- : panelState.state === "mini"
516
- ? panelState.miniSize
517
- : new NumberWithUnit(0, "px");
518
424
  FastDOM.setStyle(entry.panel.el.nativeElement, {
519
- "z-index": `${isEmbedded ? embeddedZIndex++ : overlayZIndex++}`,
425
+ "z-index": `${isRigid ? rigidZIndex++ : overZIndex++}`,
520
426
  "--docking-panel-t": panelTop != null ? `${panelTop}px` : null,
521
427
  "--docking-panel-r": panelRight != null ? `${panelRight}px` : null,
522
428
  "--docking-panel-b": panelBottom != null ? `${panelBottom}px` : null,
523
- "--docking-panel-l": panelLeft != null ? `${panelLeft}px` : null,
524
- "--docking-panel-w": !isHorizontal
525
- ? `${panelGivenSize.unit === "auto" ? "auto" : panelGivenSize}`
526
- : null,
527
- "--docking-panel-h": isHorizontal
528
- ? `${panelGivenSize.unit === "auto" ? "auto" : panelGivenSize}`
529
- : null,
530
- "--docking-panel-real-w": !isHorizontal ? `${panelSize}px` : null,
531
- "--docking-panel-real-h": isHorizontal ? `${panelSize}px` : null
429
+ "--docking-panel-l": panelLeft != null ? `${panelLeft}px` : null
532
430
  });
533
431
  }
534
- console.log({ paddingTop, paddingRight, paddingBottom, paddingLeft });
535
432
  FastDOM.setStyle(this.#el.nativeElement, {
536
433
  "--docking-layout-top": `${paddingTop}px`,
537
434
  "--docking-layout-right": `${paddingRight}px`,
@@ -540,86 +437,8 @@ class DockingLayoutComponent extends Destructible {
540
437
  });
541
438
  }
542
439
  }
543
- #layoutOld(entries) {
544
- // let paddingTop = 0
545
- // let paddingRight = 0
546
- // let paddingBottom = 0
547
- // let paddingLeft = 0
548
- // if (!this.contentOnly) {
549
- // let embeddedZIndex = EMBEDDED_ZINDEX
550
- // let overlayZIndex = OVERLAY_ZINDEX
551
- // const leftRight: PanelRefChanges[] = entries.filter(v =>
552
- // ["left", "right"].includes(v.changes.position.side)
553
- // )
554
- // const topBottom: PanelRefChanges[] = entries.filter(v =>
555
- // ["top", "bottom"].includes(v.changes.position.side)
556
- // )
557
- // for (const entry of entries) {
558
- // const changes = entry.changes
559
- // const ref = entry.ref
560
- // if (changes.mode === "embedded") {
561
- // ref.style.zIndex = `${embeddedZIndex++}`
562
- // } else if (changes.mode === "overlay") {
563
- // ref.style.zIndex = `${overlayZIndex++}`
564
- // }
565
- // }
566
- // for (const entry of leftRight) {
567
- // const changes = entry.changes
568
- // const ref = entry.ref
569
- // const padding =
570
- // changes.mode === "embedded"
571
- // ? changes.state === "full"
572
- // ? changes.fullSize
573
- // : changes.state === "mini"
574
- // ? changes.miniSize
575
- // : 0
576
- // : 0
577
- // ref.style.top = "0"
578
- // ref.style.bottom = "0"
579
- // if (changes.position.side === "left") {
580
- // paddingLeft = Math.max(paddingLeft, padding)
581
- // ref.style.left = "0"
582
- // ref.style.right = ""
583
- // } else {
584
- // paddingRight = Math.max(paddingRight, padding)
585
- // ref.style.right = "0"
586
- // ref.style.left = ""
587
- // }
588
- // }
589
- // for (const entry of topBottom) {
590
- // const changes = entry.changes
591
- // const ref = entry.ref
592
- // const padding =
593
- // changes.mode === "embedded"
594
- // ? changes.state === "full"
595
- // ? changes.fullSize
596
- // : changes.state === "mini"
597
- // ? changes.miniSize
598
- // : 0
599
- // : 0
600
- // if (changes.mode === "embedded") {
601
- // ref.style.left = `${paddingLeft}px`
602
- // ref.style.right = `${paddingRight}px`
603
- // } else {
604
- // ref.style.left = "0"
605
- // ref.style.right = "0"
606
- // }
607
- // if (changes.position?.cells[0].v === "top") {
608
- // paddingTop = Math.max(paddingTop, padding)
609
- // ref.style.top = "0"
610
- // ref.style.bottom = ""
611
- // } else {
612
- // paddingBottom = Math.max(paddingBottom, padding)
613
- // ref.style.bottom = `0`
614
- // ref.style.top = ""
615
- // }
616
- // }
617
- // }
618
- // const cel = this.contentEl.nativeElement
619
- // cel.style.padding = `${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px`
620
- }
621
440
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
622
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.3", type: DockingLayoutComponent, isStandalone: true, selector: "nu-docking", inputs: { contentOnly: "contentOnly", positionMode: "positionMode" }, queries: [{ propertyName: "contentComponent", first: true, predicate: DockingContentComponent, descendants: true }, { propertyName: "dockingPanels", predicate: DockingPanelComponent }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
441
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.3", type: DockingLayoutComponent, isStandalone: true, selector: "nu-docking", inputs: { contentOnly: "contentOnly" }, queries: [{ propertyName: "contentComponent", first: true, predicate: DockingContentComponent, descendants: true }, { propertyName: "dockingPanels", predicate: DockingPanelComponent }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
623
442
  <ng-content select="nu-docking-panel"></ng-content>
624
443
 
625
444
  @if (!contentComponent) {
@@ -629,7 +448,7 @@ class DockingLayoutComponent extends Destructible {
629
448
  } @else {
630
449
  <ng-content select="nu-docking-content"></ng-content>
631
450
  }
632
- `, isInline: true, styles: [":host{---docking-layout-top: var(--docking-layout-top, 0px);---docking-layout-right: var(--docking-layout-right, 0px);---docking-layout-bottom: var(--docking-layout-bottom, 0px);---docking-layout-left: var(--docking-layout-left, 0px);---docking-layout-anim-duration: var(--docking-layout-anim-duration, .2s);---docking-layout-anim-ease: var(--docking-layout-anim-ease, ease-out);display:flex;flex-flow:column nowrap;align-items:stretch;position:relative;overflow:hidden;box-sizing:border-box;z-index:0;padding:var(---docking-layout-top) var(---docking-layout-right) var(---docking-layout-bottom) var(---docking-layout-left);transition:padding var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}\n"], dependencies: [{ kind: "component", type: DockingContentComponent, selector: "nu-docking-content" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
451
+ `, isInline: true, styles: [":host{---docking-layout-top: var(--docking-layout-top, 0px);---docking-layout-right: var(--docking-layout-right, 0px);---docking-layout-bottom: var(--docking-layout-bottom, 0px);---docking-layout-left: var(--docking-layout-left, 0px);---docking-layout-anim-duration: var(--docking-layout-anim-duration, .3s);---docking-layout-anim-ease: var(--docking-layout-anim-ease, cubic-bezier(.4, 0, .2, 1));display:flex;flex-flow:column nowrap;align-items:stretch;position:relative;overflow:hidden;box-sizing:border-box;z-index:0;padding:var(---docking-layout-top) var(---docking-layout-right) var(---docking-layout-bottom) var(---docking-layout-left);transition:padding var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}\n"], dependencies: [{ kind: "component", type: DockingContentComponent, selector: "nu-docking-content", exportAs: ["nuDockingContent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
633
452
  }
634
453
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingLayoutComponent, decorators: [{
635
454
  type: Component,
@@ -643,11 +462,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImpor
643
462
  } @else {
644
463
  <ng-content select="nu-docking-content"></ng-content>
645
464
  }
646
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{---docking-layout-top: var(--docking-layout-top, 0px);---docking-layout-right: var(--docking-layout-right, 0px);---docking-layout-bottom: var(--docking-layout-bottom, 0px);---docking-layout-left: var(--docking-layout-left, 0px);---docking-layout-anim-duration: var(--docking-layout-anim-duration, .2s);---docking-layout-anim-ease: var(--docking-layout-anim-ease, ease-out);display:flex;flex-flow:column nowrap;align-items:stretch;position:relative;overflow:hidden;box-sizing:border-box;z-index:0;padding:var(---docking-layout-top) var(---docking-layout-right) var(---docking-layout-bottom) var(---docking-layout-left);transition:padding var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}\n"] }]
465
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{---docking-layout-top: var(--docking-layout-top, 0px);---docking-layout-right: var(--docking-layout-right, 0px);---docking-layout-bottom: var(--docking-layout-bottom, 0px);---docking-layout-left: var(--docking-layout-left, 0px);---docking-layout-anim-duration: var(--docking-layout-anim-duration, .3s);---docking-layout-anim-ease: var(--docking-layout-anim-ease, cubic-bezier(.4, 0, .2, 1));display:flex;flex-flow:column nowrap;align-items:stretch;position:relative;overflow:hidden;box-sizing:border-box;z-index:0;padding:var(---docking-layout-top) var(---docking-layout-right) var(---docking-layout-bottom) var(---docking-layout-left);transition:padding var(---docking-layout-anim-duration) var(---docking-layout-anim-ease)}\n"] }]
647
466
  }], propDecorators: { contentOnly: [{
648
467
  type: Input
649
- }], positionMode: [{
650
- type: Input
651
468
  }], contentComponent: [{
652
469
  type: ContentChild,
653
470
  args: [DockingContentComponent]
@@ -670,9 +487,245 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImpor
670
487
  }]
671
488
  }] });
672
489
 
490
+ const SLOT_REGEX = /^([^:\s]+)(?::(\d+))?(?:\s+as\s+(.*?))?$/i;
491
+ class SlotDef {
492
+ constructor(slot, tpl) {
493
+ this.tpl = tpl;
494
+ const match = slot.match(SLOT_REGEX);
495
+ if (!match) {
496
+ console.warn(`Invalid slot definition: ${slot}`);
497
+ }
498
+ else {
499
+ this.slot = match[1];
500
+ this.order = match[2] != null ? Number(match[2]) : Infinity;
501
+ this.id = match[3];
502
+ }
503
+ }
504
+ dispose() {
505
+ this.viewRef?.destroy();
506
+ this.viewRef = undefined;
507
+ }
508
+ }
509
+ /**
510
+ * @Directive({selector: "ng-template[xyzSlot]", inputs: [{name: "slot", alias: "xyzSlot"}]})
511
+ * class XYZSlotDirective extends SlotDirective<XYZComponentSlots> { }
512
+ *
513
+ * @Directive({selector: "ng-template[xyzSlotOutlet]", inputs: [{name: "slot", alias: "xyzSlotOutlet"}]})
514
+ * class XYZSlotOutletDirective extends SlotOutletDirective<XYZComponentSlots> { }
515
+ *
516
+ *
517
+ * @Component({provides: [SlotsService]})
518
+ * class XYZComponent {
519
+ * slots: inject(SlotsService<XYZComponentSlots>)
520
+ * }
521
+ *
522
+ *
523
+ */
524
+ class SlotsService extends Destructible {
525
+ #events = new Subject();
526
+ #entries = this.#events.pipe(scan((entries, event) => {
527
+ if (event.type === "add") {
528
+ const index = entries.findIndex(value => value === event.def);
529
+ if (index > -1) {
530
+ entries[index] = event.def;
531
+ }
532
+ else {
533
+ entries.push(event.def);
534
+ }
535
+ }
536
+ else if (event.type === "del") {
537
+ const index = entries.findIndex(value => value === event.def);
538
+ if (index > -1) {
539
+ entries.splice(index, 1);
540
+ }
541
+ }
542
+ return entries;
543
+ }, []), tap(entries => {
544
+ entries.sort((a, b) => {
545
+ if (a.slot === b.slot) {
546
+ return a.order - b.order;
547
+ }
548
+ else {
549
+ return a.slot.localeCompare(b.slot);
550
+ }
551
+ });
552
+ }), shareReplay(1));
553
+ constructor() {
554
+ super();
555
+ // XXX: need to collect entries from the beginning
556
+ this.d.sub(this.#entries).subscribe();
557
+ }
558
+ addTpl(def) {
559
+ this.#events.next({ type: "add", def });
560
+ }
561
+ delTpl(def) {
562
+ this.#events.next({ type: "del", def });
563
+ }
564
+ #watchers = {};
565
+ watch(slot) {
566
+ const existing = this.#watchers[slot];
567
+ if (existing == null) {
568
+ return (this.#watchers[slot] = this.#watch(slot));
569
+ }
570
+ else {
571
+ return existing;
572
+ }
573
+ }
574
+ #watch(slot) {
575
+ return this.#entries.pipe(map(entries => entries.filter(entry => entry.slot === slot)), distinctUntilChanged((prev, curr) => {
576
+ if (prev.length === curr.length) {
577
+ for (let i = 0; i < prev.length; i++) {
578
+ if (prev[i] !== curr[i]) {
579
+ return false;
580
+ }
581
+ }
582
+ return true;
583
+ }
584
+ else {
585
+ return false;
586
+ }
587
+ }), finalize(() => {
588
+ delete this.#watchers[slot];
589
+ }), shareReplay(1));
590
+ }
591
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
592
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService }); }
593
+ }
594
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService, decorators: [{
595
+ type: Injectable
596
+ }], ctorParameters: () => [] });
597
+ class SlotDirective {
598
+ constructor() {
599
+ this.tpl = inject((TemplateRef));
600
+ }
601
+ set slot(slot) {
602
+ if (this.#slot !== slot) {
603
+ this.#slot = slot;
604
+ if (this.#slotDef) {
605
+ this.slotSvc.delTpl(this.#slotDef);
606
+ }
607
+ this.#slotDef = new SlotDef(slot, this.tpl);
608
+ this.slotSvc.addTpl(this.#slotDef);
609
+ }
610
+ }
611
+ get slot() {
612
+ return this.#slot;
613
+ }
614
+ #slot;
615
+ #slotDef;
616
+ ngOnDestroy() {
617
+ if (this.#slotDef) {
618
+ this.slotSvc.delTpl(this.#slotDef);
619
+ }
620
+ }
621
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
622
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.1.3", type: SlotDirective, ngImport: i0 }); }
623
+ }
624
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotDirective, decorators: [{
625
+ type: Directive
626
+ }] });
627
+ class SlotOutletDirective extends Destructible {
628
+ set slot(slot) {
629
+ if (this.#slot.value !== slot) {
630
+ this.#slot.next(slot);
631
+ }
632
+ }
633
+ get slot() {
634
+ return this.#slot.value;
635
+ }
636
+ #slot;
637
+ #watch;
638
+ #views;
639
+ constructor() {
640
+ super();
641
+ this.vcr = inject(ViewContainerRef);
642
+ this.injector = inject(Injector);
643
+ this.#slot = new BehaviorSubject(null);
644
+ this.#watch = this.#slot.pipe(switchMap(slot => {
645
+ if (slot) {
646
+ return this.slotSvc.watch(slot);
647
+ }
648
+ else {
649
+ return of([]);
650
+ }
651
+ }));
652
+ this.#views = [];
653
+ this.#onEntriesChanged = (entries) => {
654
+ const { remove, undecided } = this.#determineActions(entries);
655
+ for (const r of remove) {
656
+ r.dispose();
657
+ const idx = this.#views.indexOf(r);
658
+ if (idx >= 0) {
659
+ this.#views.splice(idx, 1);
660
+ }
661
+ }
662
+ this.#views.length = 0;
663
+ for (const [pos, entry] of undecided.entries()) {
664
+ if (entry.viewRef && !entry.viewRef.destroyed) {
665
+ const currentPos = this.vcr.indexOf(entry.viewRef);
666
+ if (currentPos !== pos) {
667
+ this.vcr.insert(entry.viewRef, pos);
668
+ }
669
+ }
670
+ else {
671
+ ;
672
+ entry.viewRef = this.vcr.createEmbeddedView(entry.tpl, null, {
673
+ index: pos,
674
+ injector: this.injector
675
+ });
676
+ entry.viewRef.markForCheck();
677
+ }
678
+ this.#views.push(entry);
679
+ }
680
+ };
681
+ this.d.any(this.#clearViews.bind(this));
682
+ }
683
+ ngOnInit() {
684
+ this.d.sub(this.#watch).subscribe(this.#onEntriesChanged);
685
+ }
686
+ #onEntriesChanged;
687
+ #determineActions(entries) {
688
+ const byId = {};
689
+ let remove = [];
690
+ const undecided = [];
691
+ for (const entry of entries) {
692
+ if (entry.id != null) {
693
+ if (!byId[entry.id]) {
694
+ byId[entry.id] = [entry];
695
+ }
696
+ else {
697
+ byId[entry.id].push(entry);
698
+ }
699
+ }
700
+ else {
701
+ undecided.push(entry);
702
+ }
703
+ }
704
+ for (const values of Object.values(byId)) {
705
+ remove = remove.concat(values.slice(0, -1));
706
+ undecided.push(values[values.length - 1]);
707
+ }
708
+ for (const current of this.#views) {
709
+ if (!undecided.includes(current)) {
710
+ remove.push(current);
711
+ }
712
+ }
713
+ return { remove, undecided };
714
+ }
715
+ #clearViews() {
716
+ this.vcr.clear();
717
+ this.#views = [];
718
+ }
719
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotOutletDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
720
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.1.3", type: SlotOutletDirective, usesInheritance: true, ngImport: i0 }); }
721
+ }
722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotOutletDirective, decorators: [{
723
+ type: Directive
724
+ }], ctorParameters: () => [] });
725
+
673
726
  /**
674
727
  * Generated bundle index. Do not edit.
675
728
  */
676
729
 
677
- export { DockingContentComponent, DockingLayoutComponent, DockingPanelComponent, L9Cell, L9Range, L9State, NuDockingLayout, watchDimension, watchMedia };
730
+ export { DockingContentComponent, DockingLayoutComponent, DockingPanelComponent, L9Cell, L9Range, L9State, NuDockingLayout, SlotDef, SlotDirective, SlotOutletDirective, SlotsService };
678
731
  //# sourceMappingURL=ngutil-layout.mjs.map