@ngutil/layout 0.0.3-dev.0 → 0.0.3-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/docking/docking-content.component.d.ts +5 -0
  2. package/docking/docking-layout.component.d.ts +22 -0
  3. package/docking/docking-panel.component.d.ts +40 -0
  4. package/docking/index.d.ts +13 -0
  5. package/esm2022/docking/docking-content.component.mjs +11 -0
  6. package/esm2022/docking/docking-layout.component.mjs +140 -0
  7. package/esm2022/docking/docking-panel.component.mjs +179 -0
  8. package/esm2022/docking/index.mjs +20 -0
  9. package/esm2022/index.mjs +5 -0
  10. package/esm2022/l9/index.mjs +3 -0
  11. package/esm2022/l9/range.mjs +109 -0
  12. package/esm2022/l9/state.mjs +29 -0
  13. package/esm2022/ngutil-layout.mjs +5 -0
  14. package/esm2022/services/slots.service.mjs +240 -0
  15. package/esm2022/util/dimension-watcher.mjs +85 -0
  16. package/esm2022/util/dimension.mjs +2 -0
  17. package/esm2022/util/index.mjs +3 -0
  18. package/esm2022/util/media-watcher.mjs +29 -0
  19. package/esm2022/util/rect.mjs +2 -0
  20. package/fesm2022/ngutil-layout.mjs +822 -0
  21. package/fesm2022/ngutil-layout.mjs.map +1 -0
  22. package/index.d.ts +4 -0
  23. package/l9/index.d.ts +2 -0
  24. package/l9/range.d.ts +28 -0
  25. package/l9/state.d.ts +14 -0
  26. package/package.json +19 -5
  27. package/services/slots.service.d.ts +69 -0
  28. package/util/dimension-watcher.d.ts +4 -0
  29. package/util/dimension.d.ts +5 -0
  30. package/util/index.d.ts +4 -0
  31. package/util/media-watcher.d.ts +5 -0
  32. package/util/rect.d.ts +5 -0
  33. package/.eslintrc.json +0 -25
  34. package/.storybook/main.ts +0 -16
  35. package/.storybook/preview.ts +0 -0
  36. package/.storybook/tsconfig.json +0 -16
  37. package/jest.config.ts +0 -22
  38. package/ng-package.json +0 -7
  39. package/project.json +0 -73
  40. package/src/index.ts +0 -1
  41. package/src/lib/layout/layout.component.scss +0 -0
  42. package/src/lib/layout/layout.component.spec.ts +0 -22
  43. package/src/lib/layout/layout.component.stories.ts +0 -24
  44. package/src/lib/layout/layout.component.ts +0 -11
  45. package/src/test-setup.ts +0 -9
  46. package/tsconfig.json +0 -32
  47. package/tsconfig.lib.json +0 -19
  48. package/tsconfig.lib.prod.json +0 -9
  49. package/tsconfig.spec.json +0 -11
@@ -0,0 +1,822 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, NgZone, Component, ElementRef, Input, Output, ChangeDetectionStrategy, ContentChild, ContentChildren, NgModule, Injectable, TemplateRef, Directive, ViewContainerRef, Injector } from '@angular/core';
3
+ import { Observable, distinctUntilChanged, shareReplay, BehaviorSubject, map, combineLatest, switchMap, of, Subject, startWith, scan, tap, finalize } from 'rxjs';
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 = NaN;
85
+ let lastSh = NaN;
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
+ emit();
104
+ return () => {
105
+ dimSum.unsubscribe();
106
+ mutation.disconnect();
107
+ SCROLL_WATCHES.delete(el);
108
+ };
109
+ }).pipe(shareReplay(1)));
110
+ }
111
+ function _number(val) {
112
+ return new NumberWithUnit(val, "px");
113
+ }
114
+
115
+ /**
116
+ * -----------------------------------------------
117
+ * | TOP:LEFT | TOP:CENTER | TOP:RIGHT |
118
+ * -----------------------------------------------
119
+ * | MIDDLE:LEFT | MIDDLE:CENTER | MIDDLE:RIGH |
120
+ * -----------------------------------------------
121
+ * | BOTTOMN:LEFT | BOTTOM:CENTER | BOTTOM:RIGHT |
122
+ * -----------------------------------------------
123
+ */
124
+ const vertical = ["top", "middle", "bottom"];
125
+ const horizontal = ["left", "center", "right"];
126
+ class L9Cell {
127
+ static coerce(value) {
128
+ const [v, h] = value.split(":");
129
+ if (vertical.includes(v) && horizontal.includes(h)) {
130
+ return new L9Cell(v, h);
131
+ }
132
+ throw new Error(`Invalid cell value: ${value}`);
133
+ }
134
+ constructor(v, h) {
135
+ this.v = v;
136
+ this.h = h;
137
+ }
138
+ }
139
+ class L9Range {
140
+ static coerce(value) {
141
+ if (value instanceof L9Range) {
142
+ return value;
143
+ }
144
+ else {
145
+ return new L9Range(value);
146
+ }
147
+ }
148
+ constructor(range) {
149
+ this.cells = parse(range);
150
+ this.orient = this.#determineOrient();
151
+ }
152
+ isEq(other) {
153
+ if (other instanceof L9Range) {
154
+ const selfFirst = this.cells[0];
155
+ const otherFirst = other.cells[0];
156
+ if (selfFirst.h !== otherFirst.h || selfFirst.v !== otherFirst.v) {
157
+ return false;
158
+ }
159
+ const selfLast = this.cells[this.cells.length - 1];
160
+ const otherLast = other.cells[other.cells.length - 1];
161
+ if (selfLast.h === otherLast.h && selfLast.v === otherLast.v) {
162
+ return true;
163
+ }
164
+ }
165
+ return false;
166
+ }
167
+ #determineOrient() {
168
+ const { v: sv, h: sh } = this.cells[0];
169
+ const { v: ev, h: eh } = this.cells[this.cells.length - 1];
170
+ if (sv === ev) {
171
+ return "horizontal";
172
+ }
173
+ else if (sh === eh) {
174
+ return "vertical";
175
+ }
176
+ else {
177
+ return "rect";
178
+ }
179
+ }
180
+ }
181
+ function parse(value) {
182
+ const cells = [];
183
+ if (vertical.includes(value)) {
184
+ for (const h of horizontal) {
185
+ cells.push(new L9Cell(value, h));
186
+ }
187
+ }
188
+ else if (horizontal.includes(value)) {
189
+ for (const v of vertical) {
190
+ cells.push(new L9Cell(v, value));
191
+ }
192
+ }
193
+ else if (value.includes("-")) {
194
+ const [begin, end] = value.split("-");
195
+ const beginCells = parse(begin);
196
+ const endCells = parse(end);
197
+ if (beginCells.length > 1) {
198
+ throw new Error(`Currently not supported begin range value: ${begin}`);
199
+ }
200
+ if (endCells.length > 1) {
201
+ throw new Error(`Currently not supported end range value: ${end}`);
202
+ }
203
+ const { v: bv, h: bh } = beginCells[0];
204
+ const { v: ev, h: eh } = endCells[0];
205
+ const vstart = Math.min(vertical.indexOf(bv), vertical.indexOf(ev));
206
+ const vend = Math.max(vertical.indexOf(bv), vertical.indexOf(ev));
207
+ const hstart = Math.min(horizontal.indexOf(bh), horizontal.indexOf(eh));
208
+ const hend = Math.max(horizontal.indexOf(bh), horizontal.indexOf(eh));
209
+ for (let vi = vstart; vi <= vend; vi++) {
210
+ for (let hi = hstart; hi <= hend; hi++) {
211
+ cells.push(new L9Cell(vertical[vi], horizontal[hi]));
212
+ }
213
+ }
214
+ }
215
+ else if (value.includes(":")) {
216
+ cells.push(L9Cell.coerce(value));
217
+ }
218
+ if (cells.length === 0) {
219
+ throw Error(`Undefined l9cell: "${value}"`);
220
+ }
221
+ return cells;
222
+ }
223
+
224
+ class L9State {
225
+ #dims;
226
+ constructor(prefix) {
227
+ this.prefix = prefix;
228
+ this.#dims = new BehaviorSubject({});
229
+ this.style = this.#dims.pipe(map(dims => {
230
+ const res = {};
231
+ for (const [k, v] of Object.entries(dims)) {
232
+ if (v == null) {
233
+ continue;
234
+ }
235
+ const [vertical, horizontal] = k.split(":");
236
+ res[`--${this.prefix}-${vertical}-${horizontal}-w`] = v.width.toString();
237
+ res[`--${this.prefix}-${vertical}-${horizontal}-h`] = v.height.toString();
238
+ }
239
+ return res;
240
+ }), shareReplay(1));
241
+ }
242
+ update(range, dim) {
243
+ range = L9Range.coerce(range);
244
+ const dims = { ...this.#dims.value };
245
+ // for (const cell of range.horizontals) {
246
+ // dims[cell] = dim.width
247
+ // }
248
+ }
249
+ }
250
+
251
+ class DockingContentComponent {
252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
253
+ 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;overflow:auto}\n"] }); }
254
+ }
255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingContentComponent, decorators: [{
256
+ type: Component,
257
+ 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;overflow:auto}\n"] }]
258
+ }] });
259
+
260
+ const DEFAULT_POSITION = L9Range.coerce("left");
261
+ const INVISIBLE_SIZE = new NumberWithUnit(0, "px");
262
+ const AUTO_SIZE = NumberWithUnit.coerce("auto");
263
+ class DockingPanelComponent extends Destructible {
264
+ set positionInput(val) {
265
+ const coerced = L9Range.coerce(val);
266
+ if (coerced.orient === "rect") {
267
+ throw new Error(`Invalid position value: ${val}`);
268
+ }
269
+ if (!this.position.value.isEq(coerced)) {
270
+ this.position.next(coerced);
271
+ }
272
+ }
273
+ set stateInput(val) {
274
+ if (this.state.value !== val) {
275
+ this.state.next(val);
276
+ }
277
+ }
278
+ set modeInput(val) {
279
+ if (this.mode.value !== val) {
280
+ this.mode.next(val);
281
+ }
282
+ }
283
+ set fullSizeInput(val) {
284
+ const coerced = NumberWithUnit.coerce(val, "px");
285
+ if (this.#fullSize.value !== coerced) {
286
+ this.#fullSize.next(coerced);
287
+ }
288
+ }
289
+ #fullSize;
290
+ set miniSizeInput(val) {
291
+ const coerced = NumberWithUnit.coerce(val, "px");
292
+ if (this.#miniSize.value !== coerced) {
293
+ this.#miniSize.next(coerced);
294
+ }
295
+ }
296
+ #miniSize;
297
+ set minimizable(val) {
298
+ this.#minimizable = coerceBoolAttr(val);
299
+ this.#minimizableAuto = false;
300
+ }
301
+ get minimizable() {
302
+ return this.#minimizable;
303
+ }
304
+ #minimizable;
305
+ #minimizableAuto;
306
+ #contentSize;
307
+ #autoSize;
308
+ constructor() {
309
+ super();
310
+ this.el = inject((ElementRef));
311
+ this.position = new BehaviorSubject(DEFAULT_POSITION);
312
+ this.state = new BehaviorSubject("invisible");
313
+ this.mode = new BehaviorSubject("rigid");
314
+ this.#fullSize = new BehaviorSubject(AUTO_SIZE);
315
+ this.#miniSize = new BehaviorSubject(INVISIBLE_SIZE);
316
+ this.#minimizable = false;
317
+ this.#minimizableAuto = true;
318
+ this.#contentSize = watchDimension(this.el.nativeElement, "scroll-box").pipe(shareReplay(1));
319
+ this.#autoSize = combineLatest({
320
+ dim: this.#contentSize,
321
+ pos: this.position
322
+ }).pipe(map(({ dim, pos }) => {
323
+ if (pos.orient === "horizontal") {
324
+ return dim.height;
325
+ }
326
+ else {
327
+ return dim.width;
328
+ }
329
+ }), shareReplay(1));
330
+ this.fullSize = this.#fullSize.pipe(switchMap(size => {
331
+ if (size.unit === "auto") {
332
+ return this.#autoSize;
333
+ }
334
+ else {
335
+ return of(size);
336
+ }
337
+ }), shareReplay(1));
338
+ this.miniSize = this.#miniSize.pipe(switchMap(size => {
339
+ if (size.unit === "auto") {
340
+ return this.#autoSize;
341
+ }
342
+ else {
343
+ return of(size);
344
+ }
345
+ }), shareReplay(1));
346
+ this.changes = combineLatest({
347
+ position: this.position,
348
+ state: this.state,
349
+ mode: this.mode,
350
+ fullSize: this.fullSize,
351
+ miniSize: this.miniSize,
352
+ contentSize: this.#contentSize
353
+ });
354
+ this.d.sub(this.changes).subscribe(changes => {
355
+ if (this.#minimizableAuto) {
356
+ this.#minimizable = this.#miniSize.value.value !== 0;
357
+ }
358
+ FastDOM.setAttributes(this.el.nativeElement, {
359
+ state: changes.state,
360
+ orient: changes.position.orient,
361
+ mode: changes.mode,
362
+ side: changes.position.orient === "horizontal" ? changes.position.cells[0].v : changes.position.cells[0].h
363
+ });
364
+ const isHorizontal = changes.position.orient === "horizontal";
365
+ let w = null;
366
+ let h = null;
367
+ // TODO: when change state from mini -> invisible, currently wrong behavior
368
+ // the good behavior is to not gain fullSize ang go to invisible
369
+ if (changes.state === "mini") {
370
+ if (isHorizontal) {
371
+ h = changes.miniSize.unit === "auto" ? changes.contentSize.height : changes.miniSize;
372
+ }
373
+ else {
374
+ w = changes.miniSize.unit === "auto" ? changes.contentSize.width : changes.miniSize;
375
+ }
376
+ }
377
+ else {
378
+ if (isHorizontal) {
379
+ h = changes.fullSize.unit === "auto" ? changes.contentSize.height : changes.fullSize;
380
+ }
381
+ else {
382
+ w = changes.fullSize.unit === "auto" ? changes.contentSize.width : changes.fullSize;
383
+ }
384
+ }
385
+ FastDOM.setStyle(this.el.nativeElement, {
386
+ "--docking-panel-w": w != null ? `${w}` : null,
387
+ "--docking-panel-h": h != null ? `${h}` : null,
388
+ "--docking-panel-content-w": changes.contentSize.width,
389
+ "--docking-panel-content-h": changes.contentSize.height
390
+ }, () => FastDOM.setAttributes(this.el.nativeElement, { animate: "" }));
391
+ });
392
+ }
393
+ open() {
394
+ this.state.next("full");
395
+ }
396
+ close() {
397
+ this.state.next("invisible");
398
+ }
399
+ minimize() {
400
+ if (this.minimizable) {
401
+ this.state.next("mini");
402
+ }
403
+ }
404
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
405
+ 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-content-w: var(--docking-panel-content-w, var(---docking-panel-w));---docking-panel-content-h: var(--docking-panel-content-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)}: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=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"] }); }
406
+ }
407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingPanelComponent, decorators: [{
408
+ type: Component,
409
+ 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-content-w: var(--docking-panel-content-w, var(---docking-panel-w));---docking-panel-content-h: var(--docking-panel-content-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)}: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=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"] }]
410
+ }], ctorParameters: () => [], propDecorators: { positionInput: [{
411
+ type: Input,
412
+ args: ["position"]
413
+ }], stateInput: [{
414
+ type: Input,
415
+ args: ["state"]
416
+ }], state: [{
417
+ type: Output,
418
+ args: ["stateChanges"]
419
+ }], modeInput: [{
420
+ type: Input,
421
+ args: ["mode"]
422
+ }], fullSizeInput: [{
423
+ type: Input,
424
+ args: ["fullSize"]
425
+ }], miniSizeInput: [{
426
+ type: Input,
427
+ args: ["miniSize"]
428
+ }], minimizable: [{
429
+ type: Input,
430
+ args: ["minimizable"]
431
+ }] } });
432
+
433
+ const RIGID_ZINDEX = 20;
434
+ const OVER_ZINDEX = RIGID_ZINDEX * 2;
435
+ class DockingLayoutComponent extends Destructible {
436
+ constructor() {
437
+ super(...arguments);
438
+ this.#el = inject((ElementRef));
439
+ this.contentOnly = false;
440
+ this.#reflow = new Subject();
441
+ }
442
+ #el;
443
+ #reflow;
444
+ ngAfterViewInit() {
445
+ // eslint-disable-next-line prettier/prettier
446
+ this.panels = this.dockingPanels.changes.pipe(startWith(null), map(() => this.dockingPanels.toArray()), shareReplay(1));
447
+ this.d
448
+ .sub(combineLatest({ panels: this.panels, reflow: this.#reflow.pipe(startWith(null)) }))
449
+ .pipe(switchMap(({ panels }) => combineLatest(panels.map(panel => panel.changes.pipe(map(changes => {
450
+ return { panel, changes };
451
+ }))))))
452
+ .subscribe(this.#layout.bind(this));
453
+ }
454
+ ngOnChanges(changes) {
455
+ if ("contentOnly" in changes || "positionMode" in changes) {
456
+ this.#reflow.next();
457
+ }
458
+ }
459
+ #layout(entries) {
460
+ let paddingTop = 0;
461
+ let paddingRight = 0;
462
+ let paddingBottom = 0;
463
+ let paddingLeft = 0;
464
+ let rigidZIndex = RIGID_ZINDEX;
465
+ let overZIndex = OVER_ZINDEX;
466
+ if (this.contentOnly) {
467
+ // TODO:...
468
+ }
469
+ else {
470
+ for (const entry of entries) {
471
+ const panelState = entry.changes;
472
+ const panelSize = panelState.state === "full"
473
+ ? panelState.fullSize.value
474
+ : panelState.state === "mini"
475
+ ? panelState.miniSize.value
476
+ : 0;
477
+ const isHorizontal = panelState.position.orient === "horizontal";
478
+ const isRigid = panelState.mode === "rigid";
479
+ let panelTop = null;
480
+ let panelRight = null;
481
+ let panelBottom = null;
482
+ let panelLeft = null;
483
+ if (isHorizontal) {
484
+ panelLeft = 0;
485
+ panelRight = 0;
486
+ if (panelState.position.cells[0].v === "top") {
487
+ if (isRigid) {
488
+ paddingTop = Math.max(paddingTop, panelSize);
489
+ }
490
+ panelTop = 0;
491
+ }
492
+ else if (panelState.position.cells[0].v === "bottom") {
493
+ if (isRigid) {
494
+ paddingBottom = Math.max(paddingBottom, panelSize);
495
+ }
496
+ panelBottom = 0;
497
+ }
498
+ }
499
+ else {
500
+ panelTop = 0;
501
+ panelBottom = 0;
502
+ if (panelState.position.cells[0].h === "left") {
503
+ if (isRigid) {
504
+ paddingLeft = Math.max(paddingLeft, panelSize);
505
+ }
506
+ panelLeft = 0;
507
+ }
508
+ else if (panelState.position.cells[0].h === "right") {
509
+ if (isRigid) {
510
+ paddingRight = Math.max(paddingRight, panelSize);
511
+ }
512
+ panelRight = 0;
513
+ }
514
+ }
515
+ FastDOM.setStyle(entry.panel.el.nativeElement, {
516
+ "z-index": `${isRigid ? rigidZIndex++ : overZIndex++}`,
517
+ "--docking-panel-t": panelTop != null ? `${panelTop}px` : null,
518
+ "--docking-panel-r": panelRight != null ? `${panelRight}px` : null,
519
+ "--docking-panel-b": panelBottom != null ? `${panelBottom}px` : null,
520
+ "--docking-panel-l": panelLeft != null ? `${panelLeft}px` : null
521
+ });
522
+ }
523
+ FastDOM.setStyle(this.#el.nativeElement, {
524
+ "--docking-layout-top": `${paddingTop}px`,
525
+ "--docking-layout-right": `${paddingRight}px`,
526
+ "--docking-layout-bottom": `${paddingBottom}px`,
527
+ "--docking-layout-left": `${paddingLeft}px`
528
+ });
529
+ }
530
+ }
531
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
532
+ 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: `
533
+ <ng-content select="nu-docking-panel"></ng-content>
534
+
535
+ @if (!contentComponent) {
536
+ <nu-docking-content>
537
+ <ng-content></ng-content>
538
+ </nu-docking-content>
539
+ } @else {
540
+ <ng-content select="nu-docking-content"></ng-content>
541
+ }
542
+ `, 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", exportAs: ["nuDockingContent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
543
+ }
544
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: DockingLayoutComponent, decorators: [{
545
+ type: Component,
546
+ args: [{ selector: "nu-docking", standalone: true, imports: [DockingContentComponent], template: `
547
+ <ng-content select="nu-docking-panel"></ng-content>
548
+
549
+ @if (!contentComponent) {
550
+ <nu-docking-content>
551
+ <ng-content></ng-content>
552
+ </nu-docking-content>
553
+ } @else {
554
+ <ng-content select="nu-docking-content"></ng-content>
555
+ }
556
+ `, 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"] }]
557
+ }], propDecorators: { contentOnly: [{
558
+ type: Input
559
+ }], contentComponent: [{
560
+ type: ContentChild,
561
+ args: [DockingContentComponent]
562
+ }], dockingPanels: [{
563
+ type: ContentChildren,
564
+ args: [DockingPanelComponent]
565
+ }] } });
566
+
567
+ const members = [DockingLayoutComponent, DockingPanelComponent, DockingContentComponent];
568
+ class NuDockingLayout {
569
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: NuDockingLayout, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
570
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.1.3", ngImport: i0, type: NuDockingLayout, imports: [DockingLayoutComponent, DockingPanelComponent, DockingContentComponent], exports: [DockingLayoutComponent, DockingPanelComponent, DockingContentComponent] }); }
571
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: NuDockingLayout }); }
572
+ }
573
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: NuDockingLayout, decorators: [{
574
+ type: NgModule,
575
+ args: [{
576
+ imports: members,
577
+ exports: members
578
+ }]
579
+ }] });
580
+
581
+ const SLOT_REGEX = /^([^:\s]+)(?::(\d+))?(?:\s+as\s+(.*?))?$/i;
582
+ class SlotDef {
583
+ constructor(slot, tpl) {
584
+ this.tpl = tpl;
585
+ const match = slot.match(SLOT_REGEX);
586
+ if (!match) {
587
+ console.warn(`Invalid slot definition: ${slot}`);
588
+ }
589
+ else {
590
+ this.slot = match[1];
591
+ this.order = match[2] != null ? Number(match[2]) : Infinity;
592
+ this.id = match[3];
593
+ }
594
+ }
595
+ dispose() {
596
+ this.viewRef?.destroy();
597
+ this.viewRef = undefined;
598
+ }
599
+ }
600
+ /**
601
+ * @Directive({selector: "ng-template[xyzSlot]", inputs: [{name: "slot", alias: "xyzSlot"}]})
602
+ * class XYZSlotDirective extends SlotDirective<XYZComponentSlots> { }
603
+ *
604
+ * @Directive({selector: "ng-template[xyzSlotOutlet]", inputs: [{name: "slot", alias: "xyzSlotOutlet"}]})
605
+ * class XYZSlotOutletDirective extends SlotOutletDirective<XYZComponentSlots> { }
606
+ *
607
+ *
608
+ * @Component({provides: [SlotsService]})
609
+ * class XYZComponent {
610
+ * slots: inject(SlotsService<XYZComponentSlots>)
611
+ * }
612
+ *
613
+ *
614
+ */
615
+ class SlotsService extends Destructible {
616
+ #events = new Subject();
617
+ #entries = this.#events.pipe(scan((entries, event) => {
618
+ if (event.type === "add") {
619
+ const index = entries.findIndex(value => value === event.def);
620
+ if (index > -1) {
621
+ entries[index] = event.def;
622
+ }
623
+ else {
624
+ entries.push(event.def);
625
+ }
626
+ }
627
+ else if (event.type === "del") {
628
+ const index = entries.findIndex(value => value === event.def);
629
+ if (index > -1) {
630
+ entries.splice(index, 1);
631
+ }
632
+ }
633
+ return entries;
634
+ }, []), tap(entries => {
635
+ entries.sort((a, b) => {
636
+ if (a.slot === b.slot) {
637
+ return a.order - b.order;
638
+ }
639
+ else {
640
+ return a.slot.localeCompare(b.slot);
641
+ }
642
+ });
643
+ }), shareReplay(1));
644
+ constructor() {
645
+ super();
646
+ // XXX: need to collect entries from the beginning
647
+ this.d.sub(this.#entries).subscribe();
648
+ }
649
+ addTpl(def) {
650
+ this.#events.next({ type: "add", def });
651
+ }
652
+ delTpl(def) {
653
+ this.#events.next({ type: "del", def });
654
+ }
655
+ #watchers = {};
656
+ watch(slot) {
657
+ const existing = this.#watchers[slot];
658
+ if (existing == null) {
659
+ return (this.#watchers[slot] = this.#watch(slot));
660
+ }
661
+ else {
662
+ return existing;
663
+ }
664
+ }
665
+ #watch(slot) {
666
+ return this.#entries.pipe(map(entries => entries.filter(entry => entry.slot === slot)), distinctUntilChanged((prev, curr) => {
667
+ if (prev.length === curr.length) {
668
+ for (let i = 0; i < prev.length; i++) {
669
+ if (prev[i] !== curr[i]) {
670
+ return false;
671
+ }
672
+ }
673
+ return true;
674
+ }
675
+ else {
676
+ return false;
677
+ }
678
+ }), finalize(() => {
679
+ delete this.#watchers[slot];
680
+ }), shareReplay(1));
681
+ }
682
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
683
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService }); }
684
+ }
685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotsService, decorators: [{
686
+ type: Injectable
687
+ }], ctorParameters: () => [] });
688
+ class SlotDirective {
689
+ constructor() {
690
+ this.tpl = inject((TemplateRef));
691
+ }
692
+ set slot(slot) {
693
+ if (this.#slot !== slot) {
694
+ this.#slot = slot;
695
+ if (this.#slotDef) {
696
+ this.slotSvc.delTpl(this.#slotDef);
697
+ }
698
+ this.#slotDef = new SlotDef(slot, this.tpl);
699
+ this.slotSvc.addTpl(this.#slotDef);
700
+ }
701
+ }
702
+ get slot() {
703
+ return this.#slot;
704
+ }
705
+ #slot;
706
+ #slotDef;
707
+ ngOnDestroy() {
708
+ if (this.#slotDef) {
709
+ this.slotSvc.delTpl(this.#slotDef);
710
+ }
711
+ }
712
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
713
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.1.3", type: SlotDirective, ngImport: i0 }); }
714
+ }
715
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotDirective, decorators: [{
716
+ type: Directive
717
+ }] });
718
+ class SlotOutletDirective extends Destructible {
719
+ set slot(slot) {
720
+ if (this.#slot.value !== slot) {
721
+ this.#slot.next(slot);
722
+ }
723
+ }
724
+ get slot() {
725
+ return this.#slot.value;
726
+ }
727
+ #slot;
728
+ #watch;
729
+ #views;
730
+ constructor() {
731
+ super();
732
+ this.vcr = inject(ViewContainerRef);
733
+ this.injector = inject(Injector);
734
+ this.#slot = new BehaviorSubject(null);
735
+ this.#watch = this.#slot.pipe(switchMap(slot => {
736
+ if (slot) {
737
+ return this.slotSvc.watch(slot);
738
+ }
739
+ else {
740
+ return of([]);
741
+ }
742
+ }));
743
+ this.#views = [];
744
+ this.#onEntriesChanged = (entries) => {
745
+ const { remove, undecided } = this.#determineActions(entries);
746
+ for (const r of remove) {
747
+ r.dispose();
748
+ const idx = this.#views.indexOf(r);
749
+ if (idx >= 0) {
750
+ this.#views.splice(idx, 1);
751
+ }
752
+ }
753
+ this.#views.length = 0;
754
+ for (const [pos, entry] of undecided.entries()) {
755
+ if (entry.viewRef && !entry.viewRef.destroyed) {
756
+ const currentPos = this.vcr.indexOf(entry.viewRef);
757
+ if (currentPos !== pos) {
758
+ this.vcr.insert(entry.viewRef, pos);
759
+ }
760
+ }
761
+ else {
762
+ ;
763
+ entry.viewRef = this.vcr.createEmbeddedView(entry.tpl, null, {
764
+ index: pos,
765
+ injector: this.injector
766
+ });
767
+ entry.viewRef.markForCheck();
768
+ }
769
+ this.#views.push(entry);
770
+ }
771
+ };
772
+ this.d.any(this.#clearViews.bind(this));
773
+ }
774
+ ngOnInit() {
775
+ this.d.sub(this.#watch).subscribe(this.#onEntriesChanged);
776
+ }
777
+ #onEntriesChanged;
778
+ #determineActions(entries) {
779
+ const byId = {};
780
+ let remove = [];
781
+ const undecided = [];
782
+ for (const entry of entries) {
783
+ if (entry.id != null) {
784
+ if (!byId[entry.id]) {
785
+ byId[entry.id] = [entry];
786
+ }
787
+ else {
788
+ byId[entry.id].push(entry);
789
+ }
790
+ }
791
+ else {
792
+ undecided.push(entry);
793
+ }
794
+ }
795
+ for (const values of Object.values(byId)) {
796
+ remove = remove.concat(values.slice(0, -1));
797
+ undecided.push(values[values.length - 1]);
798
+ }
799
+ for (const current of this.#views) {
800
+ if (!undecided.includes(current)) {
801
+ remove.push(current);
802
+ }
803
+ }
804
+ return { remove, undecided };
805
+ }
806
+ #clearViews() {
807
+ this.vcr.clear();
808
+ this.#views = [];
809
+ }
810
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotOutletDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
811
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.1.3", type: SlotOutletDirective, usesInheritance: true, ngImport: i0 }); }
812
+ }
813
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: SlotOutletDirective, decorators: [{
814
+ type: Directive
815
+ }], ctorParameters: () => [] });
816
+
817
+ /**
818
+ * Generated bundle index. Do not edit.
819
+ */
820
+
821
+ export { DockingContentComponent, DockingLayoutComponent, DockingPanelComponent, L9Cell, L9Range, L9State, NuDockingLayout, SlotDef, SlotDirective, SlotOutletDirective, SlotsService, watchDimension, watchMedia };
822
+ //# sourceMappingURL=ngutil-layout.mjs.map