@planeasyinc/le-angular 0.0.14 → 0.0.16

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.
@@ -0,0 +1,2531 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, signal, Injectable, computed, effect, untracked, Directive, input, ChangeDetectionStrategy, Component, output, forwardRef, ViewContainerRef, Injector, Renderer2, ElementRef, DestroyRef, viewChild } from '@angular/core';
3
+ import { HttpContextToken, HttpClient, HttpContext, HttpRequest, HttpEventType } from '@angular/common/http';
4
+ import { map, filter, distinctUntilChanged, BehaviorSubject, Subject, takeUntil, firstValueFrom, of, tap, catchError, from, concatMap, finalize, fromEvent, startWith } from 'rxjs';
5
+ import { decodeJwt, UrlFragmentBuilder, normalizeConfig } from '@planeasyinc/le-core';
6
+ import { Location, JsonPipe, UpperCasePipe, DatePipe, ViewportScroller } from '@angular/common';
7
+ import { CdkDrag } from '@angular/cdk/drag-drop';
8
+ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
9
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
+ import { coerceNumberProperty } from '@angular/cdk/coercion';
11
+ import { ComponentPortal, CdkPortalOutlet, TemplatePortal } from '@angular/cdk/portal';
12
+ import { DataSource, CdkTable, CdkColumnDef, CdkHeaderCell, CdkHeaderCellDef, CdkCell, CdkCellDef, CdkHeaderRowDef, CdkHeaderRow, CdkRow, CdkRowDef } from '@angular/cdk/table';
13
+ import { DialogModule } from '@angular/cdk/dialog';
14
+ import { CdkMenuTrigger, CdkMenu, CdkMenuItem } from '@angular/cdk/menu';
15
+ import { A11yModule } from '@angular/cdk/a11y';
16
+ import { Overlay } from '@angular/cdk/overlay';
17
+ import { NestedTreeControl, CdkTree, CdkNestedTreeNode, CdkTreeNodeDef, CdkTreeNodeOutlet, CdkTreeNodeToggle } from '@angular/cdk/tree';
18
+ import { ArrayDataSource, SelectionModel } from '@angular/cdk/collections';
19
+ import { adaptOld } from '@planeasyinc/fe-adapters-old';
20
+ import { adaptSections } from '@planeasyinc/fe-adapters-sections';
21
+ import { createEngine } from '@planeasyinc/fe-core';
22
+ import { FeFieldHost } from '@planeasyinc/fe-angular';
23
+ import * as i1 from '@angular/forms';
24
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
25
+
26
+ const IS_LIBRARY_REQUEST = new HttpContextToken(() => false);
27
+
28
+ const defaultConfig = {
29
+ apiUrl: '',
30
+ withRouting: false,
31
+ rootPath: '/',
32
+ messages: {
33
+ tokenEmpty: 'No token provided',
34
+ tokenExpired: 'Your session has expired for security reasons. Please log in again to continue where you left off.',
35
+ },
36
+ };
37
+
38
+ const CONFIG_TOKEN = new InjectionToken('CONFIG', {
39
+ factory: () => defaultConfig,
40
+ });
41
+
42
+ const SECOND_IN_MS = 1000;
43
+ class LEAuthService {
44
+ config = inject(CONFIG_TOKEN);
45
+ _token = signal(null);
46
+ _apiUrl = signal(this.config.apiUrl);
47
+ _isGuest = signal(false);
48
+ token = this._token.asReadonly();
49
+ apiUrl = this._apiUrl.asReadonly();
50
+ isGuest = this._isGuest.asReadonly();
51
+ setApiUrl(apiUrl) {
52
+ this._apiUrl.set(apiUrl);
53
+ }
54
+ setToken(token) {
55
+ this._token.set(token);
56
+ }
57
+ setGuestLogin(isGuest = true) {
58
+ this._isGuest.set(true);
59
+ }
60
+ validateToken() {
61
+ const token = this._token();
62
+ if (!token) {
63
+ throw new Error(this.config.messages.tokenEmpty);
64
+ }
65
+ const decoded = decodeJwt(token);
66
+ if (decoded.exp * SECOND_IN_MS < Date.now()) {
67
+ throw new Error(this.config.messages.tokenExpired);
68
+ }
69
+ }
70
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
71
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEAuthService, providedIn: 'root' });
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEAuthService, decorators: [{
74
+ type: Injectable,
75
+ args: [{
76
+ providedIn: 'root',
77
+ }]
78
+ }] });
79
+
80
+ class LEApiService {
81
+ http = inject(HttpClient);
82
+ authService = inject(LEAuthService);
83
+ apiUrl = computed(() => {
84
+ return this.normalizeApiUrl(this.authService.apiUrl());
85
+ });
86
+ getConfig() {
87
+ const url = new UrlFragmentBuilder(this.apiUrl())
88
+ .setFragment('api/contrib/frontend-config/dashboard/config')
89
+ .build();
90
+ return this.http.get(url, { context: new HttpContext().set(IS_LIBRARY_REQUEST, true) });
91
+ }
92
+ getObject(className, query = '') {
93
+ const url = new UrlFragmentBuilder(this.apiUrl())
94
+ .setFragment('api/objects/')
95
+ .setQuery(`class_name=${className}`)
96
+ .setQuery('include_metadata=true&include_subclasses=true&load_references=false&file_optimized=true')
97
+ .setQuery(query)
98
+ .build();
99
+ return this.http.get(url, {
100
+ context: new HttpContext().set(IS_LIBRARY_REQUEST, true),
101
+ });
102
+ }
103
+ getObjectByAddress(address, params) {
104
+ const url = new UrlFragmentBuilder(this.apiUrl())
105
+ .setFragment(`api/objects/${address}/`)
106
+ .setQuery(`all_versions=${params.includeVersions ?? false}&include_metadata=${params.includeMeta ?? false}&load_references=${params.loadReferences ?? false}&file_optimized=${params.fileOptimized ?? true}`)
107
+ .build();
108
+ return this.http.get(url, {
109
+ context: new HttpContext().set(IS_LIBRARY_REQUEST, true),
110
+ });
111
+ }
112
+ executeTransaction(name, body, query = '') {
113
+ const url = new UrlFragmentBuilder(this.apiUrl())
114
+ .setFragment(`api/transactions/${name}/`)
115
+ .setQuery(query)
116
+ .build();
117
+ return this.http.post(url, body ?? {}, {
118
+ context: new HttpContext().set(IS_LIBRARY_REQUEST, true),
119
+ });
120
+ }
121
+ request(method, path, body) {
122
+ const url = new UrlFragmentBuilder(this.apiUrl()).setFragment(path).build();
123
+ const request = new HttpRequest(method, url, body, {
124
+ responseType: 'json',
125
+ context: new HttpContext().set(IS_LIBRARY_REQUEST, true),
126
+ });
127
+ return this.http.request(request);
128
+ }
129
+ getBlob(path) {
130
+ return this.http
131
+ .get(path, { responseType: 'blob', context: new HttpContext().set(IS_LIBRARY_REQUEST, true) })
132
+ .pipe(map((blob) => URL.createObjectURL(blob)));
133
+ }
134
+ normalizeApiUrl(url) {
135
+ if (url.endsWith('/')) {
136
+ return url.slice(0, -1);
137
+ }
138
+ return url;
139
+ }
140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
141
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEApiService });
142
+ }
143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEApiService, decorators: [{
144
+ type: Injectable
145
+ }] });
146
+
147
+ class LeNavigationService {
148
+ location = inject(Location);
149
+ config = inject(CONFIG_TOKEN);
150
+ _node = signal(null);
151
+ node = this._node.asReadonly();
152
+ constructor() {
153
+ effect((onCleanup) => {
154
+ if (!this.config.withRouting)
155
+ return;
156
+ const unregister = this.location.onUrlChange((url, state) => {
157
+ this._node.set(state.node ?? null);
158
+ });
159
+ onCleanup(() => {
160
+ unregister();
161
+ });
162
+ });
163
+ }
164
+ navigate(node) {
165
+ if (!this.config.withRouting)
166
+ return;
167
+ const path = this.mapNodeToLocationPath(node);
168
+ this.location.go(`/v2/${path}`, undefined, { node });
169
+ }
170
+ mapNodeToLocationPath(node) {
171
+ let pathChunks = [];
172
+ let query = '';
173
+ if (node.type === 'table') {
174
+ pathChunks.push(node?.title || node?.dataSource?.entity);
175
+ if (node?.dataSource?.params) {
176
+ query = Object.entries(node.dataSource.params).map(entry => entry.join('=')).join('&');
177
+ }
178
+ }
179
+ if (node.type === 'form') {
180
+ pathChunks.push('details', node.controlSource?.kind, node.controlSource?.entity, node.controlSource?.params?.className, node.controlSource?.params?.address, node.controlSource?.params?.mode);
181
+ }
182
+ const path = pathChunks.filter(v => !!v).join('/');
183
+ return query ? `${path}?${query}` : path;
184
+ }
185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
186
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeNavigationService });
187
+ }
188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeNavigationService, decorators: [{
189
+ type: Injectable
190
+ }], ctorParameters: () => [] });
191
+
192
+ class LEDataService {
193
+ apiService = inject(LEApiService);
194
+ leNavigationService = inject(LeNavigationService);
195
+ _config = signal(null);
196
+ _view = signal(null);
197
+ _parentPath = signal([]);
198
+ _customCss = signal(null);
199
+ view = this._view.asReadonly();
200
+ parentPath = this._parentPath.asReadonly();
201
+ config = this._config.asReadonly();
202
+ customCss = this._customCss.asReadonly();
203
+ registerEffects() {
204
+ effect(() => {
205
+ const node = this.leNavigationService.node();
206
+ if (!node)
207
+ return;
208
+ untracked(() => {
209
+ const view = this.view();
210
+ if (node.id === view?.id)
211
+ return;
212
+ this._view.set(node);
213
+ });
214
+ });
215
+ }
216
+ setView(node, skipLocationChange) {
217
+ this._view.set(node);
218
+ if (!skipLocationChange) {
219
+ window.scrollTo({ top: 0, behavior: 'smooth' });
220
+ this.leNavigationService.navigate(node);
221
+ }
222
+ }
223
+ requestConfig(force) {
224
+ if (this._config() && !force) {
225
+ return;
226
+ }
227
+ this.apiService.getConfig().subscribe({
228
+ next: (config) => {
229
+ const normalizedConfig = normalizeConfig({ type: 'section', ...config });
230
+ this._config.set(normalizedConfig);
231
+ this._customCss.set(config.custom_css);
232
+ const initialView = this.getInitialView(normalizedConfig);
233
+ this.setView(initialView.node);
234
+ this._parentPath.set(initialView.path);
235
+ },
236
+ error: (err) => {
237
+ console.error(err);
238
+ },
239
+ });
240
+ }
241
+ request(method, url, body) {
242
+ return this.apiService
243
+ .request(method, url, body)
244
+ .pipe(filter((event) => HttpEventType.Response === event.type));
245
+ }
246
+ getObjectByClassName(className) {
247
+ return this.apiService.getObject(className);
248
+ }
249
+ navigateByNodeId(id) {
250
+ const node = this.getNodeById(id);
251
+ if (node) {
252
+ this._view.set(node);
253
+ }
254
+ }
255
+ getNodeById(id, node = this._config()) {
256
+ if (!node)
257
+ return null;
258
+ if (node.id === id) {
259
+ return node;
260
+ }
261
+ if ('children' in node && node.children.length) {
262
+ for (const child of node.children) {
263
+ const target = this.getNodeById(id, child);
264
+ if (target) {
265
+ return target;
266
+ }
267
+ }
268
+ }
269
+ return null;
270
+ }
271
+ getInitialView(node, path = []) {
272
+ if ('children' in node) {
273
+ const child = node.children[0];
274
+ const _path = [...path, node];
275
+ if ('isParentMenu' in child && child.isParentMenu) {
276
+ return this.getInitialView(child, _path);
277
+ }
278
+ else {
279
+ return {
280
+ node: child,
281
+ path: _path,
282
+ };
283
+ }
284
+ }
285
+ throw new Error(`${node.type} is not a section`);
286
+ }
287
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
288
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEDataService });
289
+ }
290
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEDataService, decorators: [{
291
+ type: Injectable
292
+ }] });
293
+
294
+ class LEFiltersService {
295
+ _shown = signal(false);
296
+ _options = signal([], {
297
+ equal: (a, b) => this.isEqual(a, b),
298
+ });
299
+ _selection = signal({}, {
300
+ equal: (a, b) => this.isEqual(a, b),
301
+ });
302
+ shown = this._shown.asReadonly();
303
+ options = this._options.asReadonly();
304
+ selection = this._selection.asReadonly();
305
+ open() {
306
+ this._shown.set(true);
307
+ }
308
+ close() {
309
+ this._shown.set(false);
310
+ }
311
+ updateSelection(value) {
312
+ this._selection.set(this.filterValues(value));
313
+ }
314
+ setOptions(options) {
315
+ this._options.set(options);
316
+ }
317
+ filterValues(value) {
318
+ const clone = Object.assign({}, value);
319
+ for (const key in clone) {
320
+ if (!clone[key]) {
321
+ delete clone[key];
322
+ }
323
+ }
324
+ return clone;
325
+ }
326
+ isEqual(a, b) {
327
+ return JSON.stringify(a) === JSON.stringify(b);
328
+ }
329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEFiltersService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
330
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEFiltersService });
331
+ }
332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LEFiltersService, decorators: [{
333
+ type: Injectable
334
+ }] });
335
+
336
+ class DrawerContentDirective {
337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DrawerContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
338
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.21", type: DrawerContentDirective, isStandalone: true, selector: "[leDrawerContent]", host: { properties: { "class.le-drawer-content": "true" } }, ngImport: i0 });
339
+ }
340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DrawerContentDirective, decorators: [{
341
+ type: Directive,
342
+ args: [{
343
+ selector: '[leDrawerContent]',
344
+ host: {
345
+ '[class.le-drawer-content]': 'true',
346
+ }
347
+ }]
348
+ }] });
349
+
350
+ class DrawerContainerDirective {
351
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DrawerContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
352
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.21", type: DrawerContainerDirective, isStandalone: true, selector: "[leDrawerContainer]", host: { properties: { "class.le-drawer-container": "true" } }, ngImport: i0 });
353
+ }
354
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DrawerContainerDirective, decorators: [{
355
+ type: Directive,
356
+ args: [{
357
+ selector: '[leDrawerContainer]',
358
+ host: {
359
+ '[class.le-drawer-container]': 'true'
360
+ }
361
+ }]
362
+ }] });
363
+
364
+ const DRAWER_OPENED_WIDTH = 300;
365
+ const DRAWER_CLOSED_WIDTH = 60;
366
+ class LeDrawerDirective {
367
+ opened = input(false);
368
+ width = input(DRAWER_OPENED_WIDTH);
369
+ hideOnClose = input(false);
370
+ type = input('side');
371
+ _width = computed(() => {
372
+ if (this.opened()) {
373
+ return this.width();
374
+ }
375
+ if (this.hideOnClose()) {
376
+ return 0;
377
+ }
378
+ return DRAWER_CLOSED_WIDTH;
379
+ });
380
+ _position = computed(() => {
381
+ return this.type() === 'over' ? 'absolute' : 'relative';
382
+ });
383
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeDrawerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
384
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: LeDrawerDirective, isStandalone: true, selector: "[leDrawer]", inputs: { opened: { classPropertyName: "opened", publicName: "opened", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, hideOnClose: { classPropertyName: "hideOnClose", publicName: "hideOnClose", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.le-drawer": "true", "class.le-drawer--open": "opened()", "style.width.px": "_width()", "style.position": "_position()", "style.min-height": "\"100%\"", "style.left": "0", "style.top": "0" } }, ngImport: i0 });
385
+ }
386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeDrawerDirective, decorators: [{
387
+ type: Directive,
388
+ args: [{
389
+ selector: '[leDrawer]',
390
+ host: {
391
+ '[class.le-drawer]': 'true',
392
+ '[class.le-drawer--open]': 'opened()',
393
+ '[style.width.px]': '_width()',
394
+ '[style.position]': '_position()',
395
+ '[style.min-height]': '"100%"',
396
+ '[style.left]': '0',
397
+ '[style.top]': '0',
398
+ },
399
+ }]
400
+ }] });
401
+
402
+ const close = {
403
+ name: 'close',
404
+ viewBox: '0 -960 960 960',
405
+ content: [
406
+ 'm256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z',
407
+ ],
408
+ };
409
+
410
+ const arrowDown = {
411
+ name: 'arrow-down',
412
+ viewBox: '0 -960 960 960',
413
+ content: [
414
+ 'M480-360 280-560h400L480-360Z'
415
+ ]
416
+ };
417
+
418
+ const sort = {
419
+ name: 'sort',
420
+ viewBox: '0 -960 960 960',
421
+ content: [
422
+ 'M120-240v-80h240v80H120Zm0-200v-80h480v80H120Zm0-200v-80h720v80H120Z'
423
+ ]
424
+ };
425
+
426
+ const chevron = {
427
+ name: 'chevron',
428
+ viewBox: '0 -960 960 960',
429
+ content: [
430
+ 'M504-480 320-664l56-56 240 240-240 240-56-56 184-184Z'
431
+ ]
432
+ };
433
+
434
+ const info = {
435
+ name: 'info',
436
+ viewBox: '0 -960 960 960',
437
+ content: [
438
+ 'M440-280h80v-240h-80v240Zm68.5-331.5Q520-623 520-640t-11.5-28.5Q497-680 480-680t-28.5 11.5Q440-657 440-640t11.5 28.5Q463-600 480-600t28.5-11.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z',
439
+ ],
440
+ };
441
+
442
+ const ICONS_MAP = {
443
+ chevron: chevron,
444
+ 'arrow-down': arrowDown,
445
+ sort: sort,
446
+ close: close,
447
+ info: info
448
+ };
449
+
450
+ class LeIconComponent {
451
+ name = input.required();
452
+ size = input(24);
453
+ icon = computed(() => {
454
+ return {
455
+ ...ICONS_MAP[this.name()],
456
+ size: coerceNumberProperty(this.size()),
457
+ };
458
+ });
459
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
460
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: LeIconComponent, isStandalone: true, selector: "le-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.width.px": "size()", "style.height.px": "size()", "style.display": "\"flex\"" } }, ngImport: i0, template: `
461
+ @if (icon(); as icon) {
462
+ <svg
463
+ [attr.viewBox]="icon.viewBox"
464
+ [style.width.px]="icon.size"
465
+ [style.height.px]="icon.size"
466
+ fill="currentColor"
467
+ >
468
+ @for (path of icon.content; track path) {
469
+ <path [attr.d]="path"></path>
470
+ }
471
+ </svg>
472
+ }
473
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
474
+ }
475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeIconComponent, decorators: [{
476
+ type: Component,
477
+ args: [{
478
+ selector: 'le-icon',
479
+ template: `
480
+ @if (icon(); as icon) {
481
+ <svg
482
+ [attr.viewBox]="icon.viewBox"
483
+ [style.width.px]="icon.size"
484
+ [style.height.px]="icon.size"
485
+ fill="currentColor"
486
+ >
487
+ @for (path of icon.content; track path) {
488
+ <path [attr.d]="path"></path>
489
+ }
490
+ </svg>
491
+ }
492
+ `,
493
+ changeDetection: ChangeDetectionStrategy.OnPush,
494
+ host: {
495
+ '[style.width.px]': 'size()',
496
+ '[style.height.px]': 'size()',
497
+ '[style.display]': '"flex"',
498
+ }
499
+ }]
500
+ }] });
501
+
502
+ class SidebarComponent {
503
+ breakpointObserver = inject(BreakpointObserver);
504
+ config = input.required();
505
+ view = input.required();
506
+ selectionChange = output();
507
+ _expanded = signal(new Set());
508
+ _width = signal(300);
509
+ _isDrawerOpen = signal(true);
510
+ _isSmallScreen = signal(false);
511
+ menu = computed(() => {
512
+ const config = this.config();
513
+ return 'children' in config ? config.children : [];
514
+ });
515
+ expanded = this._expanded.asReadonly();
516
+ width = this._width.asReadonly();
517
+ isDrawerOpen = this._isDrawerOpen.asReadonly();
518
+ isSmallScreen = this._isSmallScreen.asReadonly();
519
+ constructor() {
520
+ this.breakpointObserver
521
+ .observe([Breakpoints.XSmall])
522
+ .pipe(map((state) => state.matches), distinctUntilChanged(), takeUntilDestroyed())
523
+ .subscribe({
524
+ next: (matches) => {
525
+ this._isSmallScreen.set(matches);
526
+ this._isDrawerOpen.set(!matches);
527
+ },
528
+ });
529
+ }
530
+ isSection(item) {
531
+ return item.type === 'section' && item.isParentMenu;
532
+ }
533
+ toggleExpanded(event, item) {
534
+ event.preventDefault();
535
+ event.stopPropagation();
536
+ this._expanded.update((expanded) => {
537
+ if (expanded.has(item)) {
538
+ expanded.delete(item);
539
+ }
540
+ else {
541
+ expanded.add(item);
542
+ }
543
+ return new Set(expanded);
544
+ });
545
+ }
546
+ setExpanded(item) {
547
+ this._expanded.update((expanded) => {
548
+ expanded.add(item);
549
+ return new Set(expanded);
550
+ });
551
+ }
552
+ onSidebarItemClick(event, item) {
553
+ event.preventDefault();
554
+ event.stopPropagation();
555
+ this.selectionChange.emit(item);
556
+ }
557
+ onDrawerToggleClick(event) {
558
+ event.preventDefault();
559
+ event.stopPropagation();
560
+ this._isDrawerOpen.update((val) => !val);
561
+ }
562
+ onDragMoved(event) {
563
+ this._width.set(event.pointerPosition.x);
564
+ const element = event.source.element.nativeElement;
565
+ element.style.transform = 'none';
566
+ }
567
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
568
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SidebarComponent, isStandalone: true, selector: "le-sidebar", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, ngImport: i0, template: "<div leDrawerContainer>\n <aside leDrawer [opened]=\"isDrawerOpen()\" [width]=\"width()\" [hideOnClose]=\"isSmallScreen()\">\n <h3 class=\"le-sidebar-title\">\n <button\n class=\"le-sidebar-toggle\"\n (click)=\"onDrawerToggleClick($event)\"\n (keydown.enter)=\"onDrawerToggleClick($event)\"\n >\n @if (isDrawerOpen()) {\n {{ config().title }}\n }\n\n <le-icon name=\"chevron\"></le-icon>\n </button>\n </h3>\n\n @if (isDrawerOpen()) {\n <menu>\n @for (item of menu(); track item) {\n @if (isSection(item)) {\n <a\n class=\"le-menu-item\"\n tabindex=\"0\"\n [class.le-menu-item--expanded]=\"expanded().has(item)\"\n (click)=\"toggleExpanded($event, item)\"\n (keydown.enter)=\"toggleExpanded($event, item)\"\n >\n <div class=\"le-menu-title\">\n {{ item.title }}\n </div>\n\n <le-icon class=\"le-menu-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div class=\"le-menu-group\" [class.le-menu-group--expanded]=\"expanded().has(item)\">\n @for (child of item.children; track child) {\n @if (isSection(child)) {\n <a\n class=\"le-menu-item\"\n [tabindex]=\"expanded().has(item) ? 0 : -1\"\n [class.le-menu-item--expanded]=\"expanded().has(child)\"\n (click)=\"toggleExpanded($event, child)\"\n (keydown.enter)=\"toggleExpanded($event, child)\"\n >\n <div class=\"le-menu-title\">\n {{ child.title }}\n </div>\n\n <le-icon class=\"le-menu-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div class=\"le-menu-group\" [class.le-menu-group--expanded]=\"expanded().has(child)\">\n @for (grandchild of child.children; track grandchild) {\n <a\n [tabindex]=\"expanded().has(child) ? 0 : -1\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === grandchild.id\"\n (click)=\"onSidebarItemClick($event, grandchild)\"\n (keydown.enter)=\"onSidebarItemClick($event, grandchild)\"\n >\n <div class=\"le-menu-title\">\n {{ grandchild.title }}\n </div>\n </a>\n }\n </div>\n } @else {\n <a\n [tabindex]=\"expanded().has(item) ? 0 : -1\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === child.id\"\n (click)=\"onSidebarItemClick($event, child)\"\n (keydown.enter)=\"onSidebarItemClick($event, child)\"\n >\n <div class=\"le-menu-title\">\n {{ child.title }}\n </div>\n </a>\n }\n }\n </div>\n } @else {\n <a\n tabindex=\"0\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === item.id\"\n (click)=\"onSidebarItemClick($event, item)\"\n (keydown.enter)=\"onSidebarItemClick($event, item)\"\n >\n <div class=\"le-menu-title\">\n {{ item.title }}\n </div>\n </a>\n }\n }\n </menu>\n\n <div class=\"le-drawer-resizer\" cdkDrag (cdkDragMoved)=\"onDragMoved($event)\">\n <span class=\"le-drag-icon\">\u283F</span>\n </div>\n }\n </aside>\n\n <main leDrawerContent>\n <ng-content></ng-content>\n </main>\n</div>\n", dependencies: [{ kind: "directive", type: DrawerContentDirective, selector: "[leDrawerContent]" }, { kind: "directive", type: DrawerContainerDirective, selector: "[leDrawerContainer]" }, { kind: "directive", type: LeDrawerDirective, selector: "[leDrawer]", inputs: ["opened", "width", "hideOnClose", "type"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: LeIconComponent, selector: "le-icon", inputs: ["name", "size"] }] });
569
+ }
570
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SidebarComponent, decorators: [{
571
+ type: Component,
572
+ args: [{ selector: 'le-sidebar', imports: [DrawerContentDirective, DrawerContainerDirective, LeDrawerDirective, CdkDrag, LeIconComponent], template: "<div leDrawerContainer>\n <aside leDrawer [opened]=\"isDrawerOpen()\" [width]=\"width()\" [hideOnClose]=\"isSmallScreen()\">\n <h3 class=\"le-sidebar-title\">\n <button\n class=\"le-sidebar-toggle\"\n (click)=\"onDrawerToggleClick($event)\"\n (keydown.enter)=\"onDrawerToggleClick($event)\"\n >\n @if (isDrawerOpen()) {\n {{ config().title }}\n }\n\n <le-icon name=\"chevron\"></le-icon>\n </button>\n </h3>\n\n @if (isDrawerOpen()) {\n <menu>\n @for (item of menu(); track item) {\n @if (isSection(item)) {\n <a\n class=\"le-menu-item\"\n tabindex=\"0\"\n [class.le-menu-item--expanded]=\"expanded().has(item)\"\n (click)=\"toggleExpanded($event, item)\"\n (keydown.enter)=\"toggleExpanded($event, item)\"\n >\n <div class=\"le-menu-title\">\n {{ item.title }}\n </div>\n\n <le-icon class=\"le-menu-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div class=\"le-menu-group\" [class.le-menu-group--expanded]=\"expanded().has(item)\">\n @for (child of item.children; track child) {\n @if (isSection(child)) {\n <a\n class=\"le-menu-item\"\n [tabindex]=\"expanded().has(item) ? 0 : -1\"\n [class.le-menu-item--expanded]=\"expanded().has(child)\"\n (click)=\"toggleExpanded($event, child)\"\n (keydown.enter)=\"toggleExpanded($event, child)\"\n >\n <div class=\"le-menu-title\">\n {{ child.title }}\n </div>\n\n <le-icon class=\"le-menu-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div class=\"le-menu-group\" [class.le-menu-group--expanded]=\"expanded().has(child)\">\n @for (grandchild of child.children; track grandchild) {\n <a\n [tabindex]=\"expanded().has(child) ? 0 : -1\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === grandchild.id\"\n (click)=\"onSidebarItemClick($event, grandchild)\"\n (keydown.enter)=\"onSidebarItemClick($event, grandchild)\"\n >\n <div class=\"le-menu-title\">\n {{ grandchild.title }}\n </div>\n </a>\n }\n </div>\n } @else {\n <a\n [tabindex]=\"expanded().has(item) ? 0 : -1\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === child.id\"\n (click)=\"onSidebarItemClick($event, child)\"\n (keydown.enter)=\"onSidebarItemClick($event, child)\"\n >\n <div class=\"le-menu-title\">\n {{ child.title }}\n </div>\n </a>\n }\n }\n </div>\n } @else {\n <a\n tabindex=\"0\"\n class=\"le-menu-item\"\n [class.le-menu-item--selected]=\"view().id === item.id\"\n (click)=\"onSidebarItemClick($event, item)\"\n (keydown.enter)=\"onSidebarItemClick($event, item)\"\n >\n <div class=\"le-menu-title\">\n {{ item.title }}\n </div>\n </a>\n }\n }\n </menu>\n\n <div class=\"le-drawer-resizer\" cdkDrag (cdkDragMoved)=\"onDragMoved($event)\">\n <span class=\"le-drag-icon\">\u283F</span>\n </div>\n }\n </aside>\n\n <main leDrawerContent>\n <ng-content></ng-content>\n </main>\n</div>\n" }]
573
+ }], ctorParameters: () => [] });
574
+
575
+ class ChartViewComponent {
576
+ node = input.required();
577
+ config = input();
578
+ event = output();
579
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ChartViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
580
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.21", type: ChartViewComponent, isStandalone: true, selector: "chart-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "<h2>{{ node()?.title }}</h2>\n" });
581
+ }
582
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ChartViewComponent, decorators: [{
583
+ type: Component,
584
+ args: [{ selector: 'chart-view', template: "<h2>{{ node()?.title }}</h2>\n" }]
585
+ }] });
586
+
587
+ class GridComponent {
588
+ node = input.required();
589
+ config = input();
590
+ event = output();
591
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: GridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
592
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: GridComponent, isStandalone: true, selector: "grid-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, host: { properties: { "class.le-gird-view": "true", "style.grid-template-columns": "node().layout.columns ?? 1" } }, ngImport: i0, template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n", dependencies: [{ kind: "component", type: i0.forwardRef(() => NodeComponent), selector: "le-node", inputs: ["node", "config"], outputs: ["event"] }] });
593
+ }
594
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: GridComponent, decorators: [{
595
+ type: Component,
596
+ args: [{ selector: 'grid-view', imports: [forwardRef(() => NodeComponent)], host: {
597
+ '[class.le-gird-view]': 'true',
598
+ '[style.grid-template-columns]': 'node().layout.columns ?? 1',
599
+ }, template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n" }]
600
+ }] });
601
+
602
+ class TableDataSource extends DataSource {
603
+ data;
604
+ get count() {
605
+ return this.data.value.length;
606
+ }
607
+ constructor(data = []) {
608
+ super();
609
+ this.data = new BehaviorSubject(data);
610
+ }
611
+ getValueByIndex(index) {
612
+ return this.data.value[index];
613
+ }
614
+ connect() {
615
+ return this.data;
616
+ }
617
+ disconnect() { }
618
+ }
619
+
620
+ class TableViewPaginationComponent {
621
+ pageSize = input.required();
622
+ pageSizeOptions = input.required();
623
+ entriesCount = input.required();
624
+ pageChange = output();
625
+ selectedPageSize = signal(0);
626
+ selectedPageIndex = signal(0);
627
+ entriesRange = computed(() => {
628
+ const selectedPageIndex = this.selectedPageIndex();
629
+ const pageSize = this.pageSize();
630
+ const count = this.entriesCount();
631
+ const rangeStart = selectedPageIndex * pageSize;
632
+ const rangeEnd = rangeStart + pageSize;
633
+ return {
634
+ start: rangeStart,
635
+ end: rangeEnd > count ? count : rangeEnd,
636
+ };
637
+ });
638
+ constructor() {
639
+ this.setEffects();
640
+ }
641
+ onPageSizeChange(option) {
642
+ this.selectedPageSize.set(option);
643
+ }
644
+ onNavigateClick(param) {
645
+ this.selectedPageIndex.update((index) => {
646
+ if (param === 'prev' && this.canNavigatePrev(index)) {
647
+ return index - 1;
648
+ }
649
+ if (param === 'next' && this.canNavigateNext(index)) {
650
+ return index + 1;
651
+ }
652
+ return index;
653
+ });
654
+ }
655
+ canNavigatePrev(index = this.selectedPageIndex()) {
656
+ return index > 0;
657
+ }
658
+ canNavigateNext(index = this.selectedPageIndex()) {
659
+ return index < this.getLastPageIndex();
660
+ }
661
+ setEffects() {
662
+ effect(() => {
663
+ const pageSize = this.pageSize();
664
+ untracked(() => {
665
+ this.selectedPageSize.set(pageSize);
666
+ });
667
+ });
668
+ effect(() => {
669
+ const pageSize = this.selectedPageSize();
670
+ untracked(() => {
671
+ const selectedPageIndex = this.selectedPageIndex();
672
+ const pageIndex = this.getPageIndexOnPageSizeChange();
673
+ if (selectedPageIndex !== pageIndex) {
674
+ this.selectedPageIndex.set(pageIndex);
675
+ }
676
+ else {
677
+ this.pageChange.emit({
678
+ pageSize: pageSize,
679
+ pageIndex: pageIndex,
680
+ });
681
+ }
682
+ });
683
+ });
684
+ effect(() => {
685
+ const index = this.selectedPageIndex();
686
+ untracked(() => {
687
+ this.pageChange.emit({
688
+ pageSize: this.selectedPageSize(),
689
+ pageIndex: index,
690
+ });
691
+ });
692
+ });
693
+ }
694
+ /**
695
+ * @return page index or last available page index for selected page size
696
+ */
697
+ getPageIndexOnPageSizeChange() {
698
+ const selectedPageIndex = this.selectedPageIndex();
699
+ const lasPageIndex = this.getLastPageIndex();
700
+ return selectedPageIndex > lasPageIndex ? lasPageIndex : selectedPageIndex;
701
+ }
702
+ /**
703
+ * @return last available page index
704
+ */
705
+ getLastPageIndex() {
706
+ const count = this.entriesCount();
707
+ if (count === 0)
708
+ return 0;
709
+ return Math.ceil(count / this.selectedPageSize()) - 1;
710
+ }
711
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
712
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: TableViewPaginationComponent, isStandalone: true, selector: "table-view-pagination", inputs: { pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: true, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: true, transformFunction: null }, entriesCount: { classPropertyName: "entriesCount", publicName: "entriesCount", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: "<div class=\"le-pagination\">\n <div class=\"le-pagination-options\">\n <span>Items per page:</span>\n\n <button [cdkMenuTriggerFor]=\"optionsMenu\" class=\"le-button\">\n {{ selectedPageSize() }}\n\n <le-icon name=\"arrow-down\"></le-icon>\n </button>\n\n <ng-template #optionsMenu>\n <div class=\"le-pagination-option-list\" cdkMenu>\n @for (option of pageSizeOptions(); track option) {\n <button cdkMenuItem class=\"le-pagination-option-item\" (click)=\"onPageSizeChange(option)\">\n {{ option }}\n </button>\n }\n </div>\n </ng-template>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"le-pagination-range\">\n {{ entriesRange().start }} - {{ entriesRange().end }} of {{ entriesCount() }}\n </div>\n\n <div class=\"le-pagination-navigation\">\n <button\n class=\"le-navigation le-navigation-prev\"\n [tabindex]=\"canNavigatePrev() ? 0 : -1\"\n [disabled]=\"!canNavigatePrev()\"\n (click)=\"onNavigateClick('prev')\"\n >\n <le-icon class=\"le-chevron le-chevron--left\" name=\"chevron\"></le-icon>\n </button>\n\n <button\n class=\"le-navigation le-navigation-next\"\n [tabindex]=\"canNavigateNext() ? 0 : -1\"\n [disabled]=\"!canNavigateNext()\"\n (click)=\"onNavigateClick('next')\"\n >\n <le-icon class=\"le-chevron le-chevron--right\" name=\"chevron\"></le-icon>\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: CdkMenuTrigger, selector: "[cdkMenuTriggerFor]", inputs: ["cdkMenuTriggerFor", "cdkMenuPosition", "cdkMenuTriggerData"], outputs: ["cdkMenuOpened", "cdkMenuClosed"], exportAs: ["cdkMenuTriggerFor"] }, { kind: "directive", type: CdkMenu, selector: "[cdkMenu]", outputs: ["closed"], exportAs: ["cdkMenu"] }, { kind: "directive", type: CdkMenuItem, selector: "[cdkMenuItem]", inputs: ["cdkMenuItemDisabled", "cdkMenuitemTypeaheadLabel"], outputs: ["cdkMenuItemTriggered"], exportAs: ["cdkMenuItem"] }, { kind: "ngmodule", type: A11yModule }, { kind: "component", type: LeIconComponent, selector: "le-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
713
+ }
714
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewPaginationComponent, decorators: [{
715
+ type: Component,
716
+ args: [{ selector: 'table-view-pagination', imports: [CdkMenuTrigger, CdkMenu, CdkMenuItem, A11yModule, LeIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"le-pagination\">\n <div class=\"le-pagination-options\">\n <span>Items per page:</span>\n\n <button [cdkMenuTriggerFor]=\"optionsMenu\" class=\"le-button\">\n {{ selectedPageSize() }}\n\n <le-icon name=\"arrow-down\"></le-icon>\n </button>\n\n <ng-template #optionsMenu>\n <div class=\"le-pagination-option-list\" cdkMenu>\n @for (option of pageSizeOptions(); track option) {\n <button cdkMenuItem class=\"le-pagination-option-item\" (click)=\"onPageSizeChange(option)\">\n {{ option }}\n </button>\n }\n </div>\n </ng-template>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"le-pagination-range\">\n {{ entriesRange().start }} - {{ entriesRange().end }} of {{ entriesCount() }}\n </div>\n\n <div class=\"le-pagination-navigation\">\n <button\n class=\"le-navigation le-navigation-prev\"\n [tabindex]=\"canNavigatePrev() ? 0 : -1\"\n [disabled]=\"!canNavigatePrev()\"\n (click)=\"onNavigateClick('prev')\"\n >\n <le-icon class=\"le-chevron le-chevron--left\" name=\"chevron\"></le-icon>\n </button>\n\n <button\n class=\"le-navigation le-navigation-next\"\n [tabindex]=\"canNavigateNext() ? 0 : -1\"\n [disabled]=\"!canNavigateNext()\"\n (click)=\"onNavigateClick('next')\"\n >\n <le-icon class=\"le-chevron le-chevron--right\" name=\"chevron\"></le-icon>\n </button>\n </div>\n</div>\n" }]
717
+ }], ctorParameters: () => [] });
718
+
719
+ const isPlaceholder$1 = (value) => {
720
+ return value.startsWith('{') && value.endsWith('}');
721
+ };
722
+ const mapSortStringToSort = (sort) => {
723
+ return sort.startsWith('-') ? [sort.slice(1), 'desc'] : [sort, 'desc'];
724
+ };
725
+ const mapSortStringListToSortMap = (sortList) => {
726
+ return sortList.reduce((acc, sort) => {
727
+ const { 0: key, 1: value } = mapSortStringToSort(sort);
728
+ acc.set(key, value);
729
+ return acc;
730
+ }, new Map());
731
+ };
732
+ const mapSortMapToSortStringList = (sortMap) => {
733
+ return [...sortMap].reduce((acc, sort) => {
734
+ const prefix = sort[1] === 'asc' ? '' : '-';
735
+ acc.push(`${prefix}${sort[0]}`);
736
+ return acc;
737
+ }, []);
738
+ };
739
+ const mapFiltersToFilterString = (filters) => {
740
+ return Object.entries(filters)
741
+ .reduce((acc, filter) => {
742
+ return acc.concat(`filter[${filter[0]}]=${filter[1]}`);
743
+ }, [])
744
+ .join('&');
745
+ };
746
+ const buildRequestContext = (params, ctx) => {
747
+ const requestContext = {};
748
+ if (ctx.hasPagination && params.pageIndex !== undefined) {
749
+ requestContext['pageIndex'] = params.pageIndex;
750
+ requestContext['pageSize'] = params.pageSize;
751
+ }
752
+ if (ctx.isSortable && params.sort) {
753
+ requestContext['sort'] = params.sort;
754
+ }
755
+ if (params.filters && Object.keys(params.filters).length > 0) {
756
+ requestContext['filters'] = params.filters;
757
+ }
758
+ return requestContext;
759
+ };
760
+ const buildRequestBody = (params) => {
761
+ const body = {};
762
+ if (params.sort?.length) {
763
+ body['ordering'] = params.sort;
764
+ }
765
+ if (params.filters && Object.keys(params.filters).length > 0) {
766
+ body['filters'] = params.filters;
767
+ }
768
+ if (params.pageIndex !== undefined) {
769
+ body['page'] = params.pageIndex + 1;
770
+ }
771
+ if (params.pageSize) {
772
+ body['page_size'] = params.pageSize;
773
+ }
774
+ return body;
775
+ };
776
+ const buildQueryString = (params) => {
777
+ const query = [];
778
+ if (params.pageIndex !== undefined) {
779
+ query.push(`page=${params.pageIndex + 1}`);
780
+ }
781
+ if (params.pageSize) {
782
+ query.push(`page_size=${params.pageSize}`);
783
+ }
784
+ if (params.sort?.length) {
785
+ query.push(`ordering=${params.sort.join(',')}`);
786
+ }
787
+ if (params.filters && Object.keys(params.filters).length > 0) {
788
+ query.push(mapFiltersToFilterString(params.filters));
789
+ }
790
+ return query.filter((item) => !!item).join('&');
791
+ };
792
+
793
+ class LoadingViewComponent {
794
+ headerHeight = input(0);
795
+ rowHeight = input(40);
796
+ rowCount = input(1);
797
+ rows = computed(() => {
798
+ return new Array(this.rowCount()).fill(null).map((_, i) => i);
799
+ });
800
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LoadingViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
801
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: LoadingViewComponent, isStandalone: true, selector: "app-loading-view", inputs: { headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, rowCount: { classPropertyName: "rowCount", publicName: "rowCount", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"preloader\">\n @if (headerHeight()) {\n <div class=\"preloader-row\" [style.height.px]=\"headerHeight()\"></div>\n }\n\n @for (row of rows(); track row) {\n <div class=\"preloader-row\" [style.height.px]=\"rowHeight()\"></div>\n }\n</div>\n", styles: [".preloader{display:flex;flex-direction:column;width:100%;gap:8px}.preloader-row{background:#f5f5f5 no-repeat;animation:pulse 1.5s cubic-bezier(.4,0,.2,1) infinite;animation-delay:.5s}@keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}\n"] });
802
+ }
803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LoadingViewComponent, decorators: [{
804
+ type: Component,
805
+ args: [{ selector: 'app-loading-view', template: "<div class=\"preloader\">\n @if (headerHeight()) {\n <div class=\"preloader-row\" [style.height.px]=\"headerHeight()\"></div>\n }\n\n @for (row of rows(); track row) {\n <div class=\"preloader-row\" [style.height.px]=\"rowHeight()\"></div>\n }\n</div>\n", styles: [".preloader{display:flex;flex-direction:column;width:100%;gap:8px}.preloader-row{background:#f5f5f5 no-repeat;animation:pulse 1.5s cubic-bezier(.4,0,.2,1) infinite;animation-delay:.5s}@keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}\n"] }]
806
+ }] });
807
+
808
+ class TableViewActions {
809
+ actions = input();
810
+ actionClicked = output();
811
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
812
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: TableViewActions, isStandalone: true, selector: "table-view-actions", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionClicked: "actionClicked" }, host: { properties: { "class.le-table-view-actions": "true" } }, ngImport: i0, template: `
813
+ @for (action of actions(); track action) {
814
+ <button
815
+ class="le-button le-button-flat le-button--success"
816
+ (click)="actionClicked.emit(action)"
817
+ >
818
+ {{ action.label }}
819
+ </button>
820
+ }
821
+ `, isInline: true });
822
+ }
823
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewActions, decorators: [{
824
+ type: Component,
825
+ args: [{
826
+ selector: 'table-view-actions',
827
+ template: `
828
+ @for (action of actions(); track action) {
829
+ <button
830
+ class="le-button le-button-flat le-button--success"
831
+ (click)="actionClicked.emit(action)"
832
+ >
833
+ {{ action.label }}
834
+ </button>
835
+ }
836
+ `,
837
+ host: {
838
+ '[class.le-table-view-actions]': 'true',
839
+ },
840
+ }]
841
+ }] });
842
+
843
+ class StringTemplate {
844
+ data = input.required();
845
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: StringTemplate, deps: [], target: i0.ɵɵFactoryTarget.Component });
846
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.21", type: StringTemplate, isStandalone: true, selector: "ng-component", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `<span class="le-table-template le-template-string">{{ data() ?? '–' }}</span>`, isInline: true });
847
+ }
848
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: StringTemplate, decorators: [{
849
+ type: Component,
850
+ args: [{
851
+ template: `<span class="le-table-template le-template-string">{{ data() ?? '–' }}</span>`,
852
+ }]
853
+ }] });
854
+
855
+ class ReferenceTemplate {
856
+ data = input.required();
857
+ attribute = input.required();
858
+ metadata = input.required();
859
+ clicked = output();
860
+ reference = computed(() => {
861
+ const ref = this.data()?.ref;
862
+ return {
863
+ text: this.generateText(ref),
864
+ href: this.generateLink(ref),
865
+ };
866
+ });
867
+ onClick(event) {
868
+ event.preventDefault();
869
+ event.stopImmediatePropagation();
870
+ this.clicked.emit(this.data());
871
+ }
872
+ generateText(ref) {
873
+ if (ref) {
874
+ return ref.display_name || ref.object_id;
875
+ }
876
+ return '';
877
+ }
878
+ generateLink(ref) {
879
+ if (ref) {
880
+ // statedb%23Tag%3ALATEST%3Atag_01%3ALATEST
881
+ const href = `/details/object/${ref.class_name}/${ref.resource}%2523${ref.class_name}%253A${ref.class_version}%253A${ref.object_id}%253A${ref.object_version}`;
882
+ return ref?.address ? `${href}?address=${ref.address}` : href;
883
+ }
884
+ }
885
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ReferenceTemplate, deps: [], target: i0.ɵɵFactoryTarget.Component });
886
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ReferenceTemplate, isStandalone: true, selector: "ng-component", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, attribute: { classPropertyName: "attribute", publicName: "attribute", isSignal: true, isRequired: true, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: `
887
+ <span class="le-table-template le-template-reference">
888
+ @if (reference(); as reference) {
889
+ @if (reference.href) {
890
+ <a
891
+ [href]="reference.href"
892
+ [attr.aria-label]="reference.text"
893
+ (click)="onClick($event)"
894
+ target="_blank"
895
+ >{{ reference.text }}</a
896
+ >
897
+ } @else {
898
+ {{ reference.text }}
899
+ }
900
+ }
901
+ </span>
902
+ `, isInline: true });
903
+ }
904
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ReferenceTemplate, decorators: [{
905
+ type: Component,
906
+ args: [{
907
+ template: `
908
+ <span class="le-table-template le-template-reference">
909
+ @if (reference(); as reference) {
910
+ @if (reference.href) {
911
+ <a
912
+ [href]="reference.href"
913
+ [attr.aria-label]="reference.text"
914
+ (click)="onClick($event)"
915
+ target="_blank"
916
+ >{{ reference.text }}</a
917
+ >
918
+ } @else {
919
+ {{ reference.text }}
920
+ }
921
+ }
922
+ </span>
923
+ `,
924
+ }]
925
+ }] });
926
+
927
+ class JSONTemplate {
928
+ data = input.required();
929
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: JSONTemplate, deps: [], target: i0.ɵɵFactoryTarget.Component });
930
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.21", type: JSONTemplate, isStandalone: true, selector: "ng-component", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `<pre class="le-table-template le-template-json">{{
931
+ data() ? (data() | json) : '–'
932
+ }}</pre>`, isInline: true, dependencies: [{ kind: "pipe", type: JsonPipe, name: "json" }] });
933
+ }
934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: JSONTemplate, decorators: [{
935
+ type: Component,
936
+ args: [{
937
+ template: `<pre class="le-table-template le-template-json">{{
938
+ data() ? (data() | json) : '–'
939
+ }}</pre>`,
940
+ imports: [JsonPipe],
941
+ }]
942
+ }] });
943
+
944
+ const TABLE_SELL_TEMPLATE_NAMES = {
945
+ StringTemplate: 'StringTemplate',
946
+ ReferenceTemplate: 'ReferenceTemplate',
947
+ JsonTemplate: 'JsonTemplate',
948
+ };
949
+ const TABLE_CELL_TEMPLATES = {
950
+ StringTemplate: StringTemplate,
951
+ ReferenceTemplate: ReferenceTemplate,
952
+ JsonTemplate: JSONTemplate,
953
+ };
954
+
955
+ class TableViewCellDirective {
956
+ vcr = inject(ViewContainerRef);
957
+ componentRef;
958
+ type = input();
959
+ data = input.required();
960
+ column = input();
961
+ metadata = input();
962
+ clicked = output();
963
+ constructor() {
964
+ this.setEffects();
965
+ }
966
+ setEffects() {
967
+ effect(() => {
968
+ const type = this.type();
969
+ const data = this.data();
970
+ const column = this.column();
971
+ const metadata = this.metadata();
972
+ untracked(() => {
973
+ const component = (type && TABLE_CELL_TEMPLATES[type]) || TABLE_CELL_TEMPLATES['StringTemplate'];
974
+ if (this.componentRef) {
975
+ this.componentRef.destroy();
976
+ }
977
+ this.componentRef = this.vcr.createComponent(component);
978
+ this.componentRef?.setInput('data', data);
979
+ if (type === 'ReferenceTemplate') {
980
+ if (column) {
981
+ this.componentRef?.setInput('attribute', column);
982
+ }
983
+ if (metadata) {
984
+ this.componentRef?.setInput('metadata', metadata);
985
+ }
986
+ const sub = (this.componentRef?.instance).clicked.subscribe(data => {
987
+ this.clicked.emit(data);
988
+ });
989
+ this.componentRef?.onDestroy(() => sub.unsubscribe());
990
+ }
991
+ });
992
+ });
993
+ }
994
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
995
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: TableViewCellDirective, isStandalone: true, selector: "[tableViewCell]", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0 });
996
+ }
997
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewCellDirective, decorators: [{
998
+ type: Directive,
999
+ args: [{
1000
+ selector: '[tableViewCell]',
1001
+ }]
1002
+ }], ctorParameters: () => [] });
1003
+
1004
+ const flattenNodes = (nodes) => {
1005
+ const flattenedNodes = [];
1006
+ for (const node of nodes) {
1007
+ flattenedNodes.push(node);
1008
+ if (node.leafs) {
1009
+ flattenedNodes.push(...flattenNodes(node.leafs));
1010
+ }
1011
+ }
1012
+ return flattenedNodes;
1013
+ };
1014
+ const toTree = (obj, label) => {
1015
+ if (obj !== null && typeof obj === 'object') {
1016
+ const entries = Array.isArray(obj)
1017
+ ? obj.map((v, i) => toTree(v, String(i)))
1018
+ : Object.entries(obj).map((item) => toTree(item[1], item[0]));
1019
+ return { label, leafs: entries };
1020
+ }
1021
+ return { label, value: obj };
1022
+ };
1023
+ const mapObjectToTree = (data) => {
1024
+ return Object.entries(data).map((item) => toTree(item[1], item[0]));
1025
+ };
1026
+
1027
+ class TreeViewComponent {
1028
+ treeControl = new NestedTreeControl((node) => node.leafs);
1029
+ data = input();
1030
+ source = computed(() => {
1031
+ return new ArrayDataSource(mapObjectToTree(this.data() ?? {}));
1032
+ });
1033
+ hasChild = (_, node) => !!node.leafs && node.leafs.length > 0;
1034
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1035
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.21", type: TreeViewComponent, isStandalone: true, selector: "le-tree-view", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<cdk-tree [dataSource]=\"source()\" [treeControl]=\"treeControl\">\n <!-- Tree node template for leaf nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node\" class=\"le-tree-node\">\n <div class=\"le-tree-row\">\n <b>{{node.label | uppercase}}</b>: &nbsp;&nbsp;&nbsp; {{node.value}}\n </div>\n </cdk-nested-tree-node>\n\n <!-- Tree node template for expandable nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node; when: hasChild\" class=\"le-tree-node\">\n\n <a class=\"le-tree-row\" [class.le-tree-row--expanded]=\"treeControl.isExpanded(node)\" [attr.aria-label]=\"'Toggle ' + node.label\" cdkTreeNodeToggle>\n <b>{{node.label | uppercase}}</b>:\n\n <le-icon class=\"le-tree-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div [class.le-tree-invisible]=\"!treeControl.isExpanded(node)\">\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n</cdk-tree>\n", dependencies: [{ kind: "component", type: CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "trackBy"], exportAs: ["cdkTree"] }, { kind: "directive", type: CdkNestedTreeNode, selector: "cdk-nested-tree-node", exportAs: ["cdkNestedTreeNode"] }, { kind: "directive", type: CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }, { kind: "component", type: LeIconComponent, selector: "le-icon", inputs: ["name", "size"] }, { kind: "directive", type: CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: ["cdkTreeNodeToggleRecursive"] }, { kind: "pipe", type: UpperCasePipe, name: "uppercase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1036
+ }
1037
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TreeViewComponent, decorators: [{
1038
+ type: Component,
1039
+ args: [{ selector: 'le-tree-view', imports: [
1040
+ CdkTree,
1041
+ CdkNestedTreeNode,
1042
+ CdkTreeNodeDef,
1043
+ CdkTreeNodeOutlet,
1044
+ LeIconComponent,
1045
+ CdkTreeNodeToggle,
1046
+ UpperCasePipe,
1047
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<cdk-tree [dataSource]=\"source()\" [treeControl]=\"treeControl\">\n <!-- Tree node template for leaf nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node\" class=\"le-tree-node\">\n <div class=\"le-tree-row\">\n <b>{{node.label | uppercase}}</b>: &nbsp;&nbsp;&nbsp; {{node.value}}\n </div>\n </cdk-nested-tree-node>\n\n <!-- Tree node template for expandable nodes -->\n <cdk-nested-tree-node *cdkTreeNodeDef=\"let node; when: hasChild\" class=\"le-tree-node\">\n\n <a class=\"le-tree-row\" [class.le-tree-row--expanded]=\"treeControl.isExpanded(node)\" [attr.aria-label]=\"'Toggle ' + node.label\" cdkTreeNodeToggle>\n <b>{{node.label | uppercase}}</b>:\n\n <le-icon class=\"le-tree-chevron\" name=\"chevron\"></le-icon>\n </a>\n\n <div [class.le-tree-invisible]=\"!treeControl.isExpanded(node)\">\n <ng-container cdkTreeNodeOutlet></ng-container>\n </div>\n </cdk-nested-tree-node>\n</cdk-tree>\n" }]
1048
+ }] });
1049
+
1050
+ class BlockViewComponent {
1051
+ data = input.required();
1052
+ columns = input.required();
1053
+ event = output();
1054
+ onCellClicked(data) {
1055
+ this.event.emit({ type: 'cell_clicked', data });
1056
+ this.event.emit({ type: 'close' });
1057
+ }
1058
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: BlockViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1059
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: BlockViewComponent, isStandalone: true, selector: "le-block-view", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "<div class=\"block-view\">\n @for (column of columns(); track column) {\n <div class=\"block-view-item\">\n <div class=\"block-view-label\">\n {{column.label}}\n </div>\n\n <div class=\"block-view-value\">\n @if (data(); as row) {\n <ng-template\n tableViewCell\n [data]=\"row[column.key]\"\n [type]=\"column.column_format?.cellTemplate\"\n [column]=\"column\"\n [metadata]=\"row._metadata\"\n (clicked)=\"onCellClicked($event)\"\n ></ng-template>\n }\n </div>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: TableViewCellDirective, selector: "[tableViewCell]", inputs: ["type", "data", "column", "metadata"], outputs: ["clicked"] }] });
1060
+ }
1061
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: BlockViewComponent, decorators: [{
1062
+ type: Component,
1063
+ args: [{ selector: 'le-block-view', imports: [TableViewCellDirective], template: "<div class=\"block-view\">\n @for (column of columns(); track column) {\n <div class=\"block-view-item\">\n <div class=\"block-view-label\">\n {{column.label}}\n </div>\n\n <div class=\"block-view-value\">\n @if (data(); as row) {\n <ng-template\n tableViewCell\n [data]=\"row[column.key]\"\n [type]=\"column.column_format?.cellTemplate\"\n [column]=\"column\"\n [metadata]=\"row._metadata\"\n (clicked)=\"onCellClicked($event)\"\n ></ng-template>\n }\n </div>\n </div>\n }\n</div>\n" }]
1064
+ }] });
1065
+
1066
+ class SidePanelComponent {
1067
+ dataService = inject(LEDataService);
1068
+ _selectedTab = signal(0);
1069
+ data = input.required();
1070
+ config = input();
1071
+ event = output();
1072
+ node = this.dataService.view;
1073
+ tabs = computed(() => {
1074
+ const data = this.data();
1075
+ return data.tabs.length ? data.tabs : undefined;
1076
+ });
1077
+ selectedTabIndex = this._selectedTab.asReadonly();
1078
+ selectedTab = computed(() => {
1079
+ return this.tabs()?.[this.selectedTabIndex()];
1080
+ });
1081
+ constructor() {
1082
+ effect(() => {
1083
+ const data = this.data();
1084
+ untracked(() => {
1085
+ if (data.node) {
1086
+ this.dataService.setView(data.node, true);
1087
+ }
1088
+ });
1089
+ });
1090
+ }
1091
+ onEvent(event) {
1092
+ this.event.emit(event);
1093
+ }
1094
+ onTabClick(index) {
1095
+ this._selectedTab.set(index);
1096
+ }
1097
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SidePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1098
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SidePanelComponent, isStandalone: true, selector: "ng-component", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, host: { properties: { "style.width": "\"100%\"", "style.overflow": "\"auto\"", "style.padding": "\"0 16px\"" } }, providers: [LEDataService, LEFiltersService], ngImport: i0, template: "@if (node(); as node) {\n <le-node [node]=\"node\" [config]=\"data()?.config\" (event)=\"onEvent($event)\"></le-node>\n}\n\n@if (tabs(); as tabs) {\n <div class=\"le-tabs-nav\">\n @for (tab of tabs; track tab.type; let idx = $index) {\n <button\n class=\"le-button le-button-flat le-button--success\"\n [attr.aria-label]=\"'Select ' + tab.type\"\n [class.le-button--success]=\"selectedTabIndex() === idx\"\n (click)=\"onTabClick(idx)\"\n >\n {{ tab.label }}\n </button>\n }\n <span class=\"spacer\"></span>\n\n <button\n aria-label=\"Close side panel\"\n class=\"le-button le-button-flat\"\n (click)=\"event.emit({ type: 'close' })\"\n >Close</button>\n </div>\n}\n\n@if (selectedTab(); as selectedTab) {\n <div class=\"le-tabs-content\">\n @switch (selectedTab.type) {\n @case ('tree') {\n <le-tree-view [data]=\"selectedTab.data\"></le-tree-view>\n }\n @case ('table') {\n <le-node\n [node]=\"selectedTab.data\"\n [config]=\"data()?.config\"\n (event)=\"onEvent($event)\"\n ></le-node>\n }\n @case ('block') {\n <le-block-view\n [data]=\"selectedTab.data\"\n [columns]=\"selectedTab.columns\"\n (event)=\"onEvent($event)\"\n ></le-block-view>\n }\n }\n </div>\n}\n", dependencies: [{ kind: "component", type: i0.forwardRef(() => NodeComponent), selector: "le-node", inputs: ["node", "config"], outputs: ["event"] }, { kind: "component", type: i0.forwardRef(() => TreeViewComponent), selector: "le-tree-view", inputs: ["data"] }, { kind: "component", type: i0.forwardRef(() => BlockViewComponent), selector: "le-block-view", inputs: ["data", "columns"], outputs: ["event"] }] });
1099
+ }
1100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SidePanelComponent, decorators: [{
1101
+ type: Component,
1102
+ args: [{ host: {
1103
+ '[style.width]': '"100%"',
1104
+ '[style.overflow]': '"auto"',
1105
+ '[style.padding]': '"0 16px"',
1106
+ }, imports: [forwardRef(() => NodeComponent), TreeViewComponent, BlockViewComponent], providers: [LEDataService, LEFiltersService], template: "@if (node(); as node) {\n <le-node [node]=\"node\" [config]=\"data()?.config\" (event)=\"onEvent($event)\"></le-node>\n}\n\n@if (tabs(); as tabs) {\n <div class=\"le-tabs-nav\">\n @for (tab of tabs; track tab.type; let idx = $index) {\n <button\n class=\"le-button le-button-flat le-button--success\"\n [attr.aria-label]=\"'Select ' + tab.type\"\n [class.le-button--success]=\"selectedTabIndex() === idx\"\n (click)=\"onTabClick(idx)\"\n >\n {{ tab.label }}\n </button>\n }\n <span class=\"spacer\"></span>\n\n <button\n aria-label=\"Close side panel\"\n class=\"le-button le-button-flat\"\n (click)=\"event.emit({ type: 'close' })\"\n >Close</button>\n </div>\n}\n\n@if (selectedTab(); as selectedTab) {\n <div class=\"le-tabs-content\">\n @switch (selectedTab.type) {\n @case ('tree') {\n <le-tree-view [data]=\"selectedTab.data\"></le-tree-view>\n }\n @case ('table') {\n <le-node\n [node]=\"selectedTab.data\"\n [config]=\"data()?.config\"\n (event)=\"onEvent($event)\"\n ></le-node>\n }\n @case ('block') {\n <le-block-view\n [data]=\"selectedTab.data\"\n [columns]=\"selectedTab.columns\"\n (event)=\"onEvent($event)\"\n ></le-block-view>\n }\n }\n </div>\n}\n" }]
1107
+ }], ctorParameters: () => [] });
1108
+
1109
+ const positionMap = {
1110
+ right: (position) => position.right().top('0'),
1111
+ left: (position) => position.left().top('0'),
1112
+ top: (position) => position.top('0'),
1113
+ bottom: (position) => position.bottom('0'),
1114
+ };
1115
+ class LeSidePanelService {
1116
+ overlay = inject(Overlay);
1117
+ breakpointObserver = inject(BreakpointObserver);
1118
+ overlayRef;
1119
+ output = new Subject();
1120
+ show(data, config) {
1121
+ if (this.overlayRef)
1122
+ this.hide();
1123
+ const destroy$ = new Subject();
1124
+ this.overlayRef = this.overlay.create({
1125
+ width: this.getWidth(config),
1126
+ height: this.getHeight(config),
1127
+ panelClass: 'le-side-panel',
1128
+ hasBackdrop: config.hasBackdrop ?? true,
1129
+ positionStrategy: this.getPositionStrategy(config),
1130
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
1131
+ });
1132
+ this.overlayRef.addPanelClass(this.getClosedClass(config));
1133
+ this.overlayRef?.outsidePointerEvents().subscribe({
1134
+ next: () => {
1135
+ this.overlayRef?.addPanelClass(this.getClosedClass(config));
1136
+ this.delayed(() => this.hide());
1137
+ },
1138
+ });
1139
+ const componentRef = this.overlayRef.attach(new ComponentPortal(SidePanelComponent, null, config.injector));
1140
+ componentRef.setInput('data', data);
1141
+ this.delayed(() => this.overlayRef?.removePanelClass(this.getClosedClass(config)));
1142
+ const sub = componentRef.instance.event.subscribe((event) => {
1143
+ this.output.next(event);
1144
+ if (event.type === 'close') {
1145
+ this.overlayRef?.addPanelClass(this.getClosedClass(config));
1146
+ this.delayed(() => this.hide());
1147
+ }
1148
+ });
1149
+ componentRef.onDestroy(() => {
1150
+ sub.unsubscribe();
1151
+ destroy$.next();
1152
+ destroy$.complete();
1153
+ });
1154
+ return this.output.pipe(takeUntil(destroy$));
1155
+ }
1156
+ hide() {
1157
+ this.overlayRef?.dispose();
1158
+ this.overlayRef = undefined;
1159
+ }
1160
+ delayed(fn) {
1161
+ setTimeout(() => fn(), 300);
1162
+ }
1163
+ getHeight(config) {
1164
+ if (config.height) {
1165
+ return config.height;
1166
+ }
1167
+ if (config.position === 'top' || config.position === 'bottom') {
1168
+ if (this.breakpointObserver.isMatched(Breakpoints.XSmall)) {
1169
+ return '80vh';
1170
+ }
1171
+ else if (this.breakpointObserver.isMatched(Breakpoints.Small)) {
1172
+ return '60vh';
1173
+ }
1174
+ else {
1175
+ return '40vh';
1176
+ }
1177
+ }
1178
+ return '100vh';
1179
+ }
1180
+ getWidth(config) {
1181
+ if (config.width) {
1182
+ return config.width;
1183
+ }
1184
+ if (config.position === 'left' || config.position === 'right') {
1185
+ if (this.breakpointObserver.isMatched(Breakpoints.XSmall)) {
1186
+ return '80vw';
1187
+ }
1188
+ else if (this.breakpointObserver.isMatched(Breakpoints.Small)) {
1189
+ return '60vw';
1190
+ }
1191
+ else {
1192
+ return '40vw';
1193
+ }
1194
+ }
1195
+ else {
1196
+ return '100vw';
1197
+ }
1198
+ }
1199
+ getClosedClass(config) {
1200
+ return `le-side-panel--${config.position}`;
1201
+ }
1202
+ getPositionStrategy({ position }) {
1203
+ let positionStrategy = this.overlay.position().global();
1204
+ if (positionMap[position]) {
1205
+ return positionMap[position](positionStrategy);
1206
+ }
1207
+ return positionStrategy;
1208
+ }
1209
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeSidePanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1210
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeSidePanelService, providedIn: 'root' });
1211
+ }
1212
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeSidePanelService, decorators: [{
1213
+ type: Injectable,
1214
+ args: [{
1215
+ providedIn: 'root',
1216
+ }]
1217
+ }] });
1218
+
1219
+ class TableViewComponent {
1220
+ apiService = inject(LEApiService);
1221
+ dataService = inject(LEDataService);
1222
+ filtersService = inject(LEFiltersService);
1223
+ sidePanelService = inject(LeSidePanelService);
1224
+ injector = inject(Injector);
1225
+ node = input.required();
1226
+ config = input();
1227
+ event = output();
1228
+ _columns = signal([]);
1229
+ _data = signal(new TableDataSource());
1230
+ _total = signal(0);
1231
+ _isLoading = signal(false);
1232
+ _pageIndex = signal(0);
1233
+ _pageSize = signal(0);
1234
+ _pageSizeOptions = signal([]);
1235
+ _sortConfig = signal(null);
1236
+ _sortMap = signal(new Map());
1237
+ _sortOrder = signal(new Map());
1238
+ _cancelRequest$ = new Subject();
1239
+ columns = this._columns.asReadonly();
1240
+ data = this._data.asReadonly();
1241
+ displayedColumns = computed(() => {
1242
+ const result = [];
1243
+ const firstRow = this.data().getValueByIndex(0);
1244
+ const columnKeys = this.columns().map((column) => column.key);
1245
+ const hasVersion = !!(firstRow && firstRow._metadata && firstRow._metadata.object_version);
1246
+ if (!this.config()?.hideInfo) {
1247
+ result.push('info');
1248
+ }
1249
+ result.push(...columnKeys);
1250
+ if (hasVersion) {
1251
+ result.push('modified');
1252
+ }
1253
+ return result;
1254
+ });
1255
+ isLoading = this._isLoading.asReadonly();
1256
+ pageIndex = this._pageIndex.asReadonly();
1257
+ pageSize = this._pageSize.asReadonly();
1258
+ pageSizeOptions = this._pageSizeOptions.asReadonly();
1259
+ entriesCount = this._total.asReadonly();
1260
+ sortMap = this._sortMap.asReadonly();
1261
+ sortOrder = this._sortOrder.asReadonly();
1262
+ isSortingEnabled = computed(() => {
1263
+ return !!this._sortConfig()?.fields.length;
1264
+ });
1265
+ sortColumnKeys = computed(() => {
1266
+ return new Set(this._sortConfig()?.fields ?? []);
1267
+ });
1268
+ hasRowClickAction = computed(() => {
1269
+ return !!this.node()?.actions?.onRowClick?.length;
1270
+ });
1271
+ filters = this.filtersService.options;
1272
+ constructor() {
1273
+ this.setEffects();
1274
+ }
1275
+ ngOnDestroy() {
1276
+ this._cancelRequest$.next();
1277
+ this._cancelRequest$.complete();
1278
+ }
1279
+ onPageChange(pageChange) {
1280
+ this._pageSize.set(pageChange.pageSize);
1281
+ this._pageIndex.set(pageChange.pageIndex);
1282
+ }
1283
+ onSortClick(event, column) {
1284
+ event.stopPropagation();
1285
+ this._sortMap.update((sortMap) => {
1286
+ const direction = sortMap.get(column.key);
1287
+ if (direction === 'desc') {
1288
+ sortMap.delete(column.key);
1289
+ }
1290
+ else if (direction === 'asc') {
1291
+ sortMap.set(column.key, 'desc');
1292
+ }
1293
+ else {
1294
+ sortMap.set(column.key, 'asc');
1295
+ }
1296
+ return new Map(sortMap);
1297
+ });
1298
+ }
1299
+ onFiltersClick(event) {
1300
+ event.stopPropagation();
1301
+ this.filtersService.open();
1302
+ }
1303
+ onToolbarActionClick({ actions }) {
1304
+ actions.forEach((action) => this.processTableAction(action));
1305
+ }
1306
+ onRowClick(row) {
1307
+ const actions = this.node().actions?.onRowClick ?? [];
1308
+ actions.forEach((action) => this.processRowClickAction(action, row));
1309
+ }
1310
+ onRowInfoClick(event, row) {
1311
+ event.preventDefault();
1312
+ event.stopImmediatePropagation();
1313
+ const node = {
1314
+ id: row._metadata.lakehouse_address,
1315
+ type: 'table',
1316
+ dataSource: {
1317
+ kind: 'object',
1318
+ entity: row._metadata.lakehouse_address,
1319
+ params: {
1320
+ includeVersions: true,
1321
+ includeMeta: true,
1322
+ loadReferences: false,
1323
+ fileOptimized: true,
1324
+ },
1325
+ },
1326
+ };
1327
+ this.sidePanelService
1328
+ .show({
1329
+ tabs: [
1330
+ { type: 'block', label: 'Object', data: row, columns: this._columns() },
1331
+ { type: 'tree', label: 'Metadata', data: row._metadata },
1332
+ { type: 'table', label: 'Versions', data: node },
1333
+ ],
1334
+ config: {
1335
+ hasCloseButton: true,
1336
+ skipLocationChange: true,
1337
+ hideFilters: true,
1338
+ hideInfo: true,
1339
+ },
1340
+ }, {
1341
+ position: 'bottom',
1342
+ injector: this.injector,
1343
+ })
1344
+ .subscribe({
1345
+ next: event => {
1346
+ if (event.type === 'cell_clicked') {
1347
+ this.onCellClicked(event.data);
1348
+ }
1349
+ }
1350
+ });
1351
+ }
1352
+ onCellClicked(data) {
1353
+ const path = `${data.ref.resource}%23${data.ref.class_name}%3A${data.ref.class_version}%3A${data.ref.object_id}%3A${data.ref.object_version}`;
1354
+ this.setView({
1355
+ id: path,
1356
+ type: 'form',
1357
+ controlSource: {
1358
+ kind: 'object',
1359
+ entity: path,
1360
+ },
1361
+ });
1362
+ }
1363
+ processRowClickAction(action, row) {
1364
+ const { type, node } = action;
1365
+ const clone = JSON.parse(JSON.stringify(node));
1366
+ if (type === 'open_sidebar' && 'controlSource' in clone) {
1367
+ if (clone.controlSource?.params) {
1368
+ clone.controlSource.params = this.interpolateRowValues(clone.controlSource.params, row);
1369
+ }
1370
+ this.sidePanelService
1371
+ .show({
1372
+ node: clone,
1373
+ tabs: [],
1374
+ config: { hasCloseButton: true, skipLocationChange: true },
1375
+ }, {
1376
+ position: 'right',
1377
+ injector: this.injector,
1378
+ })
1379
+ .subscribe((event) => {
1380
+ if (event.type === 'refresh') {
1381
+ this.forceFetch();
1382
+ }
1383
+ });
1384
+ }
1385
+ }
1386
+ processTableAction(action) {
1387
+ if (action.type === 'update_form') {
1388
+ const dataSource = action.dataSource;
1389
+ this.setView({
1390
+ id: action.type,
1391
+ type: 'form',
1392
+ controlSource: {
1393
+ kind: dataSource.kind,
1394
+ entity: dataSource.entity,
1395
+ params: dataSource?.params,
1396
+ },
1397
+ });
1398
+ }
1399
+ }
1400
+ setView(node) {
1401
+ this.dataService.setView(node, !!this.config()?.skipLocationChange);
1402
+ }
1403
+ setPagination(pagination) {
1404
+ this._pageSize.set(pagination?.pageSize ?? 0);
1405
+ this._pageSizeOptions.set(pagination?.options ?? []);
1406
+ }
1407
+ getTableData(node, ctx) {
1408
+ this._isLoading.set(true);
1409
+ this._cancelRequest$.next();
1410
+ this.buildRequest(node, ctx)
1411
+ .pipe(takeUntil(this._cancelRequest$))
1412
+ .subscribe({
1413
+ next: (response) => {
1414
+ this.handleTableDataSuccessResponse(response);
1415
+ },
1416
+ error: (error) => {
1417
+ this.handleTableDataErrorResponse(error);
1418
+ },
1419
+ });
1420
+ }
1421
+ buildRequest(node, ctx) {
1422
+ if (node.dataSource?.kind === 'class') {
1423
+ const query = buildQueryString(ctx);
1424
+ return this.apiService.getObject(node.dataSource.entity, query);
1425
+ }
1426
+ if (node.dataSource?.kind === 'transaction') {
1427
+ const body = Object.assign({}, node.dataSource.params, buildRequestBody(ctx));
1428
+ this.apiService.executeTransaction(node.dataSource.entity, body);
1429
+ }
1430
+ if (node.dataSource?.kind === 'object') {
1431
+ return this.apiService.getObjectByAddress(node.dataSource.entity, node.dataSource.params ?? {});
1432
+ }
1433
+ throw new Error(`Datasource type should be class or transaction. Received: ${node.dataSource?.kind}`);
1434
+ }
1435
+ handleTableDataSuccessResponse(response) {
1436
+ this._columns.set(response.columns);
1437
+ this._data.set(new TableDataSource(response.rows));
1438
+ this._total.set(response.total ?? response.rows.length);
1439
+ this._isLoading.set(false);
1440
+ const showFilters = !this.config()?.hideFilters;
1441
+ if (showFilters) {
1442
+ this.filtersService.setOptions(this.getFilters(response.columns));
1443
+ }
1444
+ }
1445
+ handleTableDataErrorResponse(error) {
1446
+ console.error(error);
1447
+ this._columns.set([]);
1448
+ this._data.set(new TableDataSource());
1449
+ this._total.set(0);
1450
+ this._isLoading.set(false);
1451
+ }
1452
+ getFilters(columns) {
1453
+ return columns.reduce((acc, column) => {
1454
+ if (column.filters) {
1455
+ acc.push(column.filters);
1456
+ }
1457
+ return acc;
1458
+ }, []);
1459
+ }
1460
+ interpolateRowValues(body, row = {}) {
1461
+ const result = {};
1462
+ Object.entries(body).forEach(([key, value]) => {
1463
+ if (typeof value === 'string' && isPlaceholder$1(value)) {
1464
+ const valueKey = value.replace(/\{row\.(\w+)\}/g, '$1');
1465
+ if (row[valueKey]) {
1466
+ result[key] = row[valueKey];
1467
+ }
1468
+ }
1469
+ else {
1470
+ result[key] = value;
1471
+ }
1472
+ });
1473
+ return result;
1474
+ }
1475
+ setEffects() {
1476
+ effect(() => {
1477
+ const node = this.node();
1478
+ untracked(() => {
1479
+ this.filtersService.updateSelection({});
1480
+ this.filtersService.setOptions([]);
1481
+ this.setPagination(node.ui?.pagination);
1482
+ this._sortConfig.set(node?.ui?.sort ?? null);
1483
+ this._sortMap.set(mapSortStringListToSortMap(node?.ui?.sort?.default ?? []));
1484
+ });
1485
+ });
1486
+ effect(() => {
1487
+ const node = this.node();
1488
+ const pageSize = this.pageSize();
1489
+ const pageIndex = this.pageIndex();
1490
+ const sort = this._sortMap();
1491
+ const filters = this.filtersService.selection();
1492
+ untracked(() => {
1493
+ if (node?.dataSource) {
1494
+ const ctx = buildRequestContext({
1495
+ pageSize,
1496
+ pageIndex,
1497
+ sort: mapSortMapToSortStringList(sort),
1498
+ filters,
1499
+ }, {
1500
+ isSortable: this.isSortingEnabled(),
1501
+ hasPagination: !!this.pageSize(),
1502
+ });
1503
+ this.getTableData(node, ctx);
1504
+ }
1505
+ });
1506
+ });
1507
+ effect(() => {
1508
+ const sortMap = this._sortMap();
1509
+ untracked(() => {
1510
+ this._sortOrder.update(() => {
1511
+ return [...sortMap].reduce((acc, item, index) => {
1512
+ return acc.set(item[0], index + 1);
1513
+ }, new Map());
1514
+ });
1515
+ });
1516
+ });
1517
+ }
1518
+ forceFetch() {
1519
+ const node = this.node();
1520
+ const pageSize = this.pageSize();
1521
+ const pageIndex = this.pageIndex();
1522
+ const sort = this._sortMap();
1523
+ const filters = this.filtersService.selection();
1524
+ if (node?.dataSource) {
1525
+ const ctx = buildRequestContext({
1526
+ pageSize,
1527
+ pageIndex,
1528
+ sort: mapSortMapToSortStringList(sort),
1529
+ filters,
1530
+ }, {
1531
+ isSortable: this.isSortingEnabled(),
1532
+ hasPagination: !!this.pageSize(),
1533
+ });
1534
+ this.getTableData(node, ctx);
1535
+ }
1536
+ }
1537
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1538
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: TableViewComponent, isStandalone: true, selector: "table-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "@if (node()?.title || node()?.actions || filters().length) {\n <div class=\"le-toolbar\">\n @if (node(); as node) {\n <h2>{{ node?.title }}</h2>\n\n <span class=\"spacer\"></span>\n\n @if (filters().length) {\n <button class=\"le-button\" (click)=\"onFiltersClick($event)\">Filters</button>\n }\n\n @if (node.actions?.tableActions) {\n <table-view-actions\n [actions]=\"node.actions!.tableActions\"\n (actionClicked)=\"onToolbarActionClick($event)\"\n ></table-view-actions>\n }\n }\n </div>\n}\n\n@if (pageSize()) {\n <table-view-pagination\n [pageSize]=\"pageSize()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [entriesCount]=\"entriesCount()\"\n (pageChange)=\"onPageChange($event)\"\n ></table-view-pagination>\n}\n\n<div class=\"le-table-container\">\n @if (displayedColumns().length) {\n <table cdk-table [dataSource]=\"data()\" class=\"le-table\" [class.le-table--loading]=\"isLoading()\">\n <!-- Info column -->\n <ng-container cdkColumnDef=\"info\">\n <th class=\"le-table-header-cell le-table-icon-cell\" cdk-header-cell *cdkHeaderCellDef>\n <le-icon size=\"16\" class=\"le-icon\" name=\"info\"></le-icon>\n </th>\n\n <td class=\"le-table-cell le-table-icon-cell\" cdk-cell *cdkCellDef=\"let row\">\n <button class=\"le-button le-button-basic le-button--success\" aria-label=\"View info\" (click)=\"onRowInfoClick($event, row)\">\n <le-icon size=\"16\" class=\"le-icon\" name=\"info\"></le-icon>\n </button>\n </td>\n </ng-container>\n <!-- Modified column -->\n <ng-container cdkColumnDef=\"modified\">\n <th class=\"le-table-header-cell\" cdk-header-cell *cdkHeaderCellDef>\n <div class=\"le-cell-content\">\n Modified\n </div>\n </th>\n\n <td class=\"le-table-cell\" cdk-cell *cdkCellDef=\"let row\">\n <span class=\"le-table-template le-template-string\">\n {{ row._metadata.updated_at | date:'medium' }}\n </span>\n </td>\n </ng-container>\n\n <!-- Columns -->\n @for (column of columns(); track column) {\n <ng-container [cdkColumnDef]=\"column.key\">\n <th\n class=\"le-table-header-cell\"\n cdk-header-cell\n *cdkHeaderCellDef\n [tabindex]=\"sortColumnKeys().has(column.key) ? 0 : -1\"\n [class.le-table-header-cell--sortable]=\"sortColumnKeys().has(column.key)\"\n (click)=\"sortColumnKeys().has(column.key) && onSortClick($event, column)\"\n >\n <div class=\"le-cell-content\">\n <ng-template\n tableViewCell\n [data]=\"column.label\"\n [type]=\"column.column_format?.headerTemplate\"\n ></ng-template>\n\n @if (sortColumnKeys().has(column.key)) {\n <div class=\"le-sort-container\">\n @if (sortMap().has(column.key)) {\n <div class=\"le-sort-icon\">\n <i\n class=\"le-sort-arrow\"\n [class.le-sort-arrow--asc]=\"sortMap().get(column.key) === 'asc'\"\n [class.le-sort-arrow--desc]=\"sortMap().get(column.key) === 'desc'\"\n ></i>\n </div>\n\n <div class=\"le-sort-order\">\n @if (sortOrder().has(column.key)) {\n <span>\n {{ sortOrder().get(column.key) }}\n </span>\n }\n </div>\n } @else {\n <le-icon name=\"sort\" class=\"le-sort-icon le-sort-icon--unfold\"></le-icon>\n }\n </div>\n }\n </div>\n </th>\n\n <td class=\"le-table-cell\" cdk-cell *cdkCellDef=\"let row\">\n <ng-template\n tableViewCell\n [data]=\"row[column.key]\"\n [type]=\"column.column_format?.cellTemplate\"\n [column]=\"column\"\n [metadata]=\"row._metadata\"\n (clicked)=\"onCellClicked($event)\"\n ></ng-template>\n </td>\n </ng-container>\n }\n\n <tr cdk-header-row class=\"le-header-row\" *cdkHeaderRowDef=\"displayedColumns()\"></tr>\n <tr\n cdk-row\n class=\"le-table-row\"\n [tabindex]=\"0\"\n [class.le-table-row--clickable]=\"hasRowClickAction()\"\n *cdkRowDef=\"let row; columns: displayedColumns()\"\n (click)=\"onRowClick(row)\"\n ></tr>\n </table>\n }\n</div>\n\n@if (data().count === 0) {\n @if (isLoading()) {\n <app-loading-view [headerHeight]=\"65\" [rowCount]=\"pageSize() || 10\"></app-loading-view>\n } @else {\n <div class=\"no-entities\">\n <span>No Entities</span>\n </div>\n }\n}\n", dependencies: [{ kind: "component", type: CdkTable, selector: "cdk-table, table[cdk-table]", inputs: ["trackBy", "dataSource", "multiTemplateDataRows", "fixedLayout"], outputs: ["contentChanged"], exportAs: ["cdkTable"] }, { kind: "directive", type: CdkColumnDef, selector: "[cdkColumnDef]", inputs: ["cdkColumnDef", "sticky", "stickyEnd"] }, { kind: "directive", type: CdkHeaderCell, selector: "cdk-header-cell, th[cdk-header-cell]" }, { kind: "directive", type: CdkHeaderCellDef, selector: "[cdkHeaderCellDef]" }, { kind: "directive", type: CdkCell, selector: "cdk-cell, td[cdk-cell]" }, { kind: "directive", type: CdkCellDef, selector: "[cdkCellDef]" }, { kind: "directive", type: CdkHeaderRowDef, selector: "[cdkHeaderRowDef]", inputs: ["cdkHeaderRowDef", "cdkHeaderRowDefSticky"] }, { kind: "component", type: CdkHeaderRow, selector: "cdk-header-row, tr[cdk-header-row]" }, { kind: "component", type: CdkRow, selector: "cdk-row, tr[cdk-row]" }, { kind: "directive", type: CdkRowDef, selector: "[cdkRowDef]", inputs: ["cdkRowDefColumns", "cdkRowDefWhen"] }, { kind: "component", type: TableViewPaginationComponent, selector: "table-view-pagination", inputs: ["pageSize", "pageSizeOptions", "entriesCount"], outputs: ["pageChange"] }, { kind: "component", type: LoadingViewComponent, selector: "app-loading-view", inputs: ["headerHeight", "rowHeight", "rowCount"] }, { kind: "component", type: TableViewActions, selector: "table-view-actions", inputs: ["actions"], outputs: ["actionClicked"] }, { kind: "directive", type: TableViewCellDirective, selector: "[tableViewCell]", inputs: ["type", "data", "column", "metadata"], outputs: ["clicked"] }, { kind: "component", type: LeIconComponent, selector: "le-icon", inputs: ["name", "size"] }, { kind: "ngmodule", type: DialogModule }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1539
+ }
1540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TableViewComponent, decorators: [{
1541
+ type: Component,
1542
+ args: [{ selector: 'table-view', imports: [
1543
+ CdkTable,
1544
+ CdkColumnDef,
1545
+ CdkHeaderCell,
1546
+ CdkHeaderCellDef,
1547
+ CdkCell,
1548
+ CdkCellDef,
1549
+ CdkHeaderRowDef,
1550
+ CdkHeaderRow,
1551
+ CdkRow,
1552
+ CdkRowDef,
1553
+ TableViewPaginationComponent,
1554
+ LoadingViewComponent,
1555
+ TableViewActions,
1556
+ TableViewCellDirective,
1557
+ LeIconComponent,
1558
+ DialogModule,
1559
+ DatePipe,
1560
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (node()?.title || node()?.actions || filters().length) {\n <div class=\"le-toolbar\">\n @if (node(); as node) {\n <h2>{{ node?.title }}</h2>\n\n <span class=\"spacer\"></span>\n\n @if (filters().length) {\n <button class=\"le-button\" (click)=\"onFiltersClick($event)\">Filters</button>\n }\n\n @if (node.actions?.tableActions) {\n <table-view-actions\n [actions]=\"node.actions!.tableActions\"\n (actionClicked)=\"onToolbarActionClick($event)\"\n ></table-view-actions>\n }\n }\n </div>\n}\n\n@if (pageSize()) {\n <table-view-pagination\n [pageSize]=\"pageSize()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [entriesCount]=\"entriesCount()\"\n (pageChange)=\"onPageChange($event)\"\n ></table-view-pagination>\n}\n\n<div class=\"le-table-container\">\n @if (displayedColumns().length) {\n <table cdk-table [dataSource]=\"data()\" class=\"le-table\" [class.le-table--loading]=\"isLoading()\">\n <!-- Info column -->\n <ng-container cdkColumnDef=\"info\">\n <th class=\"le-table-header-cell le-table-icon-cell\" cdk-header-cell *cdkHeaderCellDef>\n <le-icon size=\"16\" class=\"le-icon\" name=\"info\"></le-icon>\n </th>\n\n <td class=\"le-table-cell le-table-icon-cell\" cdk-cell *cdkCellDef=\"let row\">\n <button class=\"le-button le-button-basic le-button--success\" aria-label=\"View info\" (click)=\"onRowInfoClick($event, row)\">\n <le-icon size=\"16\" class=\"le-icon\" name=\"info\"></le-icon>\n </button>\n </td>\n </ng-container>\n <!-- Modified column -->\n <ng-container cdkColumnDef=\"modified\">\n <th class=\"le-table-header-cell\" cdk-header-cell *cdkHeaderCellDef>\n <div class=\"le-cell-content\">\n Modified\n </div>\n </th>\n\n <td class=\"le-table-cell\" cdk-cell *cdkCellDef=\"let row\">\n <span class=\"le-table-template le-template-string\">\n {{ row._metadata.updated_at | date:'medium' }}\n </span>\n </td>\n </ng-container>\n\n <!-- Columns -->\n @for (column of columns(); track column) {\n <ng-container [cdkColumnDef]=\"column.key\">\n <th\n class=\"le-table-header-cell\"\n cdk-header-cell\n *cdkHeaderCellDef\n [tabindex]=\"sortColumnKeys().has(column.key) ? 0 : -1\"\n [class.le-table-header-cell--sortable]=\"sortColumnKeys().has(column.key)\"\n (click)=\"sortColumnKeys().has(column.key) && onSortClick($event, column)\"\n >\n <div class=\"le-cell-content\">\n <ng-template\n tableViewCell\n [data]=\"column.label\"\n [type]=\"column.column_format?.headerTemplate\"\n ></ng-template>\n\n @if (sortColumnKeys().has(column.key)) {\n <div class=\"le-sort-container\">\n @if (sortMap().has(column.key)) {\n <div class=\"le-sort-icon\">\n <i\n class=\"le-sort-arrow\"\n [class.le-sort-arrow--asc]=\"sortMap().get(column.key) === 'asc'\"\n [class.le-sort-arrow--desc]=\"sortMap().get(column.key) === 'desc'\"\n ></i>\n </div>\n\n <div class=\"le-sort-order\">\n @if (sortOrder().has(column.key)) {\n <span>\n {{ sortOrder().get(column.key) }}\n </span>\n }\n </div>\n } @else {\n <le-icon name=\"sort\" class=\"le-sort-icon le-sort-icon--unfold\"></le-icon>\n }\n </div>\n }\n </div>\n </th>\n\n <td class=\"le-table-cell\" cdk-cell *cdkCellDef=\"let row\">\n <ng-template\n tableViewCell\n [data]=\"row[column.key]\"\n [type]=\"column.column_format?.cellTemplate\"\n [column]=\"column\"\n [metadata]=\"row._metadata\"\n (clicked)=\"onCellClicked($event)\"\n ></ng-template>\n </td>\n </ng-container>\n }\n\n <tr cdk-header-row class=\"le-header-row\" *cdkHeaderRowDef=\"displayedColumns()\"></tr>\n <tr\n cdk-row\n class=\"le-table-row\"\n [tabindex]=\"0\"\n [class.le-table-row--clickable]=\"hasRowClickAction()\"\n *cdkRowDef=\"let row; columns: displayedColumns()\"\n (click)=\"onRowClick(row)\"\n ></tr>\n </table>\n }\n</div>\n\n@if (data().count === 0) {\n @if (isLoading()) {\n <app-loading-view [headerHeight]=\"65\" [rowCount]=\"pageSize() || 10\"></app-loading-view>\n } @else {\n <div class=\"no-entities\">\n <span>No Entities</span>\n </div>\n }\n}\n" }]
1561
+ }], ctorParameters: () => [] });
1562
+
1563
+ const isSectionsSchema = (raw) => {
1564
+ if (!raw || typeof raw !== 'object')
1565
+ return false;
1566
+ const r = raw;
1567
+ return Boolean(r.sections || // direct sections array
1568
+ r.control?.type === 'sections' || // control.type === 'sections'
1569
+ r.control?.sections || // control.sections
1570
+ r.meta?.sections);
1571
+ };
1572
+ const adaptForm = (raw) => {
1573
+ if (isSectionsSchema(raw)) {
1574
+ return adaptSections(raw);
1575
+ }
1576
+ return adaptOld(raw);
1577
+ };
1578
+
1579
+ class FormRendererComponent {
1580
+ renderer = inject(Renderer2);
1581
+ ref = inject(ElementRef);
1582
+ destroyRef = inject(DestroyRef);
1583
+ styleRef = this.renderer.createElement('style');
1584
+ engine = input.required();
1585
+ sections = input.required();
1586
+ topLevelControls = input([]);
1587
+ customCss = input();
1588
+ config = input();
1589
+ event = output();
1590
+ constructor() {
1591
+ this.renderer.appendChild(this.ref.nativeElement, this.styleRef);
1592
+ this.destroyRef.onDestroy(() => {
1593
+ this.renderer.removeChild(this.ref.nativeElement, this.styleRef);
1594
+ });
1595
+ effect(() => {
1596
+ this.styleRef.textContent = this.customCss() ?? '';
1597
+ });
1598
+ }
1599
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1600
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: FormRendererComponent, isStandalone: true, selector: "le-form-renderer", inputs: { engine: { classPropertyName: "engine", publicName: "engine", isSignal: true, isRequired: true, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: true, transformFunction: null }, topLevelControls: { classPropertyName: "topLevelControls", publicName: "topLevelControls", isSignal: true, isRequired: false, transformFunction: null }, customCss: { classPropertyName: "customCss", publicName: "customCss", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "<div class=\"le-form-container\">\n @for (section of sections(); track section.id) {\n <section class=\"le-section\">\n <div class=\"le-toolbar\">\n @if (section.label) {\n <h3>{{ section.label }}</h3>\n }\n\n <span class=\"spacer\"></span>\n\n @if (config()?.hasCloseButton) {\n <button class=\"le-button\" aria-label=\"Close side panel\" (click)=\"event.emit({ type: 'close'})\">Close</button>\n }\n </div>\n\n <div class=\"le-section-controls\">\n @for (control of section.controls; track control) {\n <fe-field [engine]=\"engine()\" [control]=\"control\"></fe-field>\n }\n </div>\n </section>\n }\n\n @if (topLevelControls()?.length) {\n <section class=\"le-top-level-controls\">\n @for (control of topLevelControls(); track control) {\n <fe-field [engine]=\"engine()\" [control]=\"control\"></fe-field>\n }\n </section>\n }\n</div>\n", dependencies: [{ kind: "component", type: FeFieldHost, selector: "fe-field", inputs: ["engine", "control"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1601
+ }
1602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormRendererComponent, decorators: [{
1603
+ type: Component,
1604
+ args: [{ selector: 'le-form-renderer', imports: [FeFieldHost], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"le-form-container\">\n @for (section of sections(); track section.id) {\n <section class=\"le-section\">\n <div class=\"le-toolbar\">\n @if (section.label) {\n <h3>{{ section.label }}</h3>\n }\n\n <span class=\"spacer\"></span>\n\n @if (config()?.hasCloseButton) {\n <button class=\"le-button\" aria-label=\"Close side panel\" (click)=\"event.emit({ type: 'close'})\">Close</button>\n }\n </div>\n\n <div class=\"le-section-controls\">\n @for (control of section.controls; track control) {\n <fe-field [engine]=\"engine()\" [control]=\"control\"></fe-field>\n }\n </div>\n </section>\n }\n\n @if (topLevelControls()?.length) {\n <section class=\"le-top-level-controls\">\n @for (control of topLevelControls(); track control) {\n <fe-field [engine]=\"engine()\" [control]=\"control\"></fe-field>\n }\n </section>\n }\n</div>\n" }]
1605
+ }], ctorParameters: () => [] });
1606
+
1607
+ const isPlaceholder = (value) => {
1608
+ return typeof value === 'string' && value.startsWith('{') && value.endsWith('}');
1609
+ };
1610
+ const isObject = (value) => {
1611
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1612
+ };
1613
+ /**
1614
+ * @param value "{email_verification_code_field}"
1615
+ * @returns "email_verification_code_field"
1616
+ */
1617
+ const getPlaceholderValue = (value) => {
1618
+ return value.slice(1, -1);
1619
+ };
1620
+ /**
1621
+ * @param body {"email_verification_code": "{email_verification_code_field}"}
1622
+ * @returns ["email_verification_code_field"]
1623
+ */
1624
+ const mapActionBodyToFieldsArray = (body) => {
1625
+ const result = [];
1626
+ for (const placeholder of Object.values(body)) {
1627
+ if (isPlaceholder(placeholder)) {
1628
+ result.push(getPlaceholderValue(placeholder));
1629
+ }
1630
+ }
1631
+ return result;
1632
+ };
1633
+ /**
1634
+ * @param body {"email_verification_code": "{email_verification_code_field}"}
1635
+ * @param errors {"email_verification_code": "Invalid email verification code"}
1636
+ * @returns true if body fields have errors
1637
+ */
1638
+ const isErrorCorrespondActionBody = (body, errors) => {
1639
+ const bodyFields = mapActionBodyToFieldsArray(body);
1640
+ return bodyFields.some((field) => errors[field] !== undefined);
1641
+ };
1642
+ const getValueByPath = (obj = {}, path) => {
1643
+ if (path.includes('.')) {
1644
+ return path.split('.').reduce((acc, key) => acc && acc[key], obj);
1645
+ }
1646
+ return obj[path];
1647
+ };
1648
+ const parseLooseJSON = (str) => {
1649
+ const clean = str.trim().replace(/,\s*([}\]])/g, '$1');
1650
+ try {
1651
+ return JSON.parse(clean);
1652
+ }
1653
+ catch {
1654
+ return str;
1655
+ }
1656
+ };
1657
+ const mapBodyStringModelToRequestBody = (bodyModel, values = {}) => {
1658
+ if (isPlaceholder(bodyModel)) {
1659
+ const path = getPlaceholderValue(bodyModel);
1660
+ const value = getValueByPath(values, path);
1661
+ if (value !== null && value !== undefined && value !== '') {
1662
+ try {
1663
+ return parseLooseJSON(value);
1664
+ }
1665
+ catch {
1666
+ return value;
1667
+ }
1668
+ }
1669
+ else {
1670
+ return null;
1671
+ }
1672
+ }
1673
+ else {
1674
+ return bodyModel;
1675
+ }
1676
+ };
1677
+ const mapBodyObjectModelToRequestBody = (bodyModel = {}, values = {}) => {
1678
+ const result = {};
1679
+ for (const { 0: key, 1: placeholder } of Object.entries(bodyModel)) {
1680
+ if (isPlaceholder(placeholder)) {
1681
+ const path = getPlaceholderValue(placeholder);
1682
+ const value = getValueByPath(values, path);
1683
+ if (value !== null && value !== undefined && value !== '') {
1684
+ result[key] = value;
1685
+ }
1686
+ else {
1687
+ result[key] = null;
1688
+ }
1689
+ }
1690
+ else if (isObject(placeholder)) {
1691
+ result[key] = mapBodyObjectModelToRequestBody(placeholder, values);
1692
+ }
1693
+ else {
1694
+ result[key] = placeholder;
1695
+ }
1696
+ }
1697
+ return result;
1698
+ };
1699
+ const mapBodyModelToRequestBody = (bodyModel, values) => {
1700
+ if (typeof bodyModel === 'string') {
1701
+ return mapBodyStringModelToRequestBody(bodyModel, values);
1702
+ }
1703
+ return mapBodyObjectModelToRequestBody(bodyModel, values);
1704
+ };
1705
+
1706
+ class ToastListComponent {
1707
+ toastService = inject(LeToastService);
1708
+ toasts = this.toastService.toasts;
1709
+ onButtonClick(actions) {
1710
+ actions.forEach((action) => this.toastService.processAction(action));
1711
+ }
1712
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ToastListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1713
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ToastListComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: "@if (toasts().length) {\n <div class=\"le-toast-list\">\n @for (toast of toasts(); track toast.id) {\n <div\n class=\"le-toast\"\n [class.le-toast--success]=\"toast.type === 'success'\"\n [class.le-toast--info]=\"toast.type === 'info'\"\n [class.le-toast--warning]=\"toast.type === 'warning'\"\n [class.le-toast--error]=\"toast.type === 'error'\"\n >\n <div class=\"le-toast-text-list\">\n @for (control of toast.controls; track control) {\n @if (control.type === 'text') {\n <p class=\"le-toast-text\">\n {{ control.value }}\n </p>\n }\n }\n </div>\n\n <div class=\"le-toast-button-list\">\n @for (control of toast.controls; track control) {\n @if (control.type === 'button') {\n <button\n class=\"le-button le-button-stroked\"\n [class.le-button--success]=\"toast.type === 'success'\"\n [class.le-button--info]=\"toast.type === 'info'\"\n [class.le-button--warning]=\"toast.type === 'warning'\"\n [class.le-button--error]=\"toast.type === 'error'\"\n (click)=\"onButtonClick(control.actions)\"\n >\n {{ control.label }}\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n}\n" });
1714
+ }
1715
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ToastListComponent, decorators: [{
1716
+ type: Component,
1717
+ args: [{ template: "@if (toasts().length) {\n <div class=\"le-toast-list\">\n @for (toast of toasts(); track toast.id) {\n <div\n class=\"le-toast\"\n [class.le-toast--success]=\"toast.type === 'success'\"\n [class.le-toast--info]=\"toast.type === 'info'\"\n [class.le-toast--warning]=\"toast.type === 'warning'\"\n [class.le-toast--error]=\"toast.type === 'error'\"\n >\n <div class=\"le-toast-text-list\">\n @for (control of toast.controls; track control) {\n @if (control.type === 'text') {\n <p class=\"le-toast-text\">\n {{ control.value }}\n </p>\n }\n }\n </div>\n\n <div class=\"le-toast-button-list\">\n @for (control of toast.controls; track control) {\n @if (control.type === 'button') {\n <button\n class=\"le-button le-button-stroked\"\n [class.le-button--success]=\"toast.type === 'success'\"\n [class.le-button--info]=\"toast.type === 'info'\"\n [class.le-button--warning]=\"toast.type === 'warning'\"\n [class.le-button--error]=\"toast.type === 'error'\"\n (click)=\"onButtonClick(control.actions)\"\n >\n {{ control.label }}\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n}\n" }]
1718
+ }] });
1719
+
1720
+ let ID = 100_000_000;
1721
+ class LeToastService {
1722
+ overlayRef;
1723
+ overlay = inject(Overlay);
1724
+ _toasts = signal([]);
1725
+ toasts = this._toasts.asReadonly();
1726
+ constructor() {
1727
+ effect(() => {
1728
+ const toasts = this._toasts();
1729
+ if (toasts.length > 0) {
1730
+ this.show();
1731
+ }
1732
+ else {
1733
+ this.hide();
1734
+ }
1735
+ });
1736
+ }
1737
+ notify(toast) {
1738
+ this._toasts.update((toasts) => [...toasts, toast]);
1739
+ }
1740
+ warn(...messages) {
1741
+ this.notify(this.buildToast('warning', messages));
1742
+ }
1743
+ info(...messages) {
1744
+ this.notify(this.buildToast('info', messages));
1745
+ }
1746
+ success(...messages) {
1747
+ this.notify(this.buildToast('success', messages));
1748
+ }
1749
+ error(...messages) {
1750
+ this.notify(this.buildToast('error', messages));
1751
+ }
1752
+ remove(id) {
1753
+ this._toasts.update((toasts) => toasts.filter((toast) => toast.id !== id));
1754
+ }
1755
+ processAction(action) {
1756
+ if (action.type === 'close_popup') {
1757
+ this.remove(action.target);
1758
+ }
1759
+ }
1760
+ show() {
1761
+ if (this.overlayRef)
1762
+ return;
1763
+ this.overlayRef = this.overlay.create({
1764
+ positionStrategy: this.overlay.position().global().right().top(),
1765
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
1766
+ });
1767
+ this.overlayRef.attach(new ComponentPortal(ToastListComponent));
1768
+ }
1769
+ hide() {
1770
+ this.overlayRef?.dispose();
1771
+ this.overlayRef = undefined;
1772
+ }
1773
+ buildToast(type, messages) {
1774
+ const id = `${type}_${ID++}`;
1775
+ const textControls = messages.map((message) => ({
1776
+ type: 'text',
1777
+ value: message,
1778
+ }));
1779
+ const buttonControl = {
1780
+ type: 'button',
1781
+ label: 'Ok',
1782
+ actions: [{ type: 'close_popup', target: id }],
1783
+ };
1784
+ return {
1785
+ id,
1786
+ type,
1787
+ controls: [...textControls, buttonControl],
1788
+ };
1789
+ }
1790
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1791
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeToastService, providedIn: 'root' });
1792
+ }
1793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeToastService, decorators: [{
1794
+ type: Injectable,
1795
+ args: [{
1796
+ providedIn: 'root',
1797
+ }]
1798
+ }], ctorParameters: () => [] });
1799
+
1800
+ /**
1801
+ * Add or update URL parameter
1802
+ */
1803
+ const addUrlParameter = (url, paramName, paramValue) => {
1804
+ try {
1805
+ const urlObj = new URL(url);
1806
+ urlObj.searchParams.set(paramName, paramValue);
1807
+ return urlObj.toString();
1808
+ }
1809
+ catch {
1810
+ // If URL parsing fails, try simple string manipulation
1811
+ const separator = url.includes('?') ? '&' : '?';
1812
+ return `${url}${separator}${paramName}=${encodeURIComponent(paramValue)}`;
1813
+ }
1814
+ };
1815
+ /**
1816
+ * Get preview URL for image (with disposition_type=inline)
1817
+ */
1818
+ const getPreviewUrl = (downloadUrl) => {
1819
+ return addUrlParameter(downloadUrl, 'disposition_type', 'inline');
1820
+ };
1821
+ /**
1822
+ * Clear file data from 'unsafe:' prefix
1823
+ */
1824
+ const clearFileData = (data) => {
1825
+ return data.startsWith('unsafe:') ? data.substring(7) : data;
1826
+ };
1827
+ /**
1828
+ * If attachment is an image
1829
+ */
1830
+ const isImageAttachment = (attachment) => {
1831
+ return !!(attachment.mimetype && attachment.mimetype.toLowerCase().includes('image'));
1832
+ };
1833
+ const isDownloadFileString = (str) => {
1834
+ return isUrlString(str) && str.includes('/download-file/');
1835
+ };
1836
+ const isUrlString = (str) => {
1837
+ return str.startsWith('http://') || str.startsWith('https://');
1838
+ };
1839
+ const isBase64String = (str) => {
1840
+ return str.startsWith('base64,');
1841
+ };
1842
+ const isDataUrlString = (str) => {
1843
+ return str.startsWith('data:');
1844
+ };
1845
+ /**
1846
+ * Is attachment reference
1847
+ */
1848
+ const isAttachmentRef = (attachment) => {
1849
+ return (!!attachment.ref &&
1850
+ !!attachment.ref.resource &&
1851
+ !!attachment.ref.class_name &&
1852
+ !!attachment.ref.object_id &&
1853
+ !!attachment.ref.object_version);
1854
+ };
1855
+ /**
1856
+ * Get attachment url
1857
+ */
1858
+ const getAttachmentUrl = (attachment) => {
1859
+ return `${attachment.ref.resource}%23${attachment.ref.class_name}%3A${attachment.ref.class_version}%3A${attachment.ref.object_id}%3A${attachment.ref.object_version}`;
1860
+ };
1861
+
1862
+ class FormViewAttachmentService {
1863
+ apiService = inject(LEApiService);
1864
+ _cache = new Map();
1865
+ async resolve(attachment) {
1866
+ if (!attachment.ref)
1867
+ return '';
1868
+ const objectId = attachment.ref.object_id;
1869
+ if (!this._cache.has(objectId)) {
1870
+ const fileInfo = await this.fetchAttachmentData(attachment);
1871
+ if (typeof fileInfo?.data === 'string') {
1872
+ const normalized = await this.normalizeFileData(fileInfo.data, fileInfo.filename, attachment);
1873
+ this._cache.set(objectId, normalized);
1874
+ }
1875
+ }
1876
+ return this._cache.get(objectId)?.data ?? '';
1877
+ }
1878
+ async download(attachment) {
1879
+ const fileInfo = await this.resolveAttachmentData(attachment);
1880
+ if (!fileInfo)
1881
+ return;
1882
+ const file = await firstValueFrom(this.apiService.getBlob(fileInfo.data));
1883
+ this.downloadWithAnchor(file, fileInfo.filename);
1884
+ }
1885
+ async resolveAttachmentData(attachment) {
1886
+ if (!attachment?.ref?.object_id)
1887
+ return null;
1888
+ if (this._cache.has(attachment.ref.object_id)) {
1889
+ const cached = this._cache.get(attachment.ref.object_id);
1890
+ return {
1891
+ data: cached.originalUrl || cached.data,
1892
+ filename: cached.filename,
1893
+ };
1894
+ }
1895
+ if (isAttachmentRef(attachment)) {
1896
+ return this.fetchAttachmentData(attachment);
1897
+ }
1898
+ return null;
1899
+ }
1900
+ async fetchAttachmentData(attachment) {
1901
+ const response = await firstValueFrom(this.apiService.getObjectByAddress(getAttachmentUrl(attachment), {
1902
+ includeVersions: false,
1903
+ includeMeta: false,
1904
+ }));
1905
+ const row = response?.rows?.[0];
1906
+ if (!row?.data)
1907
+ return null;
1908
+ return {
1909
+ data: row.data,
1910
+ filename: row.filename || attachment.filename || 'file',
1911
+ };
1912
+ }
1913
+ async normalizeFileData(data, filename, attachment) {
1914
+ const cleanData = clearFileData(data);
1915
+ const result = {
1916
+ data: '',
1917
+ filename: filename,
1918
+ isUrl: false,
1919
+ };
1920
+ if (isDownloadFileString(cleanData) && isImageAttachment(attachment)) {
1921
+ // For images with download-file URLs, create preview URL with disposition_type=inline
1922
+ result.data = await firstValueFrom(this.apiService.getBlob(getPreviewUrl(cleanData)));
1923
+ result.isUrl = true;
1924
+ result.originalUrl = cleanData;
1925
+ }
1926
+ else if (isUrlString(cleanData)) {
1927
+ // Regular URL (not download-file) - use directly
1928
+ result.data = cleanData;
1929
+ result.originalUrl = cleanData;
1930
+ result.isUrl = true;
1931
+ }
1932
+ else if (isDataUrlString(cleanData)) {
1933
+ // Already a data URL
1934
+ result.data = cleanData;
1935
+ }
1936
+ else if (isBase64String(cleanData)) {
1937
+ // Legacy format "base64,actualdata" (for backward compatibility)
1938
+ result.data = `data:${attachment.mimetype ?? 'application/octet-stream'};${cleanData}`;
1939
+ }
1940
+ else {
1941
+ // New format: pure base64 data without prefix (like in old integration)
1942
+ result.data = `data:${attachment.mimetype ?? 'application/octet-stream'};base64,${cleanData}`;
1943
+ }
1944
+ return result;
1945
+ }
1946
+ downloadWithAnchor(file, filename) {
1947
+ const anchor = document.createElement('a');
1948
+ anchor.href = file;
1949
+ anchor.download = filename || 'File';
1950
+ anchor.click();
1951
+ URL.revokeObjectURL(file);
1952
+ }
1953
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormViewAttachmentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1954
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormViewAttachmentService });
1955
+ }
1956
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormViewAttachmentService, decorators: [{
1957
+ type: Injectable
1958
+ }] });
1959
+
1960
+ class FormViewComponent {
1961
+ apiService = inject(LEApiService);
1962
+ dataService = inject(LEDataService);
1963
+ attachmentService = inject(FormViewAttachmentService);
1964
+ viewportScroller = inject(ViewportScroller);
1965
+ toastService = inject(LeToastService);
1966
+ _isLoading = signal(false);
1967
+ _form = signal(null);
1968
+ _sections = signal([]);
1969
+ _topLevelControls = signal([]);
1970
+ _engine = signal(null);
1971
+ _cancelRequest$ = new Subject();
1972
+ _actionHandlers = {
1973
+ invoke: (action) => {
1974
+ const engine = this._engine();
1975
+ let body = {};
1976
+ if (!engine) {
1977
+ return of(null);
1978
+ }
1979
+ if (!action.async) {
1980
+ engine.markAllTouched();
1981
+ }
1982
+ engine.validate();
1983
+ const hasError = isErrorCorrespondActionBody(action.body ?? {}, engine.status.errors);
1984
+ if (hasError) {
1985
+ return of(null);
1986
+ }
1987
+ if (action.body) {
1988
+ body = mapBodyModelToRequestBody(action.body, engine.values.getAll());
1989
+ }
1990
+ if (!action.async) {
1991
+ this._isLoading.set(true);
1992
+ }
1993
+ return this.dataService
1994
+ .request(action.method, action.url, body)
1995
+ .pipe(tap((response) => {
1996
+ if (action.onSuccess && response.ok) {
1997
+ this.processActionList(action.onSuccess, response.body);
1998
+ }
1999
+ else if (action.onError && !response.ok) {
2000
+ this.processActionList(action.onError);
2001
+ }
2002
+ else {
2003
+ this._isLoading.set(false);
2004
+ }
2005
+ this.event.emit({ type: 'refresh' });
2006
+ this.event.emit({ type: 'close' });
2007
+ }), map(() => null), catchError(() => {
2008
+ if (action.onError) {
2009
+ this.processActionList(action.onError);
2010
+ }
2011
+ return of(null);
2012
+ }));
2013
+ },
2014
+ update_form: (action) => {
2015
+ const node = this.mapUpdateFormActionToFormNode(action.control_source);
2016
+ if (node) {
2017
+ this.setView(node);
2018
+ }
2019
+ return of(null);
2020
+ },
2021
+ update_table: (action) => {
2022
+ const node = this.mapUpdateTableActionToTableNode(action.data_source);
2023
+ this.event.emit({ type: 'refresh' });
2024
+ this.event.emit({ type: 'close' });
2025
+ this.setView(node);
2026
+ return of(null);
2027
+ },
2028
+ update_value: (action, response) => {
2029
+ return of(action).pipe(filter((action) => isPlaceholder(action.value)), map((action) => mapBodyObjectModelToRequestBody({ [action.field_id]: action.value }, { response })), tap((result) => {
2030
+ if (result[action.field_id]) {
2031
+ this._engine()?.values.set(action.field_id, result[action.field_id]);
2032
+ }
2033
+ }), map(() => null));
2034
+ },
2035
+ scroll_top: () => {
2036
+ this.viewportScroller.scrollToPosition([0, 0]);
2037
+ return of(null);
2038
+ },
2039
+ show_popup: (action) => {
2040
+ this.toastService.notify({
2041
+ ...action.item,
2042
+ type: 'success',
2043
+ });
2044
+ return of(null);
2045
+ },
2046
+ open_url: (action) => {
2047
+ window.open(action.url, '_blank');
2048
+ return of(null);
2049
+ },
2050
+ download: (action) => {
2051
+ this.attachmentService.download(action.payload);
2052
+ return of(null);
2053
+ },
2054
+ navigate_to_section: (action) => {
2055
+ this.dataService.navigateByNodeId(action.section_id);
2056
+ return of(null);
2057
+ },
2058
+ };
2059
+ node = input.required();
2060
+ config = input();
2061
+ event = output();
2062
+ isLoading = this._isLoading.asReadonly();
2063
+ data = computed(() => {
2064
+ const engine = this._engine();
2065
+ const sections = this._sections();
2066
+ const topLevelControls = this._topLevelControls();
2067
+ const customCss = this.dataService.customCss() ?? '';
2068
+ if (!engine || !sections)
2069
+ return null;
2070
+ return {
2071
+ engine,
2072
+ sections,
2073
+ customCss,
2074
+ topLevelControls,
2075
+ };
2076
+ });
2077
+ constructor() {
2078
+ effect((onCleanup) => {
2079
+ const node = this.node();
2080
+ if (node) {
2081
+ this.getFormData(node).pipe(takeUntil(this._cancelRequest$)).subscribe();
2082
+ }
2083
+ onCleanup(() => {
2084
+ this._cancelRequest$.next();
2085
+ });
2086
+ });
2087
+ effect(() => {
2088
+ const form = this._form();
2089
+ if (!form)
2090
+ return;
2091
+ untracked(() => {
2092
+ this.init(form);
2093
+ });
2094
+ });
2095
+ }
2096
+ init(form) {
2097
+ this._sections.set(this.sortSections(form.sections));
2098
+ this._topLevelControls.set(form.controls ?? []);
2099
+ const resolvers = {
2100
+ attachment: (value) => this.attachmentService.resolve(value),
2101
+ objectLatest: (className) => firstValueFrom(this.dataService.getObjectByClassName(className)),
2102
+ };
2103
+ this._engine.set(createEngine(form, {}, resolvers));
2104
+ // this._engine()?.on('valueChanges', (value) => {
2105
+ // console.log('Value Changes', value);
2106
+ // // this.cdr.markForCheck();
2107
+ // });
2108
+ // this._engine()?.on('fieldStateChanges', (state) => {
2109
+ // console.log('Field State Changes', state);
2110
+ // // this.cdr.markForCheck();
2111
+ // });
2112
+ this._engine()?.on('action', ({ action }) => {
2113
+ this.processActionList(action.actions);
2114
+ });
2115
+ }
2116
+ setView(node) {
2117
+ this.dataService.setView(node, !!this.config()?.skipLocationChange);
2118
+ }
2119
+ processAction(action, response) {
2120
+ return this._actionHandlers[action.type](action, response);
2121
+ }
2122
+ sortSections(sections) {
2123
+ return [...sections].sort((a, b) => {
2124
+ const orderA = a.order ?? 0;
2125
+ const orderB = b.order ?? 0;
2126
+ return orderA - orderB;
2127
+ });
2128
+ }
2129
+ getFormData(node) {
2130
+ if (node.controlSource?.kind === 'transaction') {
2131
+ this._isLoading.set(true);
2132
+ return this.apiService
2133
+ .executeTransaction(node.controlSource.entity, node.controlSource.params ?? {})
2134
+ .pipe(tap((response) => {
2135
+ this._form.set(adaptForm(response));
2136
+ this._isLoading.set(false);
2137
+ }), map(() => null), catchError((error) => {
2138
+ console.error(error);
2139
+ this._isLoading.set(false);
2140
+ return of(null);
2141
+ }));
2142
+ }
2143
+ if (node.controlSource?.kind === 'object') {
2144
+ this._isLoading.set(true);
2145
+ return this.apiService
2146
+ .getObjectByAddress(node.controlSource.entity, node.controlSource.params ?? {})
2147
+ .pipe(tap((response) => {
2148
+ this._form.set(adaptForm(response.control));
2149
+ this._isLoading.set(false);
2150
+ }), map(() => null), catchError((error) => {
2151
+ console.error(error);
2152
+ this._isLoading.set(false);
2153
+ return of(null);
2154
+ }));
2155
+ }
2156
+ throw new Error(`Unsupported control source kind ${node.controlSource?.kind}`);
2157
+ }
2158
+ mapUpdateFormActionToFormNode(source) {
2159
+ if (source.dynamic_value) {
2160
+ return {
2161
+ id: source.dynamic_value.entity_name,
2162
+ type: 'form',
2163
+ controlSource: {
2164
+ kind: source.dynamic_value.type,
2165
+ entity: source.dynamic_value.entity_name,
2166
+ params: source.dynamic_value.body ?? {},
2167
+ },
2168
+ };
2169
+ }
2170
+ return null;
2171
+ }
2172
+ mapUpdateTableActionToTableNode(source) {
2173
+ return {
2174
+ id: source.entity_name,
2175
+ title: source.entity_name,
2176
+ type: 'table',
2177
+ dataSource: {
2178
+ kind: source.type,
2179
+ entity: source.entity_name,
2180
+ },
2181
+ };
2182
+ }
2183
+ processActionList(items, response) {
2184
+ from(items)
2185
+ .pipe(concatMap((a) => this.processAction(a, response)), finalize(() => {
2186
+ this._isLoading.set(false);
2187
+ }))
2188
+ .subscribe();
2189
+ }
2190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2191
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: FormViewComponent, isStandalone: true, selector: "form-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, providers: [FormViewAttachmentService], ngImport: i0, template: "<h2>{{ node()?.title }}</h2>\n\n@if (isLoading()) {\n <app-loading-view [rowCount]=\"5\"></app-loading-view>\n} @else {\n @if (data(); as data) {\n <le-form-renderer\n [config]=\"config()\"\n [engine]=\"data.engine\"\n [sections]=\"data.sections\"\n [customCss]=\"data.customCss\"\n [topLevelControls]=\"data.topLevelControls\"\n (event)=\"event.emit($event)\"\n ></le-form-renderer>\n }\n}\n", dependencies: [{ kind: "component", type: LoadingViewComponent, selector: "app-loading-view", inputs: ["headerHeight", "rowHeight", "rowCount"] }, { kind: "component", type: FormRendererComponent, selector: "le-form-renderer", inputs: ["engine", "sections", "topLevelControls", "customCss", "config"], outputs: ["event"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2192
+ }
2193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormViewComponent, decorators: [{
2194
+ type: Component,
2195
+ args: [{ selector: 'form-view', imports: [LoadingViewComponent, FormRendererComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [FormViewAttachmentService], template: "<h2>{{ node()?.title }}</h2>\n\n@if (isLoading()) {\n <app-loading-view [rowCount]=\"5\"></app-loading-view>\n} @else {\n @if (data(); as data) {\n <le-form-renderer\n [config]=\"config()\"\n [engine]=\"data.engine\"\n [sections]=\"data.sections\"\n [customCss]=\"data.customCss\"\n [topLevelControls]=\"data.topLevelControls\"\n (event)=\"event.emit($event)\"\n ></le-form-renderer>\n }\n}\n" }]
2196
+ }], ctorParameters: () => [] });
2197
+
2198
+ class SectionComponent {
2199
+ node = input.required();
2200
+ config = input();
2201
+ event = output();
2202
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2203
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SectionComponent, isStandalone: true, selector: "section-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n", dependencies: [{ kind: "component", type: i0.forwardRef(() => NodeComponent), selector: "le-node", inputs: ["node", "config"], outputs: ["event"] }] });
2204
+ }
2205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SectionComponent, decorators: [{
2206
+ type: Component,
2207
+ args: [{ selector: 'section-view', imports: [forwardRef(() => NodeComponent)], template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n" }]
2208
+ }] });
2209
+
2210
+ class GridColViewComponent {
2211
+ node = input.required();
2212
+ config = input();
2213
+ event = output();
2214
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: GridColViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: GridColViewComponent, isStandalone: true, selector: "grid-col-view", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n", dependencies: [{ kind: "component", type: i0.forwardRef(() => NodeComponent), selector: "le-node", inputs: ["node", "config"], outputs: ["event"] }] });
2216
+ }
2217
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: GridColViewComponent, decorators: [{
2218
+ type: Component,
2219
+ args: [{ selector: 'grid-col-view', imports: [forwardRef(() => NodeComponent)], template: "@for (child of node().children; track child.id) {\n <le-node [node]=\"child\" [config]=\"config()\" (event)=\"event.emit($event)\"></le-node>\n}\n" }]
2220
+ }] });
2221
+
2222
+ const NODE_COMPONENTS = {
2223
+ section: SectionComponent,
2224
+ grid: GridComponent,
2225
+ grid_col: GridColViewComponent,
2226
+ form: FormViewComponent,
2227
+ table: TableViewComponent,
2228
+ chart: ChartViewComponent,
2229
+ };
2230
+ class NodeComponent {
2231
+ node = input.required();
2232
+ config = input();
2233
+ event = output();
2234
+ componentPortal;
2235
+ constructor() {
2236
+ effect(() => {
2237
+ this.componentPortal = new ComponentPortal(NODE_COMPONENTS[this.node().type]);
2238
+ });
2239
+ }
2240
+ onPortalAttached(ref) {
2241
+ const componentRef = ref;
2242
+ componentRef.setInput('node', this.node());
2243
+ componentRef.setInput('config', this.config());
2244
+ const sub = componentRef.instance.event.subscribe((event) => {
2245
+ this.event.emit(event);
2246
+ });
2247
+ componentRef.onDestroy(() => {
2248
+ sub.unsubscribe();
2249
+ });
2250
+ }
2251
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2252
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.21", type: NodeComponent, isStandalone: true, selector: "le-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { event: "event" }, ngImport: i0, template: "<ng-template\n [cdkPortalOutlet]=\"componentPortal\"\n (attached)=\"onPortalAttached($event)\">\n</ng-template>\n\n<!--@if (node(); as node) {-->\n<!-- @switch (node.type) {-->\n<!-- @case ('section') {-->\n<!-- <section-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></section-view>-->\n<!-- }-->\n<!-- @case ('grid') {-->\n<!-- <grid-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></grid-view>-->\n<!-- }-->\n<!-- @case ('grid_col') {-->\n<!-- <grid-col-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></grid-col-view>-->\n<!-- }-->\n<!-- @case ('table') {-->\n<!-- <table-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></table-view>-->\n<!-- }-->\n<!-- @case ('chart') {-->\n<!-- <chart-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></chart-view>-->\n<!-- }-->\n<!-- @case ('form') {-->\n<!-- <form-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></form-view>-->\n<!-- }-->\n<!-- }-->\n<!--}-->\n", dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2253
+ }
2254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: NodeComponent, decorators: [{
2255
+ type: Component,
2256
+ args: [{ selector: 'le-node', imports: [CdkPortalOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template\n [cdkPortalOutlet]=\"componentPortal\"\n (attached)=\"onPortalAttached($event)\">\n</ng-template>\n\n<!--@if (node(); as node) {-->\n<!-- @switch (node.type) {-->\n<!-- @case ('section') {-->\n<!-- <section-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></section-view>-->\n<!-- }-->\n<!-- @case ('grid') {-->\n<!-- <grid-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></grid-view>-->\n<!-- }-->\n<!-- @case ('grid_col') {-->\n<!-- <grid-col-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></grid-col-view>-->\n<!-- }-->\n<!-- @case ('table') {-->\n<!-- <table-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></table-view>-->\n<!-- }-->\n<!-- @case ('chart') {-->\n<!-- <chart-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></chart-view>-->\n<!-- }-->\n<!-- @case ('form') {-->\n<!-- <form-view [node]=\"node\" [config]=\"config()\" (event)=\"event.emit($event)\"></form-view>-->\n<!-- }-->\n<!-- }-->\n<!--}-->\n" }]
2257
+ }], ctorParameters: () => [] });
2258
+
2259
+ class FiltersComponent {
2260
+ filtersService = inject(LEFiltersService);
2261
+ overlay = inject(Overlay);
2262
+ viewContainerRef = inject(ViewContainerRef);
2263
+ _filteredOptions = signal([]);
2264
+ _isDropdownOpened = signal(false);
2265
+ overlayRef = null;
2266
+ destroy$ = new Subject();
2267
+ isOpened = this.filtersService.shown;
2268
+ options = this.filtersService.options;
2269
+ filteredOptions = this._filteredOptions.asReadonly();
2270
+ isDropdownOpened = this._isDropdownOpened.asReadonly();
2271
+ inputEl = viewChild('inputEl');
2272
+ inputContainerEl = viewChild('inputContainerEl');
2273
+ dropdownTpl = viewChild('autocompleteDropdownTpl');
2274
+ contentTpl = viewChild('contentEl');
2275
+ search = new FormControl();
2276
+ selectedFilters = new SelectionModel(true);
2277
+ filtersForm = new FormGroup({});
2278
+ constructor() {
2279
+ this.initFilters();
2280
+ this.listenSearchChanges();
2281
+ this.listenSelectionChanges();
2282
+ this.listenDropdownState();
2283
+ }
2284
+ ngAfterViewInit() {
2285
+ fromEvent(this.contentTpl().nativeElement, 'click')
2286
+ .pipe(takeUntil(this.destroy$))
2287
+ .subscribe({
2288
+ next: () => this.filtersService.close()
2289
+ });
2290
+ }
2291
+ ngOnDestroy() {
2292
+ this.destroy$.next();
2293
+ this.destroy$.complete();
2294
+ }
2295
+ onApplyClick(event) {
2296
+ event.stopPropagation();
2297
+ this.filtersService.updateSelection(this.filtersForm.getRawValue());
2298
+ }
2299
+ onResetClick(event) {
2300
+ event.stopPropagation();
2301
+ this.clearSelection();
2302
+ }
2303
+ onCloseClick(event) {
2304
+ event.stopPropagation();
2305
+ this.closeDropdown();
2306
+ this.filtersService.close();
2307
+ }
2308
+ onInput(event) {
2309
+ event.stopPropagation();
2310
+ this.search.setValue(event.target.value);
2311
+ }
2312
+ onInputClearClick(event) {
2313
+ event.stopPropagation();
2314
+ this.search.setValue('');
2315
+ }
2316
+ onFocus(event) {
2317
+ event.stopPropagation();
2318
+ this.showDropdown();
2319
+ }
2320
+ onBlur(event) {
2321
+ event.stopPropagation();
2322
+ }
2323
+ onOptionClick(control) {
2324
+ this.selectedFilters.toggle(control);
2325
+ this.closeDropdown();
2326
+ this.search.setValue('');
2327
+ }
2328
+ onFilterRemoveClick(control) {
2329
+ this.selectedFilters.toggle(control);
2330
+ }
2331
+ closePanel() {
2332
+ this.overlayRef?.detach();
2333
+ this.overlayRef?.dispose();
2334
+ this.overlayRef = null;
2335
+ }
2336
+ openPanel() {
2337
+ if (this.overlayRef !== null)
2338
+ return;
2339
+ const positionStrategy = this.overlay
2340
+ .position()
2341
+ .flexibleConnectedTo(this.inputContainerEl())
2342
+ .withPositions([
2343
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
2344
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
2345
+ ])
2346
+ .withPush(false);
2347
+ this.overlayRef = this.overlay.create({
2348
+ positionStrategy,
2349
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
2350
+ width: this.inputContainerEl().nativeElement.offsetWidth,
2351
+ hasBackdrop: false,
2352
+ });
2353
+ this.overlayRef.attach(new TemplatePortal(this.dropdownTpl(), this.viewContainerRef));
2354
+ this.overlayRef
2355
+ .outsidePointerEvents()
2356
+ .pipe(takeUntil(this.destroy$))
2357
+ .subscribe((event) => {
2358
+ if (!this.inputContainerEl().nativeElement.contains(event.target)) {
2359
+ this.closeDropdown();
2360
+ }
2361
+ });
2362
+ }
2363
+ showDropdown() {
2364
+ this._isDropdownOpened.set(true);
2365
+ }
2366
+ closeDropdown() {
2367
+ this._isDropdownOpened.set(false);
2368
+ }
2369
+ clearSelection() {
2370
+ this.selectedFilters.clear();
2371
+ this.filtersService.updateSelection({});
2372
+ }
2373
+ initFilters() {
2374
+ effect(() => {
2375
+ const options = this.options();
2376
+ untracked(() => {
2377
+ this._filteredOptions.set(options);
2378
+ this.selectedFilters.clear();
2379
+ });
2380
+ });
2381
+ }
2382
+ listenSearchChanges() {
2383
+ this.search.valueChanges
2384
+ .pipe(startWith(''), map((value) => this.filter(value)), takeUntilDestroyed())
2385
+ .subscribe((filters) => {
2386
+ this._filteredOptions.set(filters);
2387
+ });
2388
+ }
2389
+ listenSelectionChanges() {
2390
+ this.selectedFilters.changed.pipe(takeUntilDestroyed()).subscribe(({ added, removed }) => {
2391
+ added.forEach((option) => {
2392
+ this.filtersForm.addControl(option.key, new FormControl(''));
2393
+ });
2394
+ removed.forEach((option) => {
2395
+ this.filtersForm.removeControl(option.key);
2396
+ });
2397
+ });
2398
+ }
2399
+ listenDropdownState() {
2400
+ effect(() => {
2401
+ const isDropdownOpened = this.isDropdownOpened();
2402
+ untracked(() => {
2403
+ if (isDropdownOpened) {
2404
+ this.openPanel();
2405
+ }
2406
+ else {
2407
+ this.closePanel();
2408
+ }
2409
+ });
2410
+ });
2411
+ }
2412
+ filter(value) {
2413
+ const options = this.options();
2414
+ try {
2415
+ const filterValue = value.toLowerCase();
2416
+ return options.reduce((acc, option) => {
2417
+ const filters = option.filters.filter((filter) => {
2418
+ return filter.control.label.toLowerCase().includes(filterValue.toLowerCase());
2419
+ });
2420
+ if (filters.length) {
2421
+ acc.push({ ...option, filters });
2422
+ }
2423
+ return acc;
2424
+ }, []);
2425
+ }
2426
+ catch {
2427
+ return options;
2428
+ }
2429
+ }
2430
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2431
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: FiltersComponent, isStandalone: true, selector: "le-filters", viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }, { propertyName: "inputContainerEl", first: true, predicate: ["inputContainerEl"], descendants: true, isSignal: true }, { propertyName: "dropdownTpl", first: true, predicate: ["autocompleteDropdownTpl"], descendants: true, isSignal: true }, { propertyName: "contentTpl", first: true, predicate: ["contentEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div leDrawerContainer>\n <div leDrawer type=\"over\" [opened]=\"isOpened()\" [width]=\"320\" [hideOnClose]=\"true\" [style.z-index]=\"100\">\n <div class=\"le-filters-container\">\n <div class=\"le-filters-title\">Filters</div>\n\n <div class=\"le-divider le-divider-horizontal\"></div>\n\n <div class=\"le-filters-actions\">\n <button class=\"le-button le-button-flat le-button--success\" (click)=\"onApplyClick($event)\">Apply</button>\n\n <button class=\"le-button le-button-basic\" (click)=\"onResetClick($event)\">Reset</button>\n\n <button class=\"le-button le-button-basic\" (click)=\"onCloseClick($event)\">Close</button>\n </div>\n\n <div #inputContainerEl class=\"le-autocomplete-container\">\n <input\n #inputEl\n class=\"le-autocomplete-input\"\n type=\"text\"\n placeholder=\"Select filters\"\n [value]=\"search.value\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n autocomplete=\"off\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isDropdownOpened()\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n >\n\n <button type=\"button\" class=\"le-button-icon\" (click)=\"onInputClearClick($event)\">\n <le-icon size=\"16\" class=\"le-icon-clear\" name=\"close\"></le-icon>\n </button>\n </div>\n\n <div>\n <form [formGroup]=\"filtersForm\" class=\"le-filters-list\">\n @for (control of selectedFilters.selected; track control.key) {\n <div class=\"le-filters-list-item\">\n <label class=\"le-filters-list-label\" [for]=\"control.key\">{{control.label}}</label>\n\n <div class=\"le-filters-list-input\">\n @switch (control.type) {\n @case ('single_select') {\n <select [id]=\"control.key\" [formControlName]=\"control.key\">\n @for (option of control.options; track option) {\n <option [value]=\"option\">{{option}}</option>\n }\n </select>\n }\n @case ('multi_select') {\n <select [id]=\"control.key\" [formControlName]=\"control.key\" multiple>\n @for (option of control.options; track option) {\n <option [value]=\"option\">{{option}}</option>\n }\n </select>\n }\n @case ('dateTime') {\n <input type=\"datetime-local\" [id]=\"control.key\" [formControlName]=\"control.key\">\n }\n @case ('date') {\n <input type=\"date\" [id]=\"control.key\" [formControlName]=\"control.key\">\n }\n @default {\n <input\n type=\"text\"\n [id]=\"control.key\"\n [formControlName]=\"control.key\"\n (keydown.enter)=\"onApplyClick($event)\"\n >\n }\n }\n\n <button type=\"button\" class=\"le-button-icon\" (click)=\"onFilterRemoveClick(control)\">\n <le-icon size=\"16\" class=\"le-icon-clear\" name=\"close\"></le-icon>\n </button>\n </div>\n </div>\n }\n </form>\n </div>\n </div>\n </div>\n\n <div #contentEl leDrawerContent>\n <ng-content></ng-content>\n </div>\n</div>\n\n\n<ng-template #autocompleteDropdownTpl>\n <div class=\"le-autocomplete-panel\" role=\"listbox\">\n @for (option of filteredOptions(); track option) {\n <div class=\"le-autocomplete-label\">{{option.label}}</div>\n\n <div>\n @for (item of option.filters; track item) {\n <div\n role=\"option\"\n class=\"le-autocomplete-option\"\n [class.le-autocomplete-option--selected]=\"selectedFilters.isSelected(item.control)\"\n [attr.aria-selected]=\"selectedFilters.isSelected(item.control)\"\n (click)=\"onOptionClick(item.control)\"\n >{{item.control.label}}</div>\n }\n </div>\n }\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: DrawerContentDirective, selector: "[leDrawerContent]" }, { kind: "directive", type: DrawerContainerDirective, selector: "[leDrawerContainer]" }, { kind: "directive", type: LeDrawerDirective, selector: "[leDrawer]", inputs: ["opened", "width", "hideOnClose", "type"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: LeIconComponent, selector: "le-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2432
+ }
2433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FiltersComponent, decorators: [{
2434
+ type: Component,
2435
+ args: [{ selector: 'le-filters', imports: [
2436
+ DrawerContentDirective,
2437
+ DrawerContainerDirective,
2438
+ LeDrawerDirective,
2439
+ ReactiveFormsModule,
2440
+ LeIconComponent,
2441
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div leDrawerContainer>\n <div leDrawer type=\"over\" [opened]=\"isOpened()\" [width]=\"320\" [hideOnClose]=\"true\" [style.z-index]=\"100\">\n <div class=\"le-filters-container\">\n <div class=\"le-filters-title\">Filters</div>\n\n <div class=\"le-divider le-divider-horizontal\"></div>\n\n <div class=\"le-filters-actions\">\n <button class=\"le-button le-button-flat le-button--success\" (click)=\"onApplyClick($event)\">Apply</button>\n\n <button class=\"le-button le-button-basic\" (click)=\"onResetClick($event)\">Reset</button>\n\n <button class=\"le-button le-button-basic\" (click)=\"onCloseClick($event)\">Close</button>\n </div>\n\n <div #inputContainerEl class=\"le-autocomplete-container\">\n <input\n #inputEl\n class=\"le-autocomplete-input\"\n type=\"text\"\n placeholder=\"Select filters\"\n [value]=\"search.value\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n autocomplete=\"off\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isDropdownOpened()\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n >\n\n <button type=\"button\" class=\"le-button-icon\" (click)=\"onInputClearClick($event)\">\n <le-icon size=\"16\" class=\"le-icon-clear\" name=\"close\"></le-icon>\n </button>\n </div>\n\n <div>\n <form [formGroup]=\"filtersForm\" class=\"le-filters-list\">\n @for (control of selectedFilters.selected; track control.key) {\n <div class=\"le-filters-list-item\">\n <label class=\"le-filters-list-label\" [for]=\"control.key\">{{control.label}}</label>\n\n <div class=\"le-filters-list-input\">\n @switch (control.type) {\n @case ('single_select') {\n <select [id]=\"control.key\" [formControlName]=\"control.key\">\n @for (option of control.options; track option) {\n <option [value]=\"option\">{{option}}</option>\n }\n </select>\n }\n @case ('multi_select') {\n <select [id]=\"control.key\" [formControlName]=\"control.key\" multiple>\n @for (option of control.options; track option) {\n <option [value]=\"option\">{{option}}</option>\n }\n </select>\n }\n @case ('dateTime') {\n <input type=\"datetime-local\" [id]=\"control.key\" [formControlName]=\"control.key\">\n }\n @case ('date') {\n <input type=\"date\" [id]=\"control.key\" [formControlName]=\"control.key\">\n }\n @default {\n <input\n type=\"text\"\n [id]=\"control.key\"\n [formControlName]=\"control.key\"\n (keydown.enter)=\"onApplyClick($event)\"\n >\n }\n }\n\n <button type=\"button\" class=\"le-button-icon\" (click)=\"onFilterRemoveClick(control)\">\n <le-icon size=\"16\" class=\"le-icon-clear\" name=\"close\"></le-icon>\n </button>\n </div>\n </div>\n }\n </form>\n </div>\n </div>\n </div>\n\n <div #contentEl leDrawerContent>\n <ng-content></ng-content>\n </div>\n</div>\n\n\n<ng-template #autocompleteDropdownTpl>\n <div class=\"le-autocomplete-panel\" role=\"listbox\">\n @for (option of filteredOptions(); track option) {\n <div class=\"le-autocomplete-label\">{{option.label}}</div>\n\n <div>\n @for (item of option.filters; track item) {\n <div\n role=\"option\"\n class=\"le-autocomplete-option\"\n [class.le-autocomplete-option--selected]=\"selectedFilters.isSelected(item.control)\"\n [attr.aria-selected]=\"selectedFilters.isSelected(item.control)\"\n (click)=\"onOptionClick(item.control)\"\n >{{item.control.label}}</div>\n }\n </div>\n }\n </div>\n</ng-template>\n" }]
2442
+ }], ctorParameters: () => [] });
2443
+
2444
+ class LeContainerComponent {
2445
+ auth = inject(LEAuthService);
2446
+ dataService = inject(LEDataService);
2447
+ sidebar = viewChild(SidebarComponent);
2448
+ data = computed(() => {
2449
+ const config = this.dataService.config();
2450
+ const view = this.dataService.view();
2451
+ if (!config || !view)
2452
+ return null;
2453
+ return {
2454
+ config,
2455
+ view,
2456
+ };
2457
+ });
2458
+ constructor() {
2459
+ this.listenAuthState();
2460
+ this.listenPageInit();
2461
+ this.dataService.registerEffects();
2462
+ }
2463
+ onSelectionChange(view) {
2464
+ this.dataService.setView(view);
2465
+ }
2466
+ listenAuthState() {
2467
+ effect(() => {
2468
+ const token = this.auth.token();
2469
+ const isGuest = this.auth.isGuest();
2470
+ if (!token && !isGuest)
2471
+ return;
2472
+ untracked(() => {
2473
+ this.dataService.requestConfig();
2474
+ });
2475
+ });
2476
+ }
2477
+ listenPageInit() {
2478
+ effect(() => {
2479
+ const path = this.dataService.parentPath();
2480
+ const sidebar = this.sidebar();
2481
+ if (!(path.length && sidebar))
2482
+ return;
2483
+ untracked(() => {
2484
+ path.forEach((node) => {
2485
+ sidebar.setExpanded(node);
2486
+ });
2487
+ });
2488
+ });
2489
+ }
2490
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2491
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: LeContainerComponent, isStandalone: true, selector: "le-container", providers: [LEDataService, LEApiService, LEFiltersService, LeNavigationService], viewQueries: [{ propertyName: "sidebar", first: true, predicate: SidebarComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (data(); as data) {\n <le-sidebar\n [config]=\"data.config\"\n [view]=\"data.view\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n <le-filters>\n <le-node [node]=\"data.view\"></le-node>\n </le-filters>\n </le-sidebar>\n}\n", dependencies: [{ kind: "component", type: SidebarComponent, selector: "le-sidebar", inputs: ["config", "view"], outputs: ["selectionChange"] }, { kind: "component", type: NodeComponent, selector: "le-node", inputs: ["node", "config"], outputs: ["event"] }, { kind: "component", type: FiltersComponent, selector: "le-filters" }] });
2492
+ }
2493
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: LeContainerComponent, decorators: [{
2494
+ type: Component,
2495
+ args: [{ selector: 'le-container', imports: [SidebarComponent, NodeComponent, FiltersComponent], providers: [LEDataService, LEApiService, LEFiltersService, LeNavigationService], template: "@if (data(); as data) {\n <le-sidebar\n [config]=\"data.config\"\n [view]=\"data.view\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n <le-filters>\n <le-node [node]=\"data.view\"></le-node>\n </le-filters>\n </le-sidebar>\n}\n" }]
2496
+ }], ctorParameters: () => [] });
2497
+
2498
+ const provideConfig = (config) => {
2499
+ return {
2500
+ provide: CONFIG_TOKEN,
2501
+ useValue: {
2502
+ ...defaultConfig,
2503
+ ...config,
2504
+ },
2505
+ };
2506
+ };
2507
+
2508
+ const LEAuthInterceptor = (req, next) => {
2509
+ const authService = inject(LEAuthService);
2510
+ if (authService.isGuest()) {
2511
+ return next(req);
2512
+ }
2513
+ if (req.context.get(IS_LIBRARY_REQUEST)) {
2514
+ try {
2515
+ authService.validateToken();
2516
+ return next(req.clone({ setHeaders: { Authorization: authService.token() } }));
2517
+ }
2518
+ catch (e) {
2519
+ inject(LeToastService).error(e.message);
2520
+ console.error(e);
2521
+ }
2522
+ }
2523
+ return next(req);
2524
+ };
2525
+
2526
+ /**
2527
+ * Generated bundle index. Do not edit.
2528
+ */
2529
+
2530
+ export { CONFIG_TOKEN, LEAuthInterceptor, LEAuthService, LeContainerComponent, LeToastService, provideConfig };
2531
+ //# sourceMappingURL=planeasyinc-le-angular.mjs.map