@nucleus-suite-pe/core 0.7.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/nucleus-suite-pe-core.mjs +2084 -355
- package/fesm2022/nucleus-suite-pe-core.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/tokens.css +233 -13
- package/types/nucleus-suite-pe-core.d.ts +496 -51
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, Injector, signal, computed, Injectable, effect,
|
|
2
|
+
import { InjectionToken, makeEnvironmentProviders, inject, Injector, signal, computed, Injectable, effect, DestroyRef, ElementRef, input, Directive, model, untracked, ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild, output, afterNextRender, TemplateRef, contentChildren } from '@angular/core';
|
|
3
3
|
import { HttpContextToken, HttpClient, HttpContext, HttpErrorResponse } from '@angular/common/http';
|
|
4
4
|
import { firstValueFrom, catchError, throwError, from, switchMap } from 'rxjs';
|
|
5
|
-
import
|
|
5
|
+
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
6
|
+
import * as i1 from '@angular/forms';
|
|
6
7
|
import { FormsModule } from '@angular/forms';
|
|
7
|
-
import * as
|
|
8
|
+
import * as i2 from 'primeng/select';
|
|
8
9
|
import { SelectModule } from 'primeng/select';
|
|
9
10
|
import { definePreset } from '@primeuix/themes';
|
|
10
11
|
import Lara from '@primeuix/themes/lara';
|
|
11
12
|
import { providePrimeNG } from 'primeng/config';
|
|
13
|
+
import * as i1$1 from 'primeng/skeleton';
|
|
14
|
+
import { SkeletonModule } from 'primeng/skeleton';
|
|
15
|
+
import { RouterLink, Router } from '@angular/router';
|
|
12
16
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
13
|
-
import * as
|
|
14
|
-
import { CheckboxModule } from 'primeng/checkbox';
|
|
15
|
-
import * as i2 from 'primeng/inputtext';
|
|
17
|
+
import * as i2$1 from 'primeng/inputtext';
|
|
16
18
|
import { InputTextModule } from 'primeng/inputtext';
|
|
17
|
-
import * as
|
|
19
|
+
import * as i3 from 'primeng/popover';
|
|
20
|
+
import { PopoverModule } from 'primeng/popover';
|
|
21
|
+
import * as i2$2 from 'primeng/checkbox';
|
|
22
|
+
import { CheckboxModule } from 'primeng/checkbox';
|
|
23
|
+
import * as i5 from 'primeng/textarea';
|
|
18
24
|
import { TextareaModule } from 'primeng/textarea';
|
|
19
|
-
import * as
|
|
25
|
+
import * as i6 from 'primeng/toggleswitch';
|
|
20
26
|
import { ToggleSwitchModule } from 'primeng/toggleswitch';
|
|
21
|
-
import { NgTemplateOutlet } from '@angular/common';
|
|
22
27
|
import * as i1$2 from 'primeng/button';
|
|
23
28
|
import { ButtonModule } from 'primeng/button';
|
|
24
|
-
import * as
|
|
29
|
+
import * as i3$1 from 'primeng/table';
|
|
25
30
|
import { TableModule } from 'primeng/table';
|
|
26
|
-
import * as
|
|
31
|
+
import * as i4 from 'primeng/tag';
|
|
27
32
|
import { TagModule } from 'primeng/tag';
|
|
28
|
-
import * as
|
|
33
|
+
import * as i5$1 from 'primeng/tooltip';
|
|
29
34
|
import { TooltipModule } from 'primeng/tooltip';
|
|
30
|
-
import { Router } from '@angular/router';
|
|
31
35
|
import * as i1$3 from 'primeng/avatar';
|
|
32
36
|
import { AvatarModule } from 'primeng/avatar';
|
|
33
|
-
import * as i2$
|
|
37
|
+
import * as i2$3 from 'primeng/breadcrumb';
|
|
34
38
|
import { BreadcrumbModule } from 'primeng/breadcrumb';
|
|
35
|
-
import * as i4$
|
|
39
|
+
import * as i4$1 from 'primeng/confirmdialog';
|
|
36
40
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
37
|
-
import * as i5$
|
|
41
|
+
import * as i5$2 from 'primeng/toast';
|
|
38
42
|
import { ToastModule } from 'primeng/toast';
|
|
39
43
|
import * as i6$1 from 'primeng/drawer';
|
|
40
44
|
import { DrawerModule } from 'primeng/drawer';
|
|
41
|
-
import * as i7 from 'primeng/listbox';
|
|
42
|
-
import { ListboxModule } from 'primeng/listbox';
|
|
43
|
-
import * as i8 from 'primeng/popover';
|
|
44
|
-
import { PopoverModule } from 'primeng/popover';
|
|
45
45
|
|
|
46
46
|
const NUCLEUS_CONFIG = new InjectionToken('NUCLEUS_CONFIG');
|
|
47
47
|
const CONFIG_URL = 'config.json';
|
|
@@ -225,6 +225,191 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
225
225
|
args: [{ providedIn: 'root' }]
|
|
226
226
|
}] });
|
|
227
227
|
|
|
228
|
+
const COMMON_COPY = {
|
|
229
|
+
es: {
|
|
230
|
+
search: 'Buscar',
|
|
231
|
+
choose: 'Selecciona',
|
|
232
|
+
noIcon: 'Sin icono',
|
|
233
|
+
noColor: 'Sin color',
|
|
234
|
+
searchIcon: 'Busca un icono',
|
|
235
|
+
noMatches: 'Ningún icono coincide',
|
|
236
|
+
sessionExpired: 'Tu sesión expiró. Vuelve a iniciar sesión.',
|
|
237
|
+
errorNetwork: 'No se pudo conectar con el servidor. Revisa tu conexión o inténtalo en unos minutos.',
|
|
238
|
+
errorTimeout: 'El servidor tardó demasiado en responder. Inténtalo de nuevo.',
|
|
239
|
+
errorServer: 'Algo falló en el servidor y no pudimos completar la operación. Inténtalo de nuevo en unos ' +
|
|
240
|
+
'minutos; si sigue pasando, avísale al equipo.',
|
|
241
|
+
errorInvalid: 'Revisa los datos: hay algo que no es válido.',
|
|
242
|
+
errorForbidden: 'No tienes permiso para hacer esto.',
|
|
243
|
+
errorNotFound: 'No encontramos lo que buscabas. Puede que ya no exista.',
|
|
244
|
+
errorConflict: 'Ese cambio choca con algo que ya existe.',
|
|
245
|
+
errorUnknown: 'No se pudo completar la operación. Inténtalo de nuevo.',
|
|
246
|
+
},
|
|
247
|
+
en: {
|
|
248
|
+
search: 'Search',
|
|
249
|
+
choose: 'Select',
|
|
250
|
+
noIcon: 'No icon',
|
|
251
|
+
noColor: 'No colour',
|
|
252
|
+
searchIcon: 'Search an icon',
|
|
253
|
+
noMatches: 'No icon matches',
|
|
254
|
+
sessionExpired: 'Your session expired. Sign in again.',
|
|
255
|
+
errorNetwork: 'Could not reach the server. Check your connection or try again in a moment.',
|
|
256
|
+
errorTimeout: 'The server took too long to answer. Try again.',
|
|
257
|
+
errorServer: 'Something broke on the server and we could not finish. Try again in a few minutes; if it ' +
|
|
258
|
+
'keeps happening, tell the team.',
|
|
259
|
+
errorInvalid: 'Check the data: something is not valid.',
|
|
260
|
+
errorForbidden: 'You do not have permission to do this.',
|
|
261
|
+
errorNotFound: 'We could not find that. It may no longer exist.',
|
|
262
|
+
errorConflict: 'That change clashes with something that already exists.',
|
|
263
|
+
errorUnknown: 'We could not finish the operation. Try again.',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
function commonCopyOf(lang) {
|
|
267
|
+
return lang === 'en' ? COMMON_COPY.en : COMMON_COPY.es;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const NUCLEUS_LANGS = ['es', 'en'];
|
|
271
|
+
const STORAGE_KEY$3 = 'nucleus_lang';
|
|
272
|
+
function isLang(value) {
|
|
273
|
+
return value === 'es' || value === 'en';
|
|
274
|
+
}
|
|
275
|
+
function readStored$2() {
|
|
276
|
+
try {
|
|
277
|
+
const stored = localStorage.getItem(STORAGE_KEY$3);
|
|
278
|
+
if (isLang(stored))
|
|
279
|
+
return stored;
|
|
280
|
+
}
|
|
281
|
+
catch { }
|
|
282
|
+
return 'es';
|
|
283
|
+
}
|
|
284
|
+
let current = readStored$2();
|
|
285
|
+
function nucleusLang() {
|
|
286
|
+
return current;
|
|
287
|
+
}
|
|
288
|
+
class NucleusLanguage {
|
|
289
|
+
_lang = signal(readStored$2(), ...(ngDevMode ? [{ debugName: "_lang" }] : /* istanbul ignore next */ []));
|
|
290
|
+
lang = this._lang.asReadonly();
|
|
291
|
+
constructor() {
|
|
292
|
+
effect(() => {
|
|
293
|
+
const lang = this._lang();
|
|
294
|
+
current = lang;
|
|
295
|
+
document.documentElement.lang = lang;
|
|
296
|
+
try {
|
|
297
|
+
localStorage.setItem(STORAGE_KEY$3, lang);
|
|
298
|
+
}
|
|
299
|
+
catch { }
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
setLang(lang) {
|
|
303
|
+
this._lang.set(lang);
|
|
304
|
+
}
|
|
305
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusLanguage, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
306
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusLanguage, providedIn: 'root' });
|
|
307
|
+
}
|
|
308
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusLanguage, decorators: [{
|
|
309
|
+
type: Injectable,
|
|
310
|
+
args: [{ providedIn: 'root' }]
|
|
311
|
+
}], ctorParameters: () => [] });
|
|
312
|
+
|
|
313
|
+
const SPOKEN = [400, 402, 403, 404, 409, 422];
|
|
314
|
+
function problemOf(error) {
|
|
315
|
+
const body = error.error;
|
|
316
|
+
return body !== null && typeof body === 'object' ? body : null;
|
|
317
|
+
}
|
|
318
|
+
function fieldErrorsOf(problem) {
|
|
319
|
+
const groups = problem?.errors;
|
|
320
|
+
if (!groups)
|
|
321
|
+
return '';
|
|
322
|
+
return Object.values(groups)
|
|
323
|
+
.flat()
|
|
324
|
+
.map((message) => message.trim())
|
|
325
|
+
.filter(Boolean)
|
|
326
|
+
.join(' ');
|
|
327
|
+
}
|
|
328
|
+
function nucleusErrorMessage(error, lang) {
|
|
329
|
+
const copy = commonCopyOf(lang ?? nucleusLang());
|
|
330
|
+
if (!(error instanceof HttpErrorResponse))
|
|
331
|
+
return copy.errorUnknown;
|
|
332
|
+
const status = error.status;
|
|
333
|
+
if (status === 0)
|
|
334
|
+
return copy.errorNetwork;
|
|
335
|
+
if (status === 401)
|
|
336
|
+
return copy.sessionExpired;
|
|
337
|
+
if (status === 408 || status === 504)
|
|
338
|
+
return copy.errorTimeout;
|
|
339
|
+
if (SPOKEN.includes(status)) {
|
|
340
|
+
const problem = problemOf(error);
|
|
341
|
+
const fields = fieldErrorsOf(problem);
|
|
342
|
+
if (fields)
|
|
343
|
+
return fields;
|
|
344
|
+
const detail = problem?.detail?.trim() || problem?.title?.trim();
|
|
345
|
+
if (detail)
|
|
346
|
+
return detail;
|
|
347
|
+
}
|
|
348
|
+
if (status === 400 || status === 422)
|
|
349
|
+
return copy.errorInvalid;
|
|
350
|
+
if (status === 403)
|
|
351
|
+
return copy.errorForbidden;
|
|
352
|
+
if (status === 404)
|
|
353
|
+
return copy.errorNotFound;
|
|
354
|
+
if (status === 409)
|
|
355
|
+
return copy.errorConflict;
|
|
356
|
+
if (status >= 500)
|
|
357
|
+
return copy.errorServer;
|
|
358
|
+
return copy.errorUnknown;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const DEFAULT_TTL_MS = 30_000;
|
|
362
|
+
class NucleusCache {
|
|
363
|
+
entries = new Map();
|
|
364
|
+
inFlight = new Map();
|
|
365
|
+
read(key, loader, ttlMs = DEFAULT_TTL_MS) {
|
|
366
|
+
const entry = this.entries.get(key);
|
|
367
|
+
if (entry && Date.now() - entry.at < ttlMs) {
|
|
368
|
+
return Promise.resolve(entry.value);
|
|
369
|
+
}
|
|
370
|
+
return this.load(key, loader);
|
|
371
|
+
}
|
|
372
|
+
prefetch(key, loader, ttlMs = DEFAULT_TTL_MS) {
|
|
373
|
+
const entry = this.entries.get(key);
|
|
374
|
+
if (entry && Date.now() - entry.at < ttlMs)
|
|
375
|
+
return;
|
|
376
|
+
if (this.inFlight.has(key))
|
|
377
|
+
return;
|
|
378
|
+
void this.load(key, loader).catch(() => undefined);
|
|
379
|
+
}
|
|
380
|
+
invalidate(...prefixes) {
|
|
381
|
+
if (prefixes.length === 0) {
|
|
382
|
+
this.entries.clear();
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
for (const key of [...this.entries.keys()]) {
|
|
386
|
+
if (prefixes.some((prefix) => key.startsWith(prefix)))
|
|
387
|
+
this.entries.delete(key);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
load(key, loader) {
|
|
391
|
+
const running = this.inFlight.get(key);
|
|
392
|
+
if (running)
|
|
393
|
+
return running;
|
|
394
|
+
const promise = loader()
|
|
395
|
+
.then((value) => {
|
|
396
|
+
this.entries.set(key, { at: Date.now(), value });
|
|
397
|
+
return value;
|
|
398
|
+
})
|
|
399
|
+
.finally(() => {
|
|
400
|
+
this.inFlight.delete(key);
|
|
401
|
+
});
|
|
402
|
+
this.inFlight.set(key, promise);
|
|
403
|
+
return promise;
|
|
404
|
+
}
|
|
405
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCache, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
406
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCache, providedIn: 'root' });
|
|
407
|
+
}
|
|
408
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCache, decorators: [{
|
|
409
|
+
type: Injectable,
|
|
410
|
+
args: [{ providedIn: 'root' }]
|
|
411
|
+
}] });
|
|
412
|
+
|
|
228
413
|
const NO_PERMISSIONS = {
|
|
229
414
|
canView: false,
|
|
230
415
|
canInsert: false,
|
|
@@ -263,8 +448,10 @@ class SessionService {
|
|
|
263
448
|
_me = signal(null, ...(ngDevMode ? [{ debugName: "_me" }] : /* istanbul ignore next */ []));
|
|
264
449
|
_menu = signal([], ...(ngDevMode ? [{ debugName: "_menu" }] : /* istanbul ignore next */ []));
|
|
265
450
|
_access = signal(null, ...(ngDevMode ? [{ debugName: "_access" }] : /* istanbul ignore next */ []));
|
|
451
|
+
_globalRole = signal(null, ...(ngDevMode ? [{ debugName: "_globalRole" }] : /* istanbul ignore next */ []));
|
|
266
452
|
_scope = signal(null, ...(ngDevMode ? [{ debugName: "_scope" }] : /* istanbul ignore next */ []));
|
|
267
453
|
_apps = signal([], ...(ngDevMode ? [{ debugName: "_apps" }] : /* istanbul ignore next */ []));
|
|
454
|
+
_catalog = signal([], ...(ngDevMode ? [{ debugName: "_catalog" }] : /* istanbul ignore next */ []));
|
|
268
455
|
_siteId = signal(null, ...(ngDevMode ? [{ debugName: "_siteId" }] : /* istanbul ignore next */ []));
|
|
269
456
|
_loading = signal(false, ...(ngDevMode ? [{ debugName: "_loading" }] : /* istanbul ignore next */ []));
|
|
270
457
|
_switching = signal(false, ...(ngDevMode ? [{ debugName: "_switching" }] : /* istanbul ignore next */ []));
|
|
@@ -277,11 +464,13 @@ class SessionService {
|
|
|
277
464
|
access = this._access.asReadonly();
|
|
278
465
|
scope = this._scope.asReadonly();
|
|
279
466
|
apps = this._apps.asReadonly();
|
|
467
|
+
catalog = this._catalog.asReadonly();
|
|
468
|
+
currentApp = computed(() => this._catalog().find((app) => app.id.toLowerCase() === this.config.appId) ?? null, ...(ngDevMode ? [{ debugName: "currentApp" }] : /* istanbul ignore next */ []));
|
|
280
469
|
siteId = this._siteId.asReadonly();
|
|
281
470
|
loading = this._loading.asReadonly();
|
|
282
471
|
switching = this._switching.asReadonly();
|
|
283
472
|
error = this._error.asReadonly();
|
|
284
|
-
roleName = computed(() => this._access()?.roleName ??
|
|
473
|
+
roleName = computed(() => this._access()?.roleName ?? this._globalRole(), ...(ngDevMode ? [{ debugName: "roleName" }] : /* istanbul ignore next */ []));
|
|
285
474
|
siteName = computed(() => this._access()?.siteName ?? null, ...(ngDevMode ? [{ debugName: "siteName" }] : /* istanbul ignore next */ []));
|
|
286
475
|
companies = computed(() => {
|
|
287
476
|
const me = this._me();
|
|
@@ -337,12 +526,17 @@ class SessionService {
|
|
|
337
526
|
firstValueFrom(this.http.get(`${this.config.securityApiUrl}/apps`)),
|
|
338
527
|
]);
|
|
339
528
|
this._me.set(me);
|
|
529
|
+
this._catalog.set([...catalog].sort((a, b) => a.sortOrder - b.sortOrder));
|
|
340
530
|
this._apps.set(this.buildLauncher(catalog));
|
|
341
531
|
await this.resolveMenu(me, language);
|
|
342
532
|
}
|
|
343
533
|
catch (error) {
|
|
344
534
|
this._menu.set([]);
|
|
345
|
-
|
|
535
|
+
if (isSessionRejected(error)) {
|
|
536
|
+
await this.signOutToLanding();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
this._error.set(nucleusErrorMessage(error, this.language));
|
|
346
540
|
console.error('[session] no se pudo cargar la sesión del backend', error);
|
|
347
541
|
}
|
|
348
542
|
finally {
|
|
@@ -350,6 +544,22 @@ class SessionService {
|
|
|
350
544
|
this._loading.set(false);
|
|
351
545
|
}
|
|
352
546
|
}
|
|
547
|
+
async signOutToLanding() {
|
|
548
|
+
const landing = this.config.landingUrl;
|
|
549
|
+
this.loadedKey = null;
|
|
550
|
+
this._me.set(null);
|
|
551
|
+
this._access.set(null);
|
|
552
|
+
this._globalRole.set(null);
|
|
553
|
+
this._apps.set([]);
|
|
554
|
+
this._catalog.set([]);
|
|
555
|
+
if (!landing) {
|
|
556
|
+
this._error.set(commonCopyOf(this.language).sessionExpired);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
await this.sessionAuth.clear();
|
|
560
|
+
await this.auth?.signOut?.();
|
|
561
|
+
window.location.href = nucleusLogoutUrl(landing);
|
|
562
|
+
}
|
|
353
563
|
permissionsFor(route) {
|
|
354
564
|
return permissionsOf(findMenuByRoute(this._menu(), route));
|
|
355
565
|
}
|
|
@@ -386,7 +596,11 @@ class SessionService {
|
|
|
386
596
|
}
|
|
387
597
|
}
|
|
388
598
|
catch (error) {
|
|
389
|
-
|
|
599
|
+
if (isSessionRejected(error)) {
|
|
600
|
+
await this.signOutToLanding();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
this._error.set(nucleusErrorMessage(error, this.language));
|
|
390
604
|
console.error('[session] no se pudo cambiar de sede', error);
|
|
391
605
|
}
|
|
392
606
|
finally {
|
|
@@ -401,12 +615,15 @@ class SessionService {
|
|
|
401
615
|
const access = entries.find((entry) => entry.siteId === wanted) ?? entries[0] ?? null;
|
|
402
616
|
if (access) {
|
|
403
617
|
this._access.set(access);
|
|
618
|
+
this._globalRole.set(null);
|
|
404
619
|
this._siteId.set(access.siteId);
|
|
405
620
|
this._scope.set('site');
|
|
406
621
|
this._menu.set(await this.siteMenu(access.siteId, language));
|
|
407
622
|
return;
|
|
408
623
|
}
|
|
624
|
+
const global = (me.globalAccess ?? []).find((entry) => entry.appId.toLowerCase() === this.config.appId);
|
|
409
625
|
this._access.set(null);
|
|
626
|
+
this._globalRole.set(global?.roleName ?? null);
|
|
410
627
|
this._siteId.set(wanted);
|
|
411
628
|
this._scope.set('account');
|
|
412
629
|
this._menu.set(await this.accountMenu(language));
|
|
@@ -439,52 +656,375 @@ function toOption(company, relation) {
|
|
|
439
656
|
relation,
|
|
440
657
|
};
|
|
441
658
|
}
|
|
442
|
-
function
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
659
|
+
function isSessionRejected(error) {
|
|
660
|
+
return error instanceof HttpErrorResponse && (error.status === 401 || error.status === 403);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const DATE_LOCALES = {
|
|
664
|
+
es: 'es-PE',
|
|
665
|
+
en: 'en-US',
|
|
666
|
+
};
|
|
667
|
+
const NUMBER_LOCALE = 'es-PE';
|
|
668
|
+
const DAY_MS = 86400000;
|
|
669
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
670
|
+
const MONEY = new Intl.NumberFormat(NUMBER_LOCALE, {
|
|
671
|
+
minimumFractionDigits: 2,
|
|
672
|
+
maximumFractionDigits: 2,
|
|
673
|
+
});
|
|
674
|
+
const QTY = new Intl.NumberFormat(NUMBER_LOCALE, { maximumFractionDigits: 4 });
|
|
675
|
+
const INTEGER = new Intl.NumberFormat(NUMBER_LOCALE, { maximumFractionDigits: 0 });
|
|
676
|
+
const dates = new Map();
|
|
677
|
+
function formatter(kind) {
|
|
678
|
+
const locale = DATE_LOCALES[nucleusLang()];
|
|
679
|
+
const key = `${kind}:${locale}`;
|
|
680
|
+
const cached = dates.get(key);
|
|
681
|
+
if (cached)
|
|
682
|
+
return cached;
|
|
683
|
+
const options = kind === 'date'
|
|
684
|
+
? { day: '2-digit', month: 'short', year: 'numeric' }
|
|
685
|
+
: kind === 'stamp'
|
|
686
|
+
? { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }
|
|
687
|
+
: { hour: '2-digit', minute: '2-digit' };
|
|
688
|
+
const built = new Intl.DateTimeFormat(locale, options);
|
|
689
|
+
dates.set(key, built);
|
|
690
|
+
return built;
|
|
691
|
+
}
|
|
692
|
+
function nucleusParseDate(value) {
|
|
693
|
+
if (!value)
|
|
694
|
+
return null;
|
|
695
|
+
if (DATE_ONLY.test(value)) {
|
|
696
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
697
|
+
return new Date(year, month - 1, day);
|
|
698
|
+
}
|
|
699
|
+
const parsed = new Date(value);
|
|
700
|
+
return Number.isNaN(parsed.valueOf()) ? null : parsed;
|
|
701
|
+
}
|
|
702
|
+
function money(value) {
|
|
703
|
+
return value === null || value === undefined ? '' : MONEY.format(value);
|
|
704
|
+
}
|
|
705
|
+
function signedMoney(value) {
|
|
706
|
+
if (value === null || value === undefined)
|
|
707
|
+
return '';
|
|
708
|
+
return value > 0 ? `+${MONEY.format(value)}` : MONEY.format(value);
|
|
709
|
+
}
|
|
710
|
+
function qty(value) {
|
|
711
|
+
return value === null || value === undefined ? '' : QTY.format(value);
|
|
712
|
+
}
|
|
713
|
+
function integer(value) {
|
|
714
|
+
return value === null || value === undefined ? '' : INTEGER.format(value);
|
|
715
|
+
}
|
|
716
|
+
function percent(value) {
|
|
717
|
+
return value === null || value === undefined ? '' : `${MONEY.format(value)}%`;
|
|
718
|
+
}
|
|
719
|
+
function day(value) {
|
|
720
|
+
const parsed = nucleusParseDate(value);
|
|
721
|
+
return parsed ? formatter('date').format(parsed) : '';
|
|
722
|
+
}
|
|
723
|
+
function stamp(value) {
|
|
724
|
+
const parsed = nucleusParseDate(value);
|
|
725
|
+
return parsed ? formatter('stamp').format(parsed) : '';
|
|
726
|
+
}
|
|
727
|
+
function clock(value) {
|
|
728
|
+
const parsed = nucleusParseDate(value);
|
|
729
|
+
return parsed ? formatter('clock').format(parsed) : '';
|
|
730
|
+
}
|
|
731
|
+
function minutesAsClock(value) {
|
|
732
|
+
if (value === null || value === undefined)
|
|
733
|
+
return '';
|
|
734
|
+
const total = Math.max(Math.trunc(value), 0);
|
|
735
|
+
const hours = Math.trunc(total / 60);
|
|
736
|
+
const minutes = total % 60;
|
|
737
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
738
|
+
}
|
|
739
|
+
function isoDay(value) {
|
|
740
|
+
const parsed = typeof value === 'string' || value === null || value === undefined
|
|
741
|
+
? nucleusParseDate(value)
|
|
742
|
+
: value;
|
|
743
|
+
if (!parsed)
|
|
744
|
+
return '';
|
|
745
|
+
const year = parsed.getFullYear();
|
|
746
|
+
const month = String(parsed.getMonth() + 1).padStart(2, '0');
|
|
747
|
+
const date = String(parsed.getDate()).padStart(2, '0');
|
|
748
|
+
return `${year}-${month}-${date}`;
|
|
749
|
+
}
|
|
750
|
+
function toNumber(value) {
|
|
751
|
+
const parsed = typeof value === 'number' ? value : Number(value);
|
|
752
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
753
|
+
}
|
|
754
|
+
function shiftDay(value, days) {
|
|
755
|
+
const parsed = nucleusParseDate(value);
|
|
756
|
+
return parsed ? isoDay(new Date(parsed.valueOf() + days * DAY_MS)) : '';
|
|
757
|
+
}
|
|
758
|
+
function isToday(value) {
|
|
759
|
+
const parsed = nucleusParseDate(value);
|
|
760
|
+
return parsed ? isoDay(parsed) === isoDay(new Date()) : false;
|
|
761
|
+
}
|
|
762
|
+
function isBeforeToday(value) {
|
|
763
|
+
const parsed = nucleusParseDate(value);
|
|
764
|
+
return parsed ? isoDay(parsed) < isoDay(new Date()) : false;
|
|
765
|
+
}
|
|
766
|
+
function isOnOrBeforeToday(value) {
|
|
767
|
+
const parsed = nucleusParseDate(value);
|
|
768
|
+
return parsed ? isoDay(parsed) <= isoDay(new Date()) : false;
|
|
769
|
+
}
|
|
770
|
+
function startOfDayUtc(value) {
|
|
771
|
+
return `${isoDay(nucleusParseDate(value))}T00:00:00.000Z`;
|
|
772
|
+
}
|
|
773
|
+
function endOfDayUtc(value) {
|
|
774
|
+
return `${isoDay(nucleusParseDate(value))}T23:59:59.999Z`;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const FIELD_TARGETS = [
|
|
778
|
+
'input:not([type="hidden"]):not([disabled])',
|
|
779
|
+
'textarea:not([disabled])',
|
|
780
|
+
'select:not([disabled])',
|
|
781
|
+
'[role="combobox"]:not([aria-disabled="true"])',
|
|
782
|
+
].join(',');
|
|
783
|
+
const TEXTUAL = ['text', 'email', 'password', 'number', 'search', 'tel', 'url'];
|
|
784
|
+
function isReachable(element) {
|
|
785
|
+
return element.tabIndex >= 0 && element.offsetParent !== null;
|
|
786
|
+
}
|
|
787
|
+
function fieldsIn(root) {
|
|
788
|
+
return Array.from(root.querySelectorAll(FIELD_TARGETS)).filter(isReachable);
|
|
789
|
+
}
|
|
790
|
+
function moveTo(element) {
|
|
791
|
+
element.focus({ preventScroll: true });
|
|
792
|
+
if (element instanceof HTMLInputElement && TEXTUAL.includes(element.type)) {
|
|
793
|
+
element.select();
|
|
446
794
|
}
|
|
447
|
-
return 'Error desconocido';
|
|
448
795
|
}
|
|
449
796
|
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
797
|
+
const MODIFIERS = ['ctrl', 'alt', 'shift'];
|
|
798
|
+
const ALIASES = {
|
|
799
|
+
cmd: 'ctrl',
|
|
800
|
+
meta: 'ctrl',
|
|
801
|
+
command: 'ctrl',
|
|
802
|
+
control: 'ctrl',
|
|
803
|
+
option: 'alt',
|
|
804
|
+
esc: 'escape',
|
|
805
|
+
' ': 'space',
|
|
806
|
+
};
|
|
807
|
+
function normalizeKeys(keys) {
|
|
808
|
+
const parts = keys
|
|
809
|
+
.toLowerCase()
|
|
810
|
+
.split('+')
|
|
811
|
+
.map((part) => part.trim())
|
|
812
|
+
.filter(Boolean)
|
|
813
|
+
.map((part) => ALIASES[part] ?? part);
|
|
814
|
+
const modifiers = MODIFIERS.filter((modifier) => parts.includes(modifier));
|
|
815
|
+
const rest = parts.filter((part) => !MODIFIERS.includes(part));
|
|
816
|
+
return [...modifiers, ...rest].join('+');
|
|
454
817
|
}
|
|
455
|
-
function
|
|
818
|
+
function comboOf(event) {
|
|
819
|
+
const key = event.key.toLowerCase();
|
|
820
|
+
const parts = [];
|
|
821
|
+
if (event.ctrlKey || event.metaKey)
|
|
822
|
+
parts.push('ctrl');
|
|
823
|
+
if (event.altKey)
|
|
824
|
+
parts.push('alt');
|
|
825
|
+
if (event.shiftKey && key.length > 1)
|
|
826
|
+
parts.push('shift');
|
|
827
|
+
parts.push(ALIASES[key] ?? key);
|
|
828
|
+
return parts.join('+');
|
|
829
|
+
}
|
|
830
|
+
function hasModifier(keys) {
|
|
831
|
+
return keys.startsWith('ctrl+') || keys.startsWith('alt+');
|
|
832
|
+
}
|
|
833
|
+
function isTypingTarget(target) {
|
|
834
|
+
const element = target;
|
|
835
|
+
if (!element || typeof element.tagName !== 'string')
|
|
836
|
+
return false;
|
|
837
|
+
if (element.isContentEditable)
|
|
838
|
+
return true;
|
|
839
|
+
return ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName);
|
|
840
|
+
}
|
|
841
|
+
function isApplePlatform() {
|
|
842
|
+
if (typeof navigator === 'undefined')
|
|
843
|
+
return false;
|
|
844
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);
|
|
845
|
+
}
|
|
846
|
+
const DISPLAY = {
|
|
847
|
+
escape: 'Esc',
|
|
848
|
+
enter: 'Enter',
|
|
849
|
+
space: 'Espacio',
|
|
850
|
+
arrowup: '↑',
|
|
851
|
+
arrowdown: '↓',
|
|
852
|
+
arrowleft: '←',
|
|
853
|
+
arrowright: '→',
|
|
854
|
+
};
|
|
855
|
+
function shortcutHint(keys) {
|
|
856
|
+
const apple = isApplePlatform();
|
|
857
|
+
return normalizeKeys(keys)
|
|
858
|
+
.split('+')
|
|
859
|
+
.map((part) => {
|
|
860
|
+
if (part === 'ctrl')
|
|
861
|
+
return apple ? '⌘' : 'Ctrl';
|
|
862
|
+
if (part === 'alt')
|
|
863
|
+
return apple ? '⌥' : 'Alt';
|
|
864
|
+
if (part === 'shift')
|
|
865
|
+
return apple ? '⇧' : 'Shift';
|
|
866
|
+
return DISPLAY[part] ?? part.toUpperCase();
|
|
867
|
+
})
|
|
868
|
+
.join(apple ? '' : '+');
|
|
869
|
+
}
|
|
870
|
+
const ARIA = {
|
|
871
|
+
ctrl: 'Control',
|
|
872
|
+
alt: 'Alt',
|
|
873
|
+
shift: 'Shift',
|
|
874
|
+
escape: 'Escape',
|
|
875
|
+
enter: 'Enter',
|
|
876
|
+
space: 'Space',
|
|
877
|
+
};
|
|
878
|
+
function ariaKeysOf(keys) {
|
|
879
|
+
return normalizeKeys(keys)
|
|
880
|
+
.split('+')
|
|
881
|
+
.map((part) => ARIA[part] ?? part.toUpperCase())
|
|
882
|
+
.join('+');
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const MODAL_SELECTOR = '.p-drawer-mask, .p-dialog-mask, .p-overlay-mask';
|
|
886
|
+
const STORAGE_KEY$2 = 'nucleus_shortcuts';
|
|
887
|
+
function readStored$1() {
|
|
456
888
|
try {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
889
|
+
return localStorage.getItem(STORAGE_KEY$2) !== 'off';
|
|
890
|
+
}
|
|
891
|
+
catch {
|
|
892
|
+
return true;
|
|
460
893
|
}
|
|
461
|
-
catch { }
|
|
462
|
-
return 'es';
|
|
463
894
|
}
|
|
464
|
-
class
|
|
465
|
-
|
|
466
|
-
|
|
895
|
+
class NucleusShortcuts {
|
|
896
|
+
document = inject(DOCUMENT);
|
|
897
|
+
_bindings = signal([], ...(ngDevMode ? [{ debugName: "_bindings" }] : /* istanbul ignore next */ []));
|
|
898
|
+
_enabled = signal(readStored$1(), ...(ngDevMode ? [{ debugName: "_enabled" }] : /* istanbul ignore next */ []));
|
|
899
|
+
bindings = this._bindings.asReadonly();
|
|
900
|
+
enabled = this._enabled.asReadonly();
|
|
467
901
|
constructor() {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
try {
|
|
472
|
-
localStorage.setItem(STORAGE_KEY$1, lang);
|
|
473
|
-
}
|
|
474
|
-
catch { }
|
|
475
|
-
});
|
|
902
|
+
const listener = (event) => this.handle(event);
|
|
903
|
+
this.document.addEventListener('keydown', listener);
|
|
904
|
+
inject(DestroyRef).onDestroy(() => this.document.removeEventListener('keydown', listener));
|
|
476
905
|
}
|
|
477
|
-
|
|
478
|
-
this.
|
|
906
|
+
setEnabled(enabled) {
|
|
907
|
+
this._enabled.set(enabled);
|
|
908
|
+
try {
|
|
909
|
+
localStorage.setItem(STORAGE_KEY$2, enabled ? 'on' : 'off');
|
|
910
|
+
}
|
|
911
|
+
catch { }
|
|
479
912
|
}
|
|
480
|
-
|
|
481
|
-
|
|
913
|
+
register(binding) {
|
|
914
|
+
this._bindings.update((list) => [...list, binding]);
|
|
915
|
+
return () => this._bindings.update((list) => list.filter((entry) => entry !== binding));
|
|
916
|
+
}
|
|
917
|
+
handle(event) {
|
|
918
|
+
if (!this._enabled() || event.isComposing || event.repeat)
|
|
919
|
+
return;
|
|
920
|
+
const combo = comboOf(event);
|
|
921
|
+
const typing = isTypingTarget(event.target);
|
|
922
|
+
const blocked = this.document.querySelector(MODAL_SELECTOR) !== null;
|
|
923
|
+
const bindings = this._bindings();
|
|
924
|
+
for (let index = bindings.length - 1; index >= 0; index -= 1) {
|
|
925
|
+
const binding = bindings[index];
|
|
926
|
+
if (!binding || binding.keys !== combo)
|
|
927
|
+
continue;
|
|
928
|
+
if (typing && !binding.whileTyping)
|
|
929
|
+
continue;
|
|
930
|
+
if (blocked && !hasModifier(binding.keys))
|
|
931
|
+
continue;
|
|
932
|
+
event.preventDefault();
|
|
933
|
+
binding.run();
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShortcuts, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
938
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShortcuts, providedIn: 'root' });
|
|
482
939
|
}
|
|
483
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type:
|
|
940
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShortcuts, decorators: [{
|
|
484
941
|
type: Injectable,
|
|
485
942
|
args: [{ providedIn: 'root' }]
|
|
486
943
|
}], ctorParameters: () => [] });
|
|
487
944
|
|
|
945
|
+
const CLICKABLE = 'button, a, input';
|
|
946
|
+
class NucleusShortcut {
|
|
947
|
+
host = inject(ElementRef);
|
|
948
|
+
shortcuts = inject(NucleusShortcuts);
|
|
949
|
+
nucleusShortcut = input.required(...(ngDevMode ? [{ debugName: "nucleusShortcut" }] : /* istanbul ignore next */ []));
|
|
950
|
+
shortcutLabel = input('', ...(ngDevMode ? [{ debugName: "shortcutLabel" }] : /* istanbul ignore next */ []));
|
|
951
|
+
shortcutWhileTyping = input(null, ...(ngDevMode ? [{ debugName: "shortcutWhileTyping" }] : /* istanbul ignore next */ []));
|
|
952
|
+
constructor() {
|
|
953
|
+
effect((onCleanup) => {
|
|
954
|
+
const keys = normalizeKeys(this.nucleusShortcut());
|
|
955
|
+
const label = this.shortcutLabel();
|
|
956
|
+
const whileTyping = this.shortcutWhileTyping() ?? hasModifier(keys);
|
|
957
|
+
this.host.nativeElement.setAttribute('aria-keyshortcuts', ariaKeysOf(keys));
|
|
958
|
+
const dispose = this.shortcuts.register({
|
|
959
|
+
keys,
|
|
960
|
+
label,
|
|
961
|
+
whileTyping,
|
|
962
|
+
run: () => this.press(),
|
|
963
|
+
});
|
|
964
|
+
onCleanup(dispose);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
press() {
|
|
968
|
+
const element = this.host.nativeElement;
|
|
969
|
+
const target = element.matches(CLICKABLE)
|
|
970
|
+
? element
|
|
971
|
+
: element.querySelector(CLICKABLE);
|
|
972
|
+
if (!target || target.hasAttribute('disabled'))
|
|
973
|
+
return;
|
|
974
|
+
target.click();
|
|
975
|
+
}
|
|
976
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShortcut, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
977
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.22", type: NucleusShortcut, isStandalone: true, selector: "[nucleusShortcut]", inputs: { nucleusShortcut: { classPropertyName: "nucleusShortcut", publicName: "nucleusShortcut", isSignal: true, isRequired: true, transformFunction: null }, shortcutLabel: { classPropertyName: "shortcutLabel", publicName: "shortcutLabel", isSignal: true, isRequired: false, transformFunction: null }, shortcutWhileTyping: { classPropertyName: "shortcutWhileTyping", publicName: "shortcutWhileTyping", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
978
|
+
}
|
|
979
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShortcut, decorators: [{
|
|
980
|
+
type: Directive,
|
|
981
|
+
args: [{
|
|
982
|
+
selector: '[nucleusShortcut]',
|
|
983
|
+
}]
|
|
984
|
+
}], ctorParameters: () => [], propDecorators: { nucleusShortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "nucleusShortcut", required: true }] }], shortcutLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcutLabel", required: false }] }], shortcutWhileTyping: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcutWhileTyping", required: false }] }] } });
|
|
985
|
+
|
|
986
|
+
const SCOPES = 'nucleus-card, form, .p-drawer-content';
|
|
987
|
+
const ACTIONS = '.actions button:not([disabled]), .drawer-actions button:not([disabled])';
|
|
988
|
+
class NucleusEnterAdvance {
|
|
989
|
+
host = inject(ElementRef);
|
|
990
|
+
onKeyDown(event) {
|
|
991
|
+
if (event.key !== 'Enter' || event.isComposing)
|
|
992
|
+
return;
|
|
993
|
+
if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey)
|
|
994
|
+
return;
|
|
995
|
+
const source = event.target;
|
|
996
|
+
if (!source || source.tagName === 'TEXTAREA')
|
|
997
|
+
return;
|
|
998
|
+
if (source.getAttribute('aria-expanded') === 'true')
|
|
999
|
+
return;
|
|
1000
|
+
const scope = this.scope();
|
|
1001
|
+
const targets = fieldsIn(scope);
|
|
1002
|
+
const index = targets.indexOf(source);
|
|
1003
|
+
if (index === -1)
|
|
1004
|
+
return;
|
|
1005
|
+
event.preventDefault();
|
|
1006
|
+
const next = targets[index + 1];
|
|
1007
|
+
if (next) {
|
|
1008
|
+
moveTo(next);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
scope.querySelector(ACTIONS)?.focus();
|
|
1012
|
+
}
|
|
1013
|
+
scope() {
|
|
1014
|
+
const element = this.host.nativeElement;
|
|
1015
|
+
return element.closest(SCOPES) ?? element;
|
|
1016
|
+
}
|
|
1017
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusEnterAdvance, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1018
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.22", type: NucleusEnterAdvance, isStandalone: true, selector: "[nucleusEnterAdvance]", host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0 });
|
|
1019
|
+
}
|
|
1020
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusEnterAdvance, decorators: [{
|
|
1021
|
+
type: Directive,
|
|
1022
|
+
args: [{
|
|
1023
|
+
selector: '[nucleusEnterAdvance]',
|
|
1024
|
+
host: { '(keydown)': 'onKeyDown($event)' },
|
|
1025
|
+
}]
|
|
1026
|
+
}] });
|
|
1027
|
+
|
|
488
1028
|
const EMPTY_GEO_VALUE = { countryId: null, divisionId: null };
|
|
489
1029
|
function flagOf(code) {
|
|
490
1030
|
if (code.length !== 2)
|
|
@@ -495,8 +1035,6 @@ function flagOf(code) {
|
|
|
495
1035
|
const GEO_COPY = {
|
|
496
1036
|
es: {
|
|
497
1037
|
country: 'País',
|
|
498
|
-
choose: 'Selecciona',
|
|
499
|
-
search: 'Buscar',
|
|
500
1038
|
levels: ['Región', 'Provincia', 'Distrito', 'Zona'],
|
|
501
1039
|
kinds: {
|
|
502
1040
|
department: 'Departamento',
|
|
@@ -525,8 +1063,6 @@ const GEO_COPY = {
|
|
|
525
1063
|
},
|
|
526
1064
|
en: {
|
|
527
1065
|
country: 'Country',
|
|
528
|
-
choose: 'Select',
|
|
529
|
-
search: 'Search',
|
|
530
1066
|
levels: ['Region', 'Province', 'District', 'Area'],
|
|
531
1067
|
kinds: {
|
|
532
1068
|
department: 'Department',
|
|
@@ -622,6 +1158,7 @@ class NucleusGeoSelect {
|
|
|
622
1158
|
countries = signal([], ...(ngDevMode ? [{ debugName: "countries" }] : /* istanbul ignore next */ []));
|
|
623
1159
|
steps = signal([], ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
624
1160
|
labels = computed(() => geoCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
1161
|
+
words = computed(() => commonCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "words" }] : /* istanbul ignore next */ []));
|
|
625
1162
|
syncedKey = null;
|
|
626
1163
|
constructor() {
|
|
627
1164
|
effect(() => {
|
|
@@ -701,8 +1238,9 @@ class NucleusGeoSelect {
|
|
|
701
1238
|
this.steps.set(built);
|
|
702
1239
|
}
|
|
703
1240
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeoSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
704
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusGeoSelect, isStandalone: true, selector: "nucleus-geo-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: `
|
|
1241
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusGeoSelect, isStandalone: true, selector: "nucleus-geo-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, hostDirectives: [{ directive: NucleusEnterAdvance }], ngImport: i0, template: `
|
|
705
1242
|
@let copy = labels();
|
|
1243
|
+
@let shared = words();
|
|
706
1244
|
|
|
707
1245
|
<div class="ns-form-grid" [class]="gridClass()">
|
|
708
1246
|
<div class="ns-field is-span-1 is-size-md">
|
|
@@ -717,9 +1255,11 @@ class NucleusGeoSelect {
|
|
|
717
1255
|
[disabled]="readonly()"
|
|
718
1256
|
[filter]="true"
|
|
719
1257
|
filterBy="name"
|
|
720
|
-
[filterPlaceholder]="
|
|
1258
|
+
[filterPlaceholder]="shared.search"
|
|
1259
|
+
[autofocusFilter]="true"
|
|
1260
|
+
[resetFilterOnHide]="true"
|
|
721
1261
|
[showClear]="true"
|
|
722
|
-
[placeholder]="
|
|
1262
|
+
[placeholder]="shared.choose"
|
|
723
1263
|
[ngModelOptions]="{ standalone: true }"
|
|
724
1264
|
fluid
|
|
725
1265
|
/>
|
|
@@ -736,23 +1276,26 @@ class NucleusGeoSelect {
|
|
|
736
1276
|
[ngModel]="step.selectedId"
|
|
737
1277
|
(ngModelChange)="pick(index, $event)"
|
|
738
1278
|
[disabled]="readonly()"
|
|
739
|
-
[filter]="
|
|
1279
|
+
[filter]="true"
|
|
740
1280
|
filterBy="name"
|
|
741
|
-
[filterPlaceholder]="
|
|
1281
|
+
[filterPlaceholder]="shared.search"
|
|
1282
|
+
[autofocusFilter]="true"
|
|
1283
|
+
[resetFilterOnHide]="true"
|
|
742
1284
|
[showClear]="true"
|
|
743
|
-
[placeholder]="
|
|
1285
|
+
[placeholder]="shared.choose"
|
|
744
1286
|
[ngModelOptions]="{ standalone: true }"
|
|
745
1287
|
fluid
|
|
746
1288
|
/>
|
|
747
1289
|
</div>
|
|
748
1290
|
}
|
|
749
1291
|
</div>
|
|
750
|
-
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: SelectModule }, { kind: "component", type:
|
|
1292
|
+
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
751
1293
|
}
|
|
752
1294
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeoSelect, decorators: [{
|
|
753
1295
|
type: Component,
|
|
754
|
-
args: [{ selector: 'nucleus-geo-select', imports: [SelectModule, FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1296
|
+
args: [{ selector: 'nucleus-geo-select', imports: [SelectModule, FormsModule], hostDirectives: [NucleusEnterAdvance], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
755
1297
|
@let copy = labels();
|
|
1298
|
+
@let shared = words();
|
|
756
1299
|
|
|
757
1300
|
<div class="ns-form-grid" [class]="gridClass()">
|
|
758
1301
|
<div class="ns-field is-span-1 is-size-md">
|
|
@@ -767,9 +1310,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
767
1310
|
[disabled]="readonly()"
|
|
768
1311
|
[filter]="true"
|
|
769
1312
|
filterBy="name"
|
|
770
|
-
[filterPlaceholder]="
|
|
1313
|
+
[filterPlaceholder]="shared.search"
|
|
1314
|
+
[autofocusFilter]="true"
|
|
1315
|
+
[resetFilterOnHide]="true"
|
|
771
1316
|
[showClear]="true"
|
|
772
|
-
[placeholder]="
|
|
1317
|
+
[placeholder]="shared.choose"
|
|
773
1318
|
[ngModelOptions]="{ standalone: true }"
|
|
774
1319
|
fluid
|
|
775
1320
|
/>
|
|
@@ -786,11 +1331,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
786
1331
|
[ngModel]="step.selectedId"
|
|
787
1332
|
(ngModelChange)="pick(index, $event)"
|
|
788
1333
|
[disabled]="readonly()"
|
|
789
|
-
[filter]="
|
|
1334
|
+
[filter]="true"
|
|
790
1335
|
filterBy="name"
|
|
791
|
-
[filterPlaceholder]="
|
|
1336
|
+
[filterPlaceholder]="shared.search"
|
|
1337
|
+
[autofocusFilter]="true"
|
|
1338
|
+
[resetFilterOnHide]="true"
|
|
792
1339
|
[showClear]="true"
|
|
793
|
-
[placeholder]="
|
|
1340
|
+
[placeholder]="shared.choose"
|
|
794
1341
|
[ngModelOptions]="{ standalone: true }"
|
|
795
1342
|
fluid
|
|
796
1343
|
/>
|
|
@@ -811,306 +1358,608 @@ function deepestOf(steps) {
|
|
|
811
1358
|
return null;
|
|
812
1359
|
}
|
|
813
1360
|
|
|
814
|
-
const
|
|
1361
|
+
const TEXT_CONTRAST = 4.5;
|
|
1362
|
+
const CONTROL_CONTRAST = 3;
|
|
1363
|
+
const DARK_INK = '#101014';
|
|
1364
|
+
const LIGHT_INK = '#ffffff';
|
|
1365
|
+
function relativeLuminance(hex) {
|
|
1366
|
+
const channel = (offset) => {
|
|
1367
|
+
const value = parseInt(hex.slice(offset, offset + 2), 16) / 255;
|
|
1368
|
+
return value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4);
|
|
1369
|
+
};
|
|
1370
|
+
return 0.2126 * channel(1) + 0.7152 * channel(3) + 0.0722 * channel(5);
|
|
1371
|
+
}
|
|
1372
|
+
function contrastRatio(foreground, background) {
|
|
1373
|
+
const first = relativeLuminance(foreground);
|
|
1374
|
+
const second = relativeLuminance(background);
|
|
1375
|
+
return (Math.max(first, second) + 0.05) / (Math.min(first, second) + 0.05);
|
|
1376
|
+
}
|
|
1377
|
+
function readableOn(background) {
|
|
1378
|
+
return contrastRatio(DARK_INK, background) >= contrastRatio(LIGHT_INK, background)
|
|
1379
|
+
? DARK_INK
|
|
1380
|
+
: LIGHT_INK;
|
|
1381
|
+
}
|
|
1382
|
+
function mixHex(foreground, background, ratio) {
|
|
1383
|
+
const amount = Math.min(Math.max(ratio, 0), 1);
|
|
1384
|
+
const channel = (offset) => {
|
|
1385
|
+
const front = parseInt(foreground.slice(offset, offset + 2), 16);
|
|
1386
|
+
const back = parseInt(background.slice(offset, offset + 2), 16);
|
|
1387
|
+
return Math.round(front * amount + back * (1 - amount))
|
|
1388
|
+
.toString(16)
|
|
1389
|
+
.padStart(2, '0');
|
|
1390
|
+
};
|
|
1391
|
+
return `#${channel(1)}${channel(3)}${channel(5)}`;
|
|
1392
|
+
}
|
|
1393
|
+
function meetsContrast(foreground, background, required) {
|
|
1394
|
+
return contrastRatio(foreground, background) >= required;
|
|
1395
|
+
}
|
|
1396
|
+
function contrastIssue(scope, pair, foreground, background, required) {
|
|
1397
|
+
const ratio = contrastRatio(foreground, background);
|
|
1398
|
+
return ratio >= required ? null : { scope, pair, ratio, required };
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
const FRAMES = [
|
|
815
1402
|
{
|
|
816
1403
|
id: 'nucleus',
|
|
817
1404
|
scheme: 'dark',
|
|
818
|
-
swatch: '#7c6cf5',
|
|
819
1405
|
starfield: true,
|
|
1406
|
+
accent: 'violet',
|
|
820
1407
|
tokens: {
|
|
821
|
-
bg: '#
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
accent: '#7c6cf5',
|
|
828
|
-
onAccent: '#ffffff',
|
|
1408
|
+
bg: '#16132b',
|
|
1409
|
+
raised: '#241f42',
|
|
1410
|
+
ink: '#ece9fb',
|
|
1411
|
+
muted: '#9d95c6',
|
|
1412
|
+
line: '#302a54',
|
|
1413
|
+
glow: '#8c7ff0',
|
|
829
1414
|
},
|
|
830
1415
|
},
|
|
831
1416
|
{
|
|
832
|
-
id: '
|
|
833
|
-
scheme: '
|
|
834
|
-
swatch: '#f5f5f5',
|
|
1417
|
+
id: 'midnight',
|
|
1418
|
+
scheme: 'dark',
|
|
835
1419
|
starfield: false,
|
|
1420
|
+
accent: 'blue',
|
|
836
1421
|
tokens: {
|
|
837
|
-
bg: '#
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
accent: '#0078d4',
|
|
844
|
-
onAccent: '#ffffff',
|
|
1422
|
+
bg: '#131f33',
|
|
1423
|
+
raised: '#1e2e48',
|
|
1424
|
+
ink: '#e3ecfa',
|
|
1425
|
+
muted: '#94a9c9',
|
|
1426
|
+
line: '#2b3d5d',
|
|
1427
|
+
glow: '#6ea8f0',
|
|
845
1428
|
},
|
|
846
1429
|
},
|
|
847
1430
|
{
|
|
848
|
-
id: '
|
|
1431
|
+
id: 'ocean',
|
|
849
1432
|
scheme: 'dark',
|
|
850
|
-
swatch: '#1e1e1e',
|
|
851
1433
|
starfield: false,
|
|
1434
|
+
accent: 'cyan',
|
|
852
1435
|
tokens: {
|
|
853
|
-
bg: '#
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
accent: '#0097fb',
|
|
860
|
-
onAccent: '#ffffff',
|
|
1436
|
+
bg: '#0d2a30',
|
|
1437
|
+
raised: '#153c45',
|
|
1438
|
+
ink: '#e0f2f4',
|
|
1439
|
+
muted: '#8cb3b9',
|
|
1440
|
+
line: '#1d4e58',
|
|
1441
|
+
glow: '#4cc5e8',
|
|
861
1442
|
},
|
|
862
1443
|
},
|
|
863
1444
|
{
|
|
864
|
-
id: '
|
|
865
|
-
scheme: '
|
|
866
|
-
swatch: '#f7b8d4',
|
|
1445
|
+
id: 'forest',
|
|
1446
|
+
scheme: 'dark',
|
|
867
1447
|
starfield: false,
|
|
1448
|
+
accent: 'green',
|
|
868
1449
|
tokens: {
|
|
869
|
-
bg: '#
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
accent: '#d6337f',
|
|
876
|
-
onAccent: '#ffffff',
|
|
1450
|
+
bg: '#122419',
|
|
1451
|
+
raised: '#1d3526',
|
|
1452
|
+
ink: '#e2f1e7',
|
|
1453
|
+
muted: '#93b8a1',
|
|
1454
|
+
line: '#28462f',
|
|
1455
|
+
glow: '#5cc47f',
|
|
877
1456
|
},
|
|
878
1457
|
},
|
|
879
1458
|
{
|
|
880
|
-
id: '
|
|
881
|
-
scheme: '
|
|
882
|
-
swatch: '#a8d5e8',
|
|
1459
|
+
id: 'plum',
|
|
1460
|
+
scheme: 'dark',
|
|
883
1461
|
starfield: false,
|
|
1462
|
+
accent: 'rose',
|
|
884
1463
|
tokens: {
|
|
885
|
-
bg: '#
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
accent: '#0f7ba8',
|
|
892
|
-
onAccent: '#ffffff',
|
|
1464
|
+
bg: '#241330',
|
|
1465
|
+
raised: '#341e43',
|
|
1466
|
+
ink: '#f3e8f7',
|
|
1467
|
+
muted: '#b596c3',
|
|
1468
|
+
line: '#452851',
|
|
1469
|
+
glow: '#cf7fdb',
|
|
893
1470
|
},
|
|
894
1471
|
},
|
|
895
1472
|
{
|
|
896
|
-
id: '
|
|
1473
|
+
id: 'ember',
|
|
897
1474
|
scheme: 'dark',
|
|
898
|
-
swatch: '#3b4a5c',
|
|
899
1475
|
starfield: false,
|
|
1476
|
+
accent: 'red',
|
|
900
1477
|
tokens: {
|
|
901
|
-
bg: '#
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
accent: '#5fa8e8',
|
|
908
|
-
onAccent: '#0b1118',
|
|
1478
|
+
bg: '#2a1113',
|
|
1479
|
+
raised: '#3d1b1f',
|
|
1480
|
+
ink: '#f8e6e7',
|
|
1481
|
+
muted: '#c9979c',
|
|
1482
|
+
line: '#4d2429',
|
|
1483
|
+
glow: '#f0797b',
|
|
909
1484
|
},
|
|
910
1485
|
},
|
|
911
1486
|
{
|
|
912
|
-
id: '
|
|
913
|
-
scheme: '
|
|
914
|
-
swatch: '#c4ece5',
|
|
1487
|
+
id: 'sand',
|
|
1488
|
+
scheme: 'dark',
|
|
915
1489
|
starfield: false,
|
|
1490
|
+
accent: 'amber',
|
|
916
1491
|
tokens: {
|
|
917
|
-
bg: '#
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
accent: '#0d8f7d',
|
|
924
|
-
onAccent: '#ffffff',
|
|
1492
|
+
bg: '#2b2415',
|
|
1493
|
+
raised: '#3d3320',
|
|
1494
|
+
ink: '#f6efdd',
|
|
1495
|
+
muted: '#c2b18b',
|
|
1496
|
+
line: '#4b3f28',
|
|
1497
|
+
glow: '#e0a33a',
|
|
925
1498
|
},
|
|
926
1499
|
},
|
|
927
1500
|
{
|
|
928
|
-
id: '
|
|
1501
|
+
id: 'slate',
|
|
929
1502
|
scheme: 'dark',
|
|
930
|
-
swatch: '#6b3a7a',
|
|
931
1503
|
starfield: false,
|
|
1504
|
+
accent: 'blue',
|
|
932
1505
|
tokens: {
|
|
933
|
-
bg: '#
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
accent: '#c86dd7',
|
|
940
|
-
onAccent: '#1a0d20',
|
|
1506
|
+
bg: '#232b36',
|
|
1507
|
+
raised: '#313c4b',
|
|
1508
|
+
ink: '#e6ecf4',
|
|
1509
|
+
muted: '#9fb0c4',
|
|
1510
|
+
line: '#3d4a5b',
|
|
1511
|
+
glow: '#6fb2ea',
|
|
941
1512
|
},
|
|
942
1513
|
},
|
|
943
1514
|
{
|
|
944
|
-
id: '
|
|
945
|
-
scheme: '
|
|
946
|
-
swatch: '#f7c48a',
|
|
1515
|
+
id: 'ink',
|
|
1516
|
+
scheme: 'dark',
|
|
947
1517
|
starfield: false,
|
|
1518
|
+
accent: 'blue',
|
|
948
1519
|
tokens: {
|
|
949
|
-
bg: '#
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
accent: '#e07314',
|
|
956
|
-
onAccent: '#ffffff',
|
|
1520
|
+
bg: '#101318',
|
|
1521
|
+
raised: '#1b2129',
|
|
1522
|
+
ink: '#edf0f4',
|
|
1523
|
+
muted: '#97a1ae',
|
|
1524
|
+
line: '#262d37',
|
|
1525
|
+
glow: '#7fb3d9',
|
|
957
1526
|
},
|
|
958
1527
|
},
|
|
959
1528
|
{
|
|
960
|
-
id: '
|
|
1529
|
+
id: 'graphite',
|
|
961
1530
|
scheme: 'dark',
|
|
962
|
-
swatch: '#1f3a5f',
|
|
963
1531
|
starfield: false,
|
|
1532
|
+
accent: 'violet',
|
|
964
1533
|
tokens: {
|
|
965
|
-
bg: '#
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
accent: '#6ea8f0',
|
|
972
|
-
onAccent: '#0a1020',
|
|
1534
|
+
bg: '#17171a',
|
|
1535
|
+
raised: '#232327',
|
|
1536
|
+
ink: '#eeeef0',
|
|
1537
|
+
muted: '#9c9ca4',
|
|
1538
|
+
line: '#2f2f35',
|
|
1539
|
+
glow: '#b9b9c2',
|
|
973
1540
|
},
|
|
974
1541
|
},
|
|
975
1542
|
{
|
|
976
|
-
id: '
|
|
977
|
-
scheme: '
|
|
978
|
-
swatch: '#1f4a2e',
|
|
1543
|
+
id: 'paper',
|
|
1544
|
+
scheme: 'light',
|
|
979
1545
|
starfield: false,
|
|
1546
|
+
accent: 'violet',
|
|
980
1547
|
tokens: {
|
|
981
|
-
bg: '#
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
accent: '#4caf70',
|
|
988
|
-
onAccent: '#0a1610',
|
|
1548
|
+
bg: '#ffffff',
|
|
1549
|
+
raised: '#f1f0f5',
|
|
1550
|
+
ink: '#16151d',
|
|
1551
|
+
muted: '#5e5c6b',
|
|
1552
|
+
line: '#e3e2ea',
|
|
1553
|
+
glow: '#5b4fd6',
|
|
989
1554
|
},
|
|
990
1555
|
},
|
|
991
1556
|
{
|
|
992
|
-
id: '
|
|
1557
|
+
id: 'mist',
|
|
993
1558
|
scheme: 'light',
|
|
994
|
-
swatch: '#f3c2d6',
|
|
995
1559
|
starfield: false,
|
|
1560
|
+
accent: 'blue',
|
|
996
1561
|
tokens: {
|
|
997
|
-
bg: '#
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
accent: '#c14b82',
|
|
1004
|
-
onAccent: '#ffffff',
|
|
1562
|
+
bg: '#eef1f6',
|
|
1563
|
+
raised: '#e2e7ef',
|
|
1564
|
+
ink: '#141a24',
|
|
1565
|
+
muted: '#576274',
|
|
1566
|
+
line: '#d3dae5',
|
|
1567
|
+
glow: '#1160b8',
|
|
1005
1568
|
},
|
|
1006
1569
|
},
|
|
1570
|
+
];
|
|
1571
|
+
const CANVASES = [
|
|
1007
1572
|
{
|
|
1008
|
-
id: '
|
|
1009
|
-
scheme: 'dark',
|
|
1010
|
-
swatch: '#8f1f24',
|
|
1011
|
-
starfield: false,
|
|
1573
|
+
id: 'light',
|
|
1012
1574
|
tokens: {
|
|
1013
|
-
bg: '#
|
|
1014
|
-
surface: '#
|
|
1015
|
-
surfaceAlt: '#
|
|
1016
|
-
text: '#
|
|
1017
|
-
textMuted: '#
|
|
1018
|
-
border: '#
|
|
1019
|
-
|
|
1020
|
-
|
|
1575
|
+
bg: '#edeff3',
|
|
1576
|
+
surface: '#ffffff',
|
|
1577
|
+
surfaceAlt: '#e2e6ed',
|
|
1578
|
+
text: '#16151d',
|
|
1579
|
+
textMuted: '#5a5f6b',
|
|
1580
|
+
border: '#d3d8e1',
|
|
1581
|
+
},
|
|
1582
|
+
effects: {
|
|
1583
|
+
shadowOne: '0 1px 2px rgb(16 20 30 / 6%), 0 2px 8px rgb(16 20 30 / 5%)',
|
|
1584
|
+
shadowTwo: '0 4px 12px rgb(16 20 30 / 8%), 0 12px 32px rgb(16 20 30 / 7%)',
|
|
1585
|
+
wash: 0.1,
|
|
1586
|
+
washStrong: 0.18,
|
|
1587
|
+
headerBg: '#d8dee8',
|
|
1588
|
+
headerInk: '#39404e',
|
|
1589
|
+
headerLine: '#bcc5d4',
|
|
1590
|
+
danger: '#b02121',
|
|
1591
|
+
success: '#136b35',
|
|
1592
|
+
warning: '#8a5a00',
|
|
1021
1593
|
},
|
|
1022
1594
|
},
|
|
1023
1595
|
{
|
|
1024
|
-
id: '
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1596
|
+
id: 'dim',
|
|
1597
|
+
tokens: {
|
|
1598
|
+
bg: '#1b1a22',
|
|
1599
|
+
surface: '#23222c',
|
|
1600
|
+
surfaceAlt: '#2c2b37',
|
|
1601
|
+
text: '#e9e8ef',
|
|
1602
|
+
textMuted: '#a6a4b4',
|
|
1603
|
+
border: '#383744',
|
|
1604
|
+
},
|
|
1605
|
+
effects: {
|
|
1606
|
+
shadowOne: '0 1px 2px rgb(0 0 0 / 24%)',
|
|
1607
|
+
shadowTwo: '0 10px 34px rgb(0 0 0 / 40%)',
|
|
1608
|
+
wash: 0.09,
|
|
1609
|
+
washStrong: 0.17,
|
|
1610
|
+
headerBg: '#302f3d',
|
|
1611
|
+
headerInk: '#c8c6d4',
|
|
1612
|
+
headerLine: '#423f50',
|
|
1613
|
+
danger: '#f0797b',
|
|
1614
|
+
success: '#5bd98a',
|
|
1615
|
+
warning: '#e0a33a',
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
id: 'dark',
|
|
1028
1620
|
tokens: {
|
|
1029
|
-
bg: '#
|
|
1030
|
-
surface: '#
|
|
1031
|
-
surfaceAlt: '#
|
|
1032
|
-
text: '#
|
|
1033
|
-
textMuted: '#
|
|
1034
|
-
border: '#
|
|
1035
|
-
|
|
1036
|
-
|
|
1621
|
+
bg: '#0e0d13',
|
|
1622
|
+
surface: '#17161f',
|
|
1623
|
+
surfaceAlt: '#211f2a',
|
|
1624
|
+
text: '#f2f1f6',
|
|
1625
|
+
textMuted: '#a09eb0',
|
|
1626
|
+
border: '#2a2833',
|
|
1627
|
+
},
|
|
1628
|
+
effects: {
|
|
1629
|
+
shadowOne: '0 1px 2px rgb(0 0 0 / 28%)',
|
|
1630
|
+
shadowTwo: '0 10px 34px rgb(0 0 0 / 45%)',
|
|
1631
|
+
wash: 0.08,
|
|
1632
|
+
washStrong: 0.16,
|
|
1633
|
+
headerBg: '#262433',
|
|
1634
|
+
headerInk: '#c2c0ce',
|
|
1635
|
+
headerLine: '#3a3745',
|
|
1636
|
+
danger: '#f0797b',
|
|
1637
|
+
success: '#5bd98a',
|
|
1638
|
+
warning: '#e0a33a',
|
|
1037
1639
|
},
|
|
1038
1640
|
},
|
|
1039
1641
|
];
|
|
1040
|
-
const
|
|
1642
|
+
const ACCENTS = [
|
|
1643
|
+
{ id: 'violet', light: '#5b4fd6', dark: '#9b8ef5' },
|
|
1644
|
+
{ id: 'blue', light: '#1160b8', dark: '#6fb2f2' },
|
|
1645
|
+
{ id: 'cyan', light: '#0d6f87', dark: '#4cc5e8' },
|
|
1646
|
+
{ id: 'green', light: '#157a3c', dark: '#5bd98a' },
|
|
1647
|
+
{ id: 'lime', light: '#4d7c0f', dark: '#a3d94b' },
|
|
1648
|
+
{ id: 'amber', light: '#8a5a00', dark: '#e0a33a' },
|
|
1649
|
+
{ id: 'orange', light: '#b2430c', dark: '#f2934f' },
|
|
1650
|
+
{ id: 'rose', light: '#b21b5c', dark: '#f07aab' },
|
|
1651
|
+
{ id: 'red', light: '#b02121', dark: '#f0797b' },
|
|
1652
|
+
];
|
|
1653
|
+
const DENSITIES = [
|
|
1654
|
+
{ id: 'comfortable', scale: 1 },
|
|
1655
|
+
{ id: 'compact', scale: 0.72 },
|
|
1656
|
+
];
|
|
1657
|
+
const DEFAULT_APPEARANCE = {
|
|
1658
|
+
frame: 'nucleus',
|
|
1659
|
+
canvas: 'dark',
|
|
1660
|
+
accent: 'auto',
|
|
1661
|
+
density: 'comfortable',
|
|
1662
|
+
};
|
|
1663
|
+
const LEGACY_THEMES = {
|
|
1664
|
+
nucleus: { frame: 'nucleus', canvas: 'dark', accent: 'auto' },
|
|
1665
|
+
light: { frame: 'paper', canvas: 'light', accent: 'blue' },
|
|
1666
|
+
dark: { frame: 'graphite', canvas: 'dark', accent: 'blue' },
|
|
1667
|
+
bubblegum: { frame: 'plum', canvas: 'light', accent: 'rose' },
|
|
1668
|
+
'cool-breeze': { frame: 'ocean', canvas: 'light', accent: 'cyan' },
|
|
1669
|
+
'cool-slate': { frame: 'slate', canvas: 'dark', accent: 'blue' },
|
|
1670
|
+
'icy-mint': { frame: 'ocean', canvas: 'light', accent: 'green' },
|
|
1671
|
+
'juicy-plum': { frame: 'plum', canvas: 'dark', accent: 'rose' },
|
|
1672
|
+
'mango-paradise': { frame: 'sand', canvas: 'light', accent: 'orange' },
|
|
1673
|
+
'moonlight-glow': { frame: 'midnight', canvas: 'dark', accent: 'blue' },
|
|
1674
|
+
'mystical-forest': { frame: 'forest', canvas: 'dark', accent: 'green' },
|
|
1675
|
+
'silky-pink': { frame: 'plum', canvas: 'light', accent: 'rose' },
|
|
1676
|
+
'spicy-red': { frame: 'ember', canvas: 'dark', accent: 'red' },
|
|
1677
|
+
'sunny-day': { frame: 'sand', canvas: 'light', accent: 'amber' },
|
|
1678
|
+
system: { frame: 'paper', canvas: 'system', accent: 'blue' },
|
|
1679
|
+
};
|
|
1680
|
+
const FRAME_BY_ID = new Map(FRAMES.map((frame) => [frame.id, frame]));
|
|
1681
|
+
const CANVAS_BY_ID = new Map(CANVASES.map((canvas) => [canvas.id, canvas]));
|
|
1682
|
+
const ACCENT_BY_ID = new Map(ACCENTS.map((accent) => [accent.id, accent]));
|
|
1683
|
+
const DENSITY_BY_ID = new Map(DENSITIES.map((density) => [density.id, density]));
|
|
1684
|
+
function frameById(id) {
|
|
1685
|
+
return FRAME_BY_ID.get(id) ?? FRAME_BY_ID.get(DEFAULT_APPEARANCE.frame);
|
|
1686
|
+
}
|
|
1687
|
+
function canvasById(id) {
|
|
1688
|
+
return CANVAS_BY_ID.get(id) ?? CANVAS_BY_ID.get('dark');
|
|
1689
|
+
}
|
|
1690
|
+
function accentById(id) {
|
|
1691
|
+
return ACCENT_BY_ID.get(id) ?? ACCENT_BY_ID.get('violet');
|
|
1692
|
+
}
|
|
1693
|
+
function densityById(id) {
|
|
1694
|
+
return DENSITY_BY_ID.get(id) ?? DENSITY_BY_ID.get('comfortable');
|
|
1695
|
+
}
|
|
1696
|
+
function isFrameId(value) {
|
|
1697
|
+
return typeof value === 'string' && FRAME_BY_ID.has(value);
|
|
1698
|
+
}
|
|
1699
|
+
function isCanvasPreference(value) {
|
|
1700
|
+
return value === 'system' || (typeof value === 'string' && CANVAS_BY_ID.has(value));
|
|
1701
|
+
}
|
|
1702
|
+
function isAccentPreference(value) {
|
|
1703
|
+
return value === 'auto' || (typeof value === 'string' && ACCENT_BY_ID.has(value));
|
|
1704
|
+
}
|
|
1705
|
+
function isDensityId(value) {
|
|
1706
|
+
return typeof value === 'string' && DENSITY_BY_ID.has(value);
|
|
1707
|
+
}
|
|
1708
|
+
function resolveAccent(frame, preference) {
|
|
1709
|
+
return accentById(preference === 'auto' ? frame.accent : preference);
|
|
1710
|
+
}
|
|
1711
|
+
function accentOn(accent, canvas) {
|
|
1712
|
+
return canvas === 'light' ? accent.light : accent.dark;
|
|
1713
|
+
}
|
|
1714
|
+
function isDarkCanvas(canvas) {
|
|
1715
|
+
return canvas !== 'light';
|
|
1716
|
+
}
|
|
1717
|
+
function auditAppearance() {
|
|
1718
|
+
const issues = [];
|
|
1719
|
+
for (const canvas of CANVASES) {
|
|
1720
|
+
const { bg, surface, surfaceAlt } = canvas.tokens;
|
|
1721
|
+
const { danger, success, warning } = canvas.effects;
|
|
1722
|
+
const scope = `semantic:${canvas.id}`;
|
|
1723
|
+
for (const [name, color] of [
|
|
1724
|
+
['danger', danger],
|
|
1725
|
+
['success', success],
|
|
1726
|
+
['warning', warning],
|
|
1727
|
+
]) {
|
|
1728
|
+
issues.push(contrastIssue(scope, `${name}/bg`, color, bg, TEXT_CONTRAST), contrastIssue(scope, `${name}/surface`, color, surface, TEXT_CONTRAST), contrastIssue(scope, `${name}/surfaceAlt`, color, surfaceAlt, TEXT_CONTRAST));
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
for (const frame of FRAMES) {
|
|
1732
|
+
const { bg, raised, ink, muted, glow } = frame.tokens;
|
|
1733
|
+
const scope = `frame:${frame.id}`;
|
|
1734
|
+
issues.push(contrastIssue(scope, 'ink/bg', ink, bg, TEXT_CONTRAST), contrastIssue(scope, 'ink/raised', ink, raised, TEXT_CONTRAST), contrastIssue(scope, 'muted/bg', muted, bg, TEXT_CONTRAST), contrastIssue(scope, 'muted/raised', muted, raised, TEXT_CONTRAST), contrastIssue(scope, 'glow/bg', glow, bg, CONTROL_CONTRAST), contrastIssue(scope, 'glow/raised', glow, raised, CONTROL_CONTRAST), contrastIssue(scope, 'onGlow/glow', readableOn(glow), glow, TEXT_CONTRAST));
|
|
1735
|
+
}
|
|
1736
|
+
for (const canvas of CANVASES) {
|
|
1737
|
+
const { bg, surface, surfaceAlt, text, textMuted } = canvas.tokens;
|
|
1738
|
+
const scope = `canvas:${canvas.id}`;
|
|
1739
|
+
issues.push(contrastIssue(scope, 'text/bg', text, bg, TEXT_CONTRAST), contrastIssue(scope, 'text/surface', text, surface, TEXT_CONTRAST), contrastIssue(scope, 'muted/bg', textMuted, bg, TEXT_CONTRAST), contrastIssue(scope, 'muted/surface', textMuted, surface, TEXT_CONTRAST), contrastIssue(scope, 'muted/surfaceAlt', textMuted, surfaceAlt, TEXT_CONTRAST));
|
|
1740
|
+
}
|
|
1741
|
+
for (const accent of ACCENTS) {
|
|
1742
|
+
for (const canvas of CANVASES) {
|
|
1743
|
+
const color = accentOn(accent, canvas.id);
|
|
1744
|
+
const scope = `accent:${accent.id}/${canvas.id}`;
|
|
1745
|
+
issues.push(contrastIssue(scope, 'accent/bg', color, canvas.tokens.bg, CONTROL_CONTRAST), contrastIssue(scope, 'accent/surface', color, canvas.tokens.surface, CONTROL_CONTRAST), contrastIssue(scope, 'accent/surfaceAlt', color, canvas.tokens.surfaceAlt, CONTROL_CONTRAST), contrastIssue(scope, 'onAccent/accent', readableOn(color), color, TEXT_CONTRAST));
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return issues.filter((issue) => issue !== null);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
const FRAMES_ES = {
|
|
1041
1752
|
nucleus: 'Nucleus',
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
'spicy-red': 'Spicy Red',
|
|
1054
|
-
'sunny-day': 'Sunny Day',
|
|
1753
|
+
midnight: 'Medianoche',
|
|
1754
|
+
ocean: 'Océano',
|
|
1755
|
+
forest: 'Bosque',
|
|
1756
|
+
plum: 'Ciruela',
|
|
1757
|
+
ember: 'Brasa',
|
|
1758
|
+
sand: 'Arena',
|
|
1759
|
+
slate: 'Pizarra',
|
|
1760
|
+
ink: 'Tinta',
|
|
1761
|
+
graphite: 'Grafito',
|
|
1762
|
+
paper: 'Papel',
|
|
1763
|
+
mist: 'Bruma',
|
|
1055
1764
|
};
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1765
|
+
const FRAMES_EN = {
|
|
1766
|
+
nucleus: 'Nucleus',
|
|
1767
|
+
midnight: 'Midnight',
|
|
1768
|
+
ocean: 'Ocean',
|
|
1769
|
+
forest: 'Forest',
|
|
1770
|
+
plum: 'Plum',
|
|
1771
|
+
ember: 'Ember',
|
|
1772
|
+
sand: 'Sand',
|
|
1773
|
+
slate: 'Slate',
|
|
1774
|
+
ink: 'Ink',
|
|
1775
|
+
graphite: 'Graphite',
|
|
1776
|
+
paper: 'Paper',
|
|
1777
|
+
mist: 'Mist',
|
|
1778
|
+
};
|
|
1779
|
+
const ACCENTS_ES = {
|
|
1780
|
+
auto: 'Del marco',
|
|
1781
|
+
violet: 'Violeta',
|
|
1782
|
+
blue: 'Azul',
|
|
1783
|
+
cyan: 'Cian',
|
|
1784
|
+
green: 'Verde',
|
|
1785
|
+
lime: 'Lima',
|
|
1786
|
+
amber: 'Ámbar',
|
|
1787
|
+
orange: 'Naranja',
|
|
1788
|
+
rose: 'Rosa',
|
|
1789
|
+
red: 'Rojo',
|
|
1790
|
+
};
|
|
1791
|
+
const ACCENTS_EN = {
|
|
1792
|
+
auto: 'From the frame',
|
|
1793
|
+
violet: 'Violet',
|
|
1794
|
+
blue: 'Blue',
|
|
1795
|
+
cyan: 'Cyan',
|
|
1796
|
+
green: 'Green',
|
|
1797
|
+
lime: 'Lime',
|
|
1798
|
+
amber: 'Amber',
|
|
1799
|
+
orange: 'Orange',
|
|
1800
|
+
rose: 'Rose',
|
|
1801
|
+
red: 'Red',
|
|
1802
|
+
};
|
|
1803
|
+
const THEME_COPY = {
|
|
1804
|
+
es: {
|
|
1805
|
+
title: 'Apariencia',
|
|
1806
|
+
frameTitle: 'Marco',
|
|
1807
|
+
frameHint: 'El color de la barra superior y del menú lateral.',
|
|
1808
|
+
canvasTitle: 'Lienzo',
|
|
1809
|
+
canvasHint: 'La superficie donde lees y escribes. Nunca se tiñe.',
|
|
1810
|
+
accentTitle: 'Acción',
|
|
1811
|
+
accentHint: 'El color del botón principal, del foco y de lo seleccionado.',
|
|
1812
|
+
densityTitle: 'Densidad',
|
|
1813
|
+
densityHint: 'Cuántas filas quieres ver de una sola pasada.',
|
|
1814
|
+
frames: FRAMES_ES,
|
|
1815
|
+
canvases: { light: 'Claro', dim: 'Atenuado', dark: 'Oscuro', system: 'Del sistema' },
|
|
1816
|
+
accents: ACCENTS_ES,
|
|
1817
|
+
densities: { comfortable: 'Cómoda', compact: 'Compacta' },
|
|
1818
|
+
},
|
|
1819
|
+
en: {
|
|
1820
|
+
title: 'Appearance',
|
|
1821
|
+
frameTitle: 'Frame',
|
|
1822
|
+
frameHint: 'The color of the top bar and the side menu.',
|
|
1823
|
+
canvasTitle: 'Canvas',
|
|
1824
|
+
canvasHint: 'The surface where you read and type. Never tinted.',
|
|
1825
|
+
accentTitle: 'Action',
|
|
1826
|
+
accentHint: 'The color of the primary button, the focus ring and the selected row.',
|
|
1827
|
+
densityTitle: 'Density',
|
|
1828
|
+
densityHint: 'How many rows you want to see at a glance.',
|
|
1829
|
+
frames: FRAMES_EN,
|
|
1830
|
+
canvases: { light: 'Light', dim: 'Dim', dark: 'Dark', system: 'System' },
|
|
1831
|
+
accents: ACCENTS_EN,
|
|
1832
|
+
densities: { comfortable: 'Comfortable', compact: 'Compact' },
|
|
1833
|
+
},
|
|
1834
|
+
};
|
|
1835
|
+
function themeCopyOf(lang) {
|
|
1836
|
+
return THEME_COPY[lang] ?? THEME_COPY.es;
|
|
1060
1837
|
}
|
|
1061
|
-
function
|
|
1062
|
-
return
|
|
1838
|
+
function accentLabelOf(lang, id) {
|
|
1839
|
+
return themeCopyOf(lang).accents[id];
|
|
1063
1840
|
}
|
|
1064
1841
|
|
|
1065
|
-
const STORAGE_KEY = '
|
|
1066
|
-
|
|
1842
|
+
const STORAGE_KEY$1 = 'nucleus_appearance';
|
|
1843
|
+
const LEGACY_STORAGE_KEY = 'nucleus_theme';
|
|
1844
|
+
const DARK_QUERY = '(prefers-color-scheme: dark)';
|
|
1845
|
+
function parseAppearance(raw) {
|
|
1846
|
+
if (!raw)
|
|
1847
|
+
return null;
|
|
1067
1848
|
try {
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1849
|
+
const parsed = JSON.parse(raw);
|
|
1850
|
+
return {
|
|
1851
|
+
frame: isFrameId(parsed.frame) ? parsed.frame : DEFAULT_APPEARANCE.frame,
|
|
1852
|
+
canvas: isCanvasPreference(parsed.canvas) ? parsed.canvas : DEFAULT_APPEARANCE.canvas,
|
|
1853
|
+
accent: isAccentPreference(parsed.accent) ? parsed.accent : DEFAULT_APPEARANCE.accent,
|
|
1854
|
+
density: isDensityId(parsed.density) ? parsed.density : DEFAULT_APPEARANCE.density,
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
catch {
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
function migrateLegacy(raw) {
|
|
1862
|
+
if (!raw)
|
|
1863
|
+
return null;
|
|
1864
|
+
const legacy = LEGACY_THEMES[raw];
|
|
1865
|
+
return legacy ? { ...DEFAULT_APPEARANCE, ...legacy } : null;
|
|
1866
|
+
}
|
|
1867
|
+
function readStored() {
|
|
1868
|
+
try {
|
|
1869
|
+
const current = parseAppearance(localStorage.getItem(STORAGE_KEY$1));
|
|
1870
|
+
if (current)
|
|
1871
|
+
return current;
|
|
1872
|
+
const migrated = migrateLegacy(localStorage.getItem(LEGACY_STORAGE_KEY));
|
|
1873
|
+
if (migrated) {
|
|
1874
|
+
localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
1875
|
+
return migrated;
|
|
1876
|
+
}
|
|
1071
1877
|
}
|
|
1072
1878
|
catch { }
|
|
1073
|
-
return
|
|
1879
|
+
return DEFAULT_APPEARANCE;
|
|
1074
1880
|
}
|
|
1075
1881
|
function prefersDark() {
|
|
1076
|
-
return globalThis.matchMedia?.(
|
|
1882
|
+
return globalThis.matchMedia?.(DARK_QUERY).matches ?? false;
|
|
1077
1883
|
}
|
|
1078
1884
|
class ThemeService {
|
|
1079
|
-
|
|
1885
|
+
_appearance = signal(readStored(), ...(ngDevMode ? [{ debugName: "_appearance" }] : /* istanbul ignore next */ []));
|
|
1080
1886
|
systemDark = signal(prefersDark(), ...(ngDevMode ? [{ debugName: "systemDark" }] : /* istanbul ignore next */ []));
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1887
|
+
appearance = this._appearance.asReadonly();
|
|
1888
|
+
frame = computed(() => frameById(this._appearance().frame), ...(ngDevMode ? [{ debugName: "frame" }] : /* istanbul ignore next */ []));
|
|
1889
|
+
canvasId = computed(() => {
|
|
1890
|
+
const preference = this._appearance().canvas;
|
|
1891
|
+
if (preference !== 'system')
|
|
1892
|
+
return preference;
|
|
1893
|
+
return this.systemCanvasId();
|
|
1894
|
+
}, ...(ngDevMode ? [{ debugName: "canvasId" }] : /* istanbul ignore next */ []));
|
|
1895
|
+
systemCanvasId = computed(() => (this.systemDark() ? 'dark' : 'light'), ...(ngDevMode ? [{ debugName: "systemCanvasId" }] : /* istanbul ignore next */ []));
|
|
1896
|
+
canvas = computed(() => canvasById(this.canvasId()), ...(ngDevMode ? [{ debugName: "canvas" }] : /* istanbul ignore next */ []));
|
|
1897
|
+
accent = computed(() => resolveAccent(this.frame(), this._appearance().accent), ...(ngDevMode ? [{ debugName: "accent" }] : /* istanbul ignore next */ []));
|
|
1898
|
+
accentColor = computed(() => accentOn(this.accent(), this.canvasId()), ...(ngDevMode ? [{ debugName: "accentColor" }] : /* istanbul ignore next */ []));
|
|
1899
|
+
density = computed(() => densityById(this._appearance().density), ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
|
|
1900
|
+
starfield = computed(() => this.frame().starfield && isDarkCanvas(this.canvasId()), ...(ngDevMode ? [{ debugName: "starfield" }] : /* istanbul ignore next */ []));
|
|
1089
1901
|
constructor() {
|
|
1090
1902
|
globalThis
|
|
1091
|
-
.matchMedia?.(
|
|
1903
|
+
.matchMedia?.(DARK_QUERY)
|
|
1092
1904
|
.addEventListener('change', (event) => this.systemDark.set(event.matches));
|
|
1093
1905
|
effect(() => {
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
this.applyToDom(theme);
|
|
1906
|
+
const appearance = this._appearance();
|
|
1907
|
+
this.applyToDom();
|
|
1097
1908
|
try {
|
|
1098
|
-
localStorage.setItem(STORAGE_KEY,
|
|
1909
|
+
localStorage.setItem(STORAGE_KEY$1, JSON.stringify(appearance));
|
|
1099
1910
|
}
|
|
1100
1911
|
catch { }
|
|
1101
1912
|
});
|
|
1102
1913
|
}
|
|
1103
|
-
|
|
1104
|
-
this.
|
|
1914
|
+
setFrame(frame) {
|
|
1915
|
+
this.patch({ frame });
|
|
1916
|
+
}
|
|
1917
|
+
setCanvas(canvas) {
|
|
1918
|
+
this.patch({ canvas });
|
|
1919
|
+
}
|
|
1920
|
+
setAccent(accent) {
|
|
1921
|
+
this.patch({ accent });
|
|
1922
|
+
}
|
|
1923
|
+
setDensity(density) {
|
|
1924
|
+
this.patch({ density });
|
|
1925
|
+
}
|
|
1926
|
+
reset() {
|
|
1927
|
+
this._appearance.set(DEFAULT_APPEARANCE);
|
|
1105
1928
|
}
|
|
1106
|
-
|
|
1929
|
+
patch(change) {
|
|
1930
|
+
this._appearance.update((appearance) => ({ ...appearance, ...change }));
|
|
1931
|
+
}
|
|
1932
|
+
applyToDom() {
|
|
1107
1933
|
const root = document.documentElement;
|
|
1108
|
-
|
|
1109
|
-
|
|
1934
|
+
const frame = this.frame();
|
|
1935
|
+
const canvas = this.canvas();
|
|
1936
|
+
const accent = this.accentColor();
|
|
1937
|
+
const density = this.density();
|
|
1938
|
+
for (const [name, value] of Object.entries(frame.tokens)) {
|
|
1939
|
+
root.style.setProperty(`--ns-frame-${kebab(name)}`, value);
|
|
1940
|
+
}
|
|
1941
|
+
root.style.setProperty('--ns-frame-on-glow', readableOn(frame.tokens.glow));
|
|
1942
|
+
for (const [name, value] of Object.entries(canvas.tokens)) {
|
|
1943
|
+
root.style.setProperty(`--ac-${kebab(name)}`, value);
|
|
1110
1944
|
}
|
|
1111
|
-
root.
|
|
1112
|
-
root.
|
|
1113
|
-
root.style.
|
|
1945
|
+
root.style.setProperty('--ac-accent', accent);
|
|
1946
|
+
root.style.setProperty('--ac-on-accent', readableOn(accent));
|
|
1947
|
+
root.style.setProperty('--ns-shadow-1', canvas.effects.shadowOne);
|
|
1948
|
+
root.style.setProperty('--ns-shadow-2', canvas.effects.shadowTwo);
|
|
1949
|
+
root.style.setProperty('--ns-header-bg', canvas.effects.headerBg);
|
|
1950
|
+
root.style.setProperty('--ns-header-ink', canvas.effects.headerInk);
|
|
1951
|
+
root.style.setProperty('--ns-header-line', canvas.effects.headerLine);
|
|
1952
|
+
root.style.setProperty('--ns-danger', canvas.effects.danger);
|
|
1953
|
+
root.style.setProperty('--ns-success', canvas.effects.success);
|
|
1954
|
+
root.style.setProperty('--ns-warning', canvas.effects.warning);
|
|
1955
|
+
root.style.setProperty('--ns-accent-wash', mixHex(accent, canvas.tokens.surface, canvas.effects.wash));
|
|
1956
|
+
root.style.setProperty('--ns-accent-wash-strong', mixHex(accent, canvas.tokens.surface, canvas.effects.washStrong));
|
|
1957
|
+
root.style.setProperty('--ns-density', String(density.scale));
|
|
1958
|
+
root.dataset['frame'] = frame.id;
|
|
1959
|
+
root.dataset['canvas'] = canvas.id;
|
|
1960
|
+
root.dataset['density'] = density.id;
|
|
1961
|
+
root.classList.toggle('ns-dark', isDarkCanvas(canvas.id));
|
|
1962
|
+
root.style.colorScheme = isDarkCanvas(canvas.id) ? 'dark' : 'light';
|
|
1114
1963
|
}
|
|
1115
1964
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1116
1965
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ThemeService, providedIn: 'root' });
|
|
@@ -1119,12 +1968,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1119
1968
|
type: Injectable,
|
|
1120
1969
|
args: [{ providedIn: 'root' }]
|
|
1121
1970
|
}], ctorParameters: () => [] });
|
|
1971
|
+
function kebab(name) {
|
|
1972
|
+
return name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
1973
|
+
}
|
|
1122
1974
|
|
|
1123
1975
|
const MASK = {
|
|
1124
1976
|
background: 'rgba(0, 0, 0, 0.55)',
|
|
1125
1977
|
color: 'var(--ac-text)',
|
|
1126
1978
|
};
|
|
1127
|
-
const SHADOW = '0 10px 40px
|
|
1979
|
+
const SHADOW = 'var(--ns-shadow-2, 0 10px 40px rgb(0 0 0 / 45%))';
|
|
1128
1980
|
const PANEL = {
|
|
1129
1981
|
background: 'var(--ac-surface)',
|
|
1130
1982
|
borderColor: 'var(--ac-border)',
|
|
@@ -1174,6 +2026,7 @@ const schemeTokens = {
|
|
|
1174
2026
|
},
|
|
1175
2027
|
},
|
|
1176
2028
|
formField: {
|
|
2029
|
+
paddingY: 'var(--ns-control-y, 0.55rem)',
|
|
1177
2030
|
background: 'var(--ac-bg)',
|
|
1178
2031
|
disabledBackground: 'var(--ac-surface-alt)',
|
|
1179
2032
|
filledBackground: 'var(--ac-surface-alt)',
|
|
@@ -1279,9 +2132,9 @@ class NucleusLordicon {
|
|
|
1279
2132
|
size = input(48, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1280
2133
|
tone = input('accent', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
1281
2134
|
colors = computed(() => {
|
|
1282
|
-
const
|
|
1283
|
-
const primary = this.tone() === 'danger' ?
|
|
1284
|
-
return `primary:${primary},secondary:${
|
|
2135
|
+
const canvas = this.themes.canvas();
|
|
2136
|
+
const primary = this.tone() === 'danger' ? canvas.effects.danger : this.themes.accentColor();
|
|
2137
|
+
return `primary:${primary},secondary:${canvas.tokens.text}`;
|
|
1285
2138
|
}, ...(ngDevMode ? [{ debugName: "colors" }] : /* istanbul ignore next */ []));
|
|
1286
2139
|
constructor() {
|
|
1287
2140
|
void loadLordicon();
|
|
@@ -1316,8 +2169,23 @@ class NucleusCard {
|
|
|
1316
2169
|
heading = input('', ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
1317
2170
|
subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
1318
2171
|
size = input('full', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2172
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2173
|
+
skeletonRows = input(4, ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
|
|
2174
|
+
static widths = [
|
|
2175
|
+
['46%', '22%'],
|
|
2176
|
+
['62%', '17%'],
|
|
2177
|
+
['38%', '25%'],
|
|
2178
|
+
['54%', '19%'],
|
|
2179
|
+
['44%', '23%'],
|
|
2180
|
+
['58%', '16%'],
|
|
2181
|
+
];
|
|
2182
|
+
placeholders = computed(() => Array.from({ length: Math.max(this.skeletonRows(), 1) }, (_, index) => ({
|
|
2183
|
+
index,
|
|
2184
|
+
left: NucleusCard.widths[index % NucleusCard.widths.length][0],
|
|
2185
|
+
right: NucleusCard.widths[index % NucleusCard.widths.length][1],
|
|
2186
|
+
})), ...(ngDevMode ? [{ debugName: "placeholders" }] : /* istanbul ignore next */ []));
|
|
1319
2187
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1320
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusCard, isStandalone: true, selector: "nucleus-card", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-size": "size()" } }, ngImport: i0, template: `
|
|
2188
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusCard, isStandalone: true, selector: "nucleus-card", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, skeletonRows: { classPropertyName: "skeletonRows", publicName: "skeletonRows", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-size": "size()" } }, ngImport: i0, template: `
|
|
1321
2189
|
@if (heading() || subtitle()) {
|
|
1322
2190
|
<div class="head">
|
|
1323
2191
|
@if (heading()) {
|
|
@@ -1329,16 +2197,29 @@ class NucleusCard {
|
|
|
1329
2197
|
</div>
|
|
1330
2198
|
}
|
|
1331
2199
|
|
|
1332
|
-
|
|
2200
|
+
@if (loading()) {
|
|
2201
|
+
<div class="skeleton-rows">
|
|
2202
|
+
@for (row of placeholders(); track row.index) {
|
|
2203
|
+
<div class="skeleton-row">
|
|
2204
|
+
<p-skeleton height="13px" [width]="row.left" />
|
|
2205
|
+
<p-skeleton height="12px" [width]="row.right" />
|
|
2206
|
+
</div>
|
|
2207
|
+
}
|
|
2208
|
+
</div>
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
<div class="body" [style.display]="loading() ? 'none' : null">
|
|
2212
|
+
<ng-content />
|
|
2213
|
+
</div>
|
|
1333
2214
|
|
|
1334
2215
|
<div class="actions">
|
|
1335
2216
|
<ng-content select="[nucleusCardActions]" />
|
|
1336
2217
|
</div>
|
|
1337
|
-
`, isInline: true, styles: [":host{display:block;padding:22px 24px;border:1px solid var(--ac-border);border-radius:12px;background:
|
|
2218
|
+
`, isInline: true, styles: [":host{display:block;padding:22px 24px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);box-shadow:var(--ns-shadow-1)}:host([data-size=\"sm\"]){max-width:34rem}:host([data-size=\"md\"]){max-width:48rem}:host([data-size=\"lg\"]){max-width:64rem}.head{margin-bottom:18px}h2{margin:0;font-size:16px;font-weight:600}.subtitle{margin:4px 0 0;color:var(--ac-text-muted);font-size:13px}.body{display:contents}.skeleton-rows{display:grid}.skeleton-row{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:13px 0;border-bottom:1px solid color-mix(in srgb,var(--ac-border) 60%,transparent)}.skeleton-row:last-child{border-bottom:0}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.actions:empty{display:none}\n"], dependencies: [{ kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1338
2219
|
}
|
|
1339
2220
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCard, decorators: [{
|
|
1340
2221
|
type: Component,
|
|
1341
|
-
args: [{ selector: 'nucleus-card', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[attr.data-size]': 'size()' }, template: `
|
|
2222
|
+
args: [{ selector: 'nucleus-card', imports: [SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[attr.data-size]': 'size()' }, template: `
|
|
1342
2223
|
@if (heading() || subtitle()) {
|
|
1343
2224
|
<div class="head">
|
|
1344
2225
|
@if (heading()) {
|
|
@@ -1350,13 +2231,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1350
2231
|
</div>
|
|
1351
2232
|
}
|
|
1352
2233
|
|
|
1353
|
-
|
|
2234
|
+
@if (loading()) {
|
|
2235
|
+
<div class="skeleton-rows">
|
|
2236
|
+
@for (row of placeholders(); track row.index) {
|
|
2237
|
+
<div class="skeleton-row">
|
|
2238
|
+
<p-skeleton height="13px" [width]="row.left" />
|
|
2239
|
+
<p-skeleton height="12px" [width]="row.right" />
|
|
2240
|
+
</div>
|
|
2241
|
+
}
|
|
2242
|
+
</div>
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
<div class="body" [style.display]="loading() ? 'none' : null">
|
|
2246
|
+
<ng-content />
|
|
2247
|
+
</div>
|
|
1354
2248
|
|
|
1355
2249
|
<div class="actions">
|
|
1356
2250
|
<ng-content select="[nucleusCardActions]" />
|
|
1357
2251
|
</div>
|
|
1358
|
-
`, styles: [":host{display:block;padding:22px 24px;border:1px solid var(--ac-border);border-radius:12px;background:
|
|
1359
|
-
}], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
2252
|
+
`, styles: [":host{display:block;padding:22px 24px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);box-shadow:var(--ns-shadow-1)}:host([data-size=\"sm\"]){max-width:34rem}:host([data-size=\"md\"]){max-width:48rem}:host([data-size=\"lg\"]){max-width:64rem}.head{margin-bottom:18px}h2{margin:0;font-size:16px;font-weight:600}.subtitle{margin:4px 0 0;color:var(--ac-text-muted);font-size:13px}.body{display:contents}.skeleton-rows{display:grid}.skeleton-row{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:13px 0;border-bottom:1px solid color-mix(in srgb,var(--ac-border) 60%,transparent)}.skeleton-row:last-child{border-bottom:0}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.actions:empty{display:none}\n"] }]
|
|
2253
|
+
}], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], skeletonRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonRows", required: false }] }] } });
|
|
2254
|
+
|
|
2255
|
+
class NucleusMetrics {
|
|
2256
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2257
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMetrics, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2258
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.22", type: NucleusMetrics, isStandalone: true, selector: "nucleus-metrics", inputs: { loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<ng-content />', isInline: true, styles: [":host{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(var(--ns-metric-min, 210px),1fr))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2259
|
+
}
|
|
2260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMetrics, decorators: [{
|
|
2261
|
+
type: Component,
|
|
2262
|
+
args: [{ selector: 'nucleus-metrics', changeDetection: ChangeDetectionStrategy.OnPush, template: '<ng-content />', styles: [":host{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(var(--ns-metric-min, 210px),1fr))}\n"] }]
|
|
2263
|
+
}], propDecorators: { loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
|
|
2264
|
+
|
|
2265
|
+
class NucleusMetric {
|
|
2266
|
+
group = inject(NucleusMetrics, { optional: true });
|
|
2267
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
2268
|
+
value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
2269
|
+
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
2270
|
+
link = input('', ...(ngDevMode ? [{ debugName: "link" }] : /* istanbul ignore next */ []));
|
|
2271
|
+
tone = input('plain', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
2272
|
+
loading = input(null, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2273
|
+
busy = computed(() => this.loading() ?? this.group?.loading() ?? false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
2274
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMetric, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2275
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusMetric, isStandalone: true, selector: "nucleus-metric", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-tone": "tone() === 'accent' ? 'accent' : null" } }, ngImport: i0, template: `
|
|
2276
|
+
@if (link().length) {
|
|
2277
|
+
<a class="tile" [routerLink]="link()">
|
|
2278
|
+
<ng-container [ngTemplateOutlet]="body" />
|
|
2279
|
+
</a>
|
|
2280
|
+
} @else {
|
|
2281
|
+
<div class="tile">
|
|
2282
|
+
<ng-container [ngTemplateOutlet]="body" />
|
|
2283
|
+
</div>
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
<ng-template #body>
|
|
2287
|
+
<span class="label">{{ label() }}</span>
|
|
2288
|
+
|
|
2289
|
+
@if (busy()) {
|
|
2290
|
+
<p-skeleton styleClass="value-skeleton" height="23px" width="92px" />
|
|
2291
|
+
@if (hint().length) {
|
|
2292
|
+
<p-skeleton styleClass="hint-skeleton" height="10px" width="132px" />
|
|
2293
|
+
}
|
|
2294
|
+
} @else {
|
|
2295
|
+
<strong class="value">{{ value() }}</strong>
|
|
2296
|
+
@if (hint().length) {
|
|
2297
|
+
<small class="hint">{{ hint() }}</small>
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
</ng-template>
|
|
2301
|
+
`, isInline: true, styles: [":host{display:block}.tile{display:grid;align-content:start;gap:5px;height:100%;padding:18px 20px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);color:inherit;text-decoration:none;transition:border-color .14s ease}a.tile:hover{border-color:var(--ac-accent)}:host([data-tone=\"accent\"]) .tile{border-color:var(--ac-accent)}.label{color:var(--ac-text-muted);font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.value{font-size:26px;font-variant-numeric:tabular-nums;line-height:1}.hint{color:var(--ac-text-muted);font-size:12px}.value-skeleton{margin:1px 0 2px}.hint-skeleton{margin:3px 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2302
|
+
}
|
|
2303
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMetric, decorators: [{
|
|
2304
|
+
type: Component,
|
|
2305
|
+
args: [{ selector: 'nucleus-metric', imports: [NgTemplateOutlet, RouterLink, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[attr.data-tone]': "tone() === 'accent' ? 'accent' : null" }, template: `
|
|
2306
|
+
@if (link().length) {
|
|
2307
|
+
<a class="tile" [routerLink]="link()">
|
|
2308
|
+
<ng-container [ngTemplateOutlet]="body" />
|
|
2309
|
+
</a>
|
|
2310
|
+
} @else {
|
|
2311
|
+
<div class="tile">
|
|
2312
|
+
<ng-container [ngTemplateOutlet]="body" />
|
|
2313
|
+
</div>
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
<ng-template #body>
|
|
2317
|
+
<span class="label">{{ label() }}</span>
|
|
2318
|
+
|
|
2319
|
+
@if (busy()) {
|
|
2320
|
+
<p-skeleton styleClass="value-skeleton" height="23px" width="92px" />
|
|
2321
|
+
@if (hint().length) {
|
|
2322
|
+
<p-skeleton styleClass="hint-skeleton" height="10px" width="132px" />
|
|
2323
|
+
}
|
|
2324
|
+
} @else {
|
|
2325
|
+
<strong class="value">{{ value() }}</strong>
|
|
2326
|
+
@if (hint().length) {
|
|
2327
|
+
<small class="hint">{{ hint() }}</small>
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
</ng-template>
|
|
2331
|
+
`, styles: [":host{display:block}.tile{display:grid;align-content:start;gap:5px;height:100%;padding:18px 20px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);color:inherit;text-decoration:none;transition:border-color .14s ease}a.tile:hover{border-color:var(--ac-accent)}:host([data-tone=\"accent\"]) .tile{border-color:var(--ac-accent)}.label{color:var(--ac-text-muted);font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.value{font-size:26px;font-variant-numeric:tabular-nums;line-height:1}.hint{color:var(--ac-text-muted);font-size:12px}.value-skeleton{margin:1px 0 2px}.hint-skeleton{margin:3px 0}\n"] }]
|
|
2332
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
|
|
1360
2333
|
|
|
1361
2334
|
class NucleusTabs {
|
|
1362
2335
|
tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
@@ -1465,8 +2438,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1465
2438
|
function resolveFieldSize(field) {
|
|
1466
2439
|
if (field.size)
|
|
1467
2440
|
return field.size;
|
|
1468
|
-
if (field.kind === 'textarea')
|
|
2441
|
+
if (field.kind === 'textarea' || field.kind === 'color')
|
|
1469
2442
|
return 'full';
|
|
2443
|
+
if (field.kind === 'icon')
|
|
2444
|
+
return 'md';
|
|
1470
2445
|
const max = field.maxlength;
|
|
1471
2446
|
if (max === undefined)
|
|
1472
2447
|
return 'md';
|
|
@@ -1480,13 +2455,343 @@ function resolveFieldSize(field) {
|
|
|
1480
2455
|
return 'lg';
|
|
1481
2456
|
return 'full';
|
|
1482
2457
|
}
|
|
2458
|
+
function isFieldSearchable(field) {
|
|
2459
|
+
return field.searchable !== false;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
const NUCLEUS_ICON_CATALOG = [
|
|
2463
|
+
'address-book', 'align-center', 'align-justify', 'align-left', 'align-right', 'amazon',
|
|
2464
|
+
'android', 'angle-double-down', 'angle-double-left', 'angle-double-right', 'angle-double-up',
|
|
2465
|
+
'angle-down', 'angle-left', 'angle-right', 'angle-up', 'apple', 'arrow-circle-down',
|
|
2466
|
+
'arrow-circle-left', 'arrow-circle-right', 'arrow-circle-up', 'arrow-down', 'arrow-down-left',
|
|
2467
|
+
'arrow-down-left-and-arrow-up-right-to-center', 'arrow-down-right', 'arrow-left',
|
|
2468
|
+
'arrow-right', 'arrow-right-arrow-left', 'arrow-u-turn-up-left', 'arrow-u-turn-up-right',
|
|
2469
|
+
'arrow-up', 'arrow-up-left', 'arrow-up-right',
|
|
2470
|
+
'arrow-up-right-and-arrow-down-left-from-center', 'arrows-alt', 'arrows-h', 'arrows-v',
|
|
2471
|
+
'asterisk', 'at', 'backward', 'ban', 'barcode', 'bars', 'bell', 'bell-slash', 'bitcoin',
|
|
2472
|
+
'block-quote', 'bold', 'bolt', 'book', 'bookmark', 'bookmark-fill', 'box', 'briefcase',
|
|
2473
|
+
'building', 'building-columns', 'bullseye', 'calculator', 'calendar', 'calendar-clock',
|
|
2474
|
+
'calendar-minus', 'calendar-plus', 'calendar-times', 'camera', 'car', 'caret-down',
|
|
2475
|
+
'caret-left', 'caret-right', 'caret-up', 'cart-arrow-down', 'cart-minus', 'cart-plus',
|
|
2476
|
+
'case-sensitive', 'chart-bar', 'chart-line', 'chart-pie', 'chart-scatter', 'check',
|
|
2477
|
+
'check-circle', 'check-square', 'chevron-circle-down', 'chevron-circle-left',
|
|
2478
|
+
'chevron-circle-right', 'chevron-circle-up', 'chevron-down', 'chevron-left', 'chevron-right',
|
|
2479
|
+
'chevron-up', 'circle', 'circle-fill', 'circle-off', 'circle-on', 'clipboard', 'clock',
|
|
2480
|
+
'clone', 'cloud', 'cloud-download', 'cloud-upload', 'code', 'code-branch', 'cog', 'columns-2',
|
|
2481
|
+
'comment', 'comments', 'compass', 'compress', 'copy', 'credit-card', 'crown', 'database',
|
|
2482
|
+
'delete-left', 'desktop', 'directions', 'directions-alt', 'discord', 'dollar', 'dot',
|
|
2483
|
+
'download', 'eject', 'ellipsis-h', 'ellipsis-v', 'envelope', 'equals', 'eraser', 'ethereum',
|
|
2484
|
+
'euro', 'exclamation-circle', 'exclamation-triangle', 'expand', 'external-link', 'eye',
|
|
2485
|
+
'eye-dropper', 'eye-slash', 'face-smile', 'facebook', 'fast-backward', 'fast-forward', 'file',
|
|
2486
|
+
'file-arrow-up', 'file-check', 'file-edit', 'file-excel', 'file-export', 'file-import',
|
|
2487
|
+
'file-o', 'file-pdf', 'file-plus', 'file-word', 'filter', 'filter-fill', 'filter-slash',
|
|
2488
|
+
'flag', 'flag-fill', 'folder', 'folder-open', 'folder-plus', 'forward', 'gauge', 'gift',
|
|
2489
|
+
'github', 'globe', 'google', 'graduation-cap', 'grid-2', 'grip', 'grip-horizontal',
|
|
2490
|
+
'grip-vertical', 'hammer', 'hashtag', 'heading', 'heading-1', 'heading-2', 'heading-3',
|
|
2491
|
+
'heading-4', 'heading-5', 'heading-6', 'headphones', 'heart', 'heart-fill', 'highlighter',
|
|
2492
|
+
'history', 'home', 'horizontal-rule', 'hourglass', 'id-card', 'image', 'images', 'inbox',
|
|
2493
|
+
'indent', 'indian-rupee', 'info', 'info-circle', 'instagram', 'italic', 'key', 'language',
|
|
2494
|
+
'lightbulb', 'link', 'linkedin', 'list', 'list-check', 'list-ol', 'list-tree', 'lock',
|
|
2495
|
+
'lock-open', 'map', 'map-marker', 'mars', 'megaphone', 'microchip', 'microchip-ai',
|
|
2496
|
+
'microphone', 'microsoft', 'minus', 'minus-circle', 'mobile', 'money-bill', 'moon',
|
|
2497
|
+
'note-sticky', 'objects-column', 'outdent', 'palette', 'paperclip', 'paragraph-left',
|
|
2498
|
+
'paragraph-right', 'pause', 'pause-circle', 'paypal', 'pen-line', 'pen-to-square', 'pencil',
|
|
2499
|
+
'percentage', 'phone', 'pinterest', 'play', 'play-circle', 'plus', 'plus-circle', 'pound',
|
|
2500
|
+
'power-off', 'prime', 'print', 'qrcode', 'question', 'question-circle', 'receipt',
|
|
2501
|
+
'rectangle-xmark', 'reddit', 'refresh', 'replay', 'reply', 'rows-2', 'save', 'search',
|
|
2502
|
+
'search-minus', 'search-plus', 'send', 'server', 'share-alt', 'shield', 'shop', 'shopping-bag',
|
|
2503
|
+
'shopping-cart', 'sidebar', 'sign-in', 'sign-out', 'signature', 'sitemap', 'slack', 'slash',
|
|
2504
|
+
'sliders-h', 'sliders-v', 'sort', 'sort-alpha-down', 'sort-alpha-down-alt', 'sort-alpha-up',
|
|
2505
|
+
'sort-alpha-up-alt', 'sort-alt', 'sort-alt-slash', 'sort-amount-down', 'sort-amount-down-alt',
|
|
2506
|
+
'sort-amount-up', 'sort-amount-up-alt', 'sort-down', 'sort-down-fill', 'sort-numeric-down',
|
|
2507
|
+
'sort-numeric-down-alt', 'sort-numeric-up', 'sort-numeric-up-alt', 'sort-up', 'sort-up-fill',
|
|
2508
|
+
'sparkles', 'spinner', 'spinner-dotted', 'square', 'stamp', 'star', 'star-fill', 'star-half',
|
|
2509
|
+
'star-half-fill', 'step-backward', 'step-backward-alt', 'step-forward', 'step-forward-alt',
|
|
2510
|
+
'stop', 'stop-circle', 'stopwatch', 'strikethrough', 'subscript', 'sun', 'superscript', 'sync',
|
|
2511
|
+
'table', 'tablet', 'tag', 'tags', 'telegram', 'text', 'text-color', 'th-large', 'thumbs-down',
|
|
2512
|
+
'thumbs-down-fill', 'thumbs-up', 'thumbs-up-fill', 'thumbtack', 'ticket', 'tiktok', 'times',
|
|
2513
|
+
'times-circle', 'trash', 'trophy', 'truck', 'turkish-lira', 'twitch', 'twitter', 'underline',
|
|
2514
|
+
'undo', 'unlock', 'upload', 'user', 'user-edit', 'user-minus', 'user-plus', 'users', 'venus',
|
|
2515
|
+
'verified', 'video', 'vimeo', 'volume-down', 'volume-off', 'volume-up', 'wallet', 'warehouse',
|
|
2516
|
+
'wave-pulse', 'whatsapp', 'wifi', 'window-maximize', 'window-minimize', 'wrench', 'youtube'
|
|
2517
|
+
];
|
|
2518
|
+
function searchIcons(term) {
|
|
2519
|
+
const needle = term.trim().toLowerCase();
|
|
2520
|
+
if (!needle)
|
|
2521
|
+
return NUCLEUS_ICON_CATALOG;
|
|
2522
|
+
return NUCLEUS_ICON_CATALOG.filter((name) => name.includes(needle));
|
|
2523
|
+
}
|
|
2524
|
+
function iconClassOf(name) {
|
|
2525
|
+
return name ? (name.startsWith('pi ') ? name : `pi pi-${name}`) : '';
|
|
2526
|
+
}
|
|
2527
|
+
function iconNameOf(value) {
|
|
2528
|
+
if (!value)
|
|
2529
|
+
return null;
|
|
2530
|
+
const trimmed = value.trim().replace(/^pi\s+/, '').replace(/^pi-/, '');
|
|
2531
|
+
return trimmed || null;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
class NucleusIconPicker {
|
|
2535
|
+
language = inject(NucleusLanguage);
|
|
2536
|
+
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
2537
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
2538
|
+
inputId = input('', ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
|
|
2539
|
+
panel = viewChild.required('popover');
|
|
2540
|
+
search = viewChild('searchBox', ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
|
|
2541
|
+
term = signal('', ...(ngDevMode ? [{ debugName: "term" }] : /* istanbul ignore next */ []));
|
|
2542
|
+
labels = computed(() => commonCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
2543
|
+
name = computed(() => iconNameOf(this.value()), ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
2544
|
+
iconClass = computed(() => iconClassOf(this.name()), ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
|
|
2545
|
+
results = computed(() => searchIcons(this.term()), ...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
|
|
2546
|
+
onShow() {
|
|
2547
|
+
this.term.set('');
|
|
2548
|
+
setTimeout(() => this.search()?.nativeElement.focus(), 0);
|
|
2549
|
+
}
|
|
2550
|
+
pick(icon) {
|
|
2551
|
+
this.value.set(icon ? `pi pi-${icon}` : null);
|
|
2552
|
+
this.panel().hide();
|
|
2553
|
+
}
|
|
2554
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusIconPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2555
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusIconPicker, isStandalone: true, selector: "nucleus-icon-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["popover"], descendants: true, isSignal: true }, { propertyName: "search", first: true, predicate: ["searchBox"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2556
|
+
@let copy = labels();
|
|
2557
|
+
|
|
2558
|
+
<button
|
|
2559
|
+
type="button"
|
|
2560
|
+
class="trigger"
|
|
2561
|
+
[id]="inputId()"
|
|
2562
|
+
[disabled]="disabled()"
|
|
2563
|
+
[attr.aria-label]="copy.searchIcon"
|
|
2564
|
+
(click)="panel().toggle($event)"
|
|
2565
|
+
>
|
|
2566
|
+
<span class="preview" [class.is-empty]="!name()">
|
|
2567
|
+
<i [class]="name() ? iconClass() : 'pi pi-image'" aria-hidden="true"></i>
|
|
2568
|
+
</span>
|
|
2569
|
+
<span class="name" [class.is-empty]="!name()">{{ name() ?? copy.noIcon }}</span>
|
|
2570
|
+
<i class="pi pi-chevron-down caret" aria-hidden="true"></i>
|
|
2571
|
+
</button>
|
|
2572
|
+
|
|
2573
|
+
<p-popover #popover appendTo="body" [dismissable]="true" (onShow)="onShow()">
|
|
2574
|
+
<div class="panel">
|
|
2575
|
+
<div class="panel-head">
|
|
2576
|
+
<input
|
|
2577
|
+
#searchBox
|
|
2578
|
+
pInputText
|
|
2579
|
+
type="search"
|
|
2580
|
+
autocomplete="off"
|
|
2581
|
+
[placeholder]="copy.searchIcon"
|
|
2582
|
+
[ngModel]="term()"
|
|
2583
|
+
(ngModelChange)="term.set($event)"
|
|
2584
|
+
[ngModelOptions]="{ standalone: true }"
|
|
2585
|
+
/>
|
|
2586
|
+
<button type="button" class="clear" (click)="pick(null)">{{ copy.noIcon }}</button>
|
|
2587
|
+
</div>
|
|
2588
|
+
|
|
2589
|
+
@if (results().length) {
|
|
2590
|
+
<div class="grid">
|
|
2591
|
+
@for (icon of results(); track icon) {
|
|
2592
|
+
<button
|
|
2593
|
+
type="button"
|
|
2594
|
+
class="cell"
|
|
2595
|
+
[class.is-active]="icon === name()"
|
|
2596
|
+
[title]="icon"
|
|
2597
|
+
[attr.aria-label]="icon"
|
|
2598
|
+
(click)="pick(icon)"
|
|
2599
|
+
>
|
|
2600
|
+
<i [class]="'pi pi-' + icon" aria-hidden="true"></i>
|
|
2601
|
+
</button>
|
|
2602
|
+
}
|
|
2603
|
+
</div>
|
|
2604
|
+
} @else {
|
|
2605
|
+
<p class="empty">{{ copy.noMatches }}</p>
|
|
2606
|
+
}
|
|
2607
|
+
</div>
|
|
2608
|
+
</p-popover>
|
|
2609
|
+
`, isInline: true, styles: [":host{display:block}.trigger{display:flex;align-items:center;gap:10px;width:100%;padding:0 10px;height:38px;border:1px solid var(--ac-border);border-radius:8px;background:var(--ac-surface);color:var(--ac-text);font:inherit;font-size:14px;text-align:left;cursor:pointer}.trigger:disabled{opacity:.6;cursor:default}.trigger:not(:disabled):hover{border-color:color-mix(in srgb,var(--ac-accent) 55%,var(--ac-border))}.preview{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;background:color-mix(in srgb,var(--ac-accent) 16%,transparent);color:var(--ac-accent);font-size:13px}.preview.is-empty{background:color-mix(in srgb,var(--ac-text) 8%,transparent);color:var(--ac-text-muted)}.name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.name.is-empty{color:var(--ac-text-muted)}.caret{color:var(--ac-text-muted);font-size:11px}.panel{width:min(21rem,80vw)}.panel-head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.panel-head input{flex:1}.clear{padding:0 10px;height:34px;border:1px solid var(--ac-border);border-radius:8px;background:transparent;color:var(--ac-text-muted);font:inherit;font-size:12px;white-space:nowrap;cursor:pointer}.clear:hover{color:var(--ac-text)}.grid{display:grid;grid-template-columns:repeat(8,1fr);gap:4px;overflow-y:auto;max-height:16rem}.cell{display:inline-flex;align-items:center;justify-content:center;aspect-ratio:1;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--ac-text);font-size:15px;cursor:pointer}.cell:hover{background:color-mix(in srgb,var(--ac-text) 10%,transparent)}.cell.is-active{border-color:var(--ac-accent);background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-accent)}.empty{padding:18px 4px;color:var(--ac-text-muted);font-size:13px;text-align:center}@media(max-width:420px){.grid{grid-template-columns:repeat(6,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i2$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2610
|
+
}
|
|
2611
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusIconPicker, decorators: [{
|
|
2612
|
+
type: Component,
|
|
2613
|
+
args: [{ selector: 'nucleus-icon-picker', imports: [FormsModule, InputTextModule, PopoverModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2614
|
+
@let copy = labels();
|
|
2615
|
+
|
|
2616
|
+
<button
|
|
2617
|
+
type="button"
|
|
2618
|
+
class="trigger"
|
|
2619
|
+
[id]="inputId()"
|
|
2620
|
+
[disabled]="disabled()"
|
|
2621
|
+
[attr.aria-label]="copy.searchIcon"
|
|
2622
|
+
(click)="panel().toggle($event)"
|
|
2623
|
+
>
|
|
2624
|
+
<span class="preview" [class.is-empty]="!name()">
|
|
2625
|
+
<i [class]="name() ? iconClass() : 'pi pi-image'" aria-hidden="true"></i>
|
|
2626
|
+
</span>
|
|
2627
|
+
<span class="name" [class.is-empty]="!name()">{{ name() ?? copy.noIcon }}</span>
|
|
2628
|
+
<i class="pi pi-chevron-down caret" aria-hidden="true"></i>
|
|
2629
|
+
</button>
|
|
2630
|
+
|
|
2631
|
+
<p-popover #popover appendTo="body" [dismissable]="true" (onShow)="onShow()">
|
|
2632
|
+
<div class="panel">
|
|
2633
|
+
<div class="panel-head">
|
|
2634
|
+
<input
|
|
2635
|
+
#searchBox
|
|
2636
|
+
pInputText
|
|
2637
|
+
type="search"
|
|
2638
|
+
autocomplete="off"
|
|
2639
|
+
[placeholder]="copy.searchIcon"
|
|
2640
|
+
[ngModel]="term()"
|
|
2641
|
+
(ngModelChange)="term.set($event)"
|
|
2642
|
+
[ngModelOptions]="{ standalone: true }"
|
|
2643
|
+
/>
|
|
2644
|
+
<button type="button" class="clear" (click)="pick(null)">{{ copy.noIcon }}</button>
|
|
2645
|
+
</div>
|
|
2646
|
+
|
|
2647
|
+
@if (results().length) {
|
|
2648
|
+
<div class="grid">
|
|
2649
|
+
@for (icon of results(); track icon) {
|
|
2650
|
+
<button
|
|
2651
|
+
type="button"
|
|
2652
|
+
class="cell"
|
|
2653
|
+
[class.is-active]="icon === name()"
|
|
2654
|
+
[title]="icon"
|
|
2655
|
+
[attr.aria-label]="icon"
|
|
2656
|
+
(click)="pick(icon)"
|
|
2657
|
+
>
|
|
2658
|
+
<i [class]="'pi pi-' + icon" aria-hidden="true"></i>
|
|
2659
|
+
</button>
|
|
2660
|
+
}
|
|
2661
|
+
</div>
|
|
2662
|
+
} @else {
|
|
2663
|
+
<p class="empty">{{ copy.noMatches }}</p>
|
|
2664
|
+
}
|
|
2665
|
+
</div>
|
|
2666
|
+
</p-popover>
|
|
2667
|
+
`, styles: [":host{display:block}.trigger{display:flex;align-items:center;gap:10px;width:100%;padding:0 10px;height:38px;border:1px solid var(--ac-border);border-radius:8px;background:var(--ac-surface);color:var(--ac-text);font:inherit;font-size:14px;text-align:left;cursor:pointer}.trigger:disabled{opacity:.6;cursor:default}.trigger:not(:disabled):hover{border-color:color-mix(in srgb,var(--ac-accent) 55%,var(--ac-border))}.preview{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;background:color-mix(in srgb,var(--ac-accent) 16%,transparent);color:var(--ac-accent);font-size:13px}.preview.is-empty{background:color-mix(in srgb,var(--ac-text) 8%,transparent);color:var(--ac-text-muted)}.name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.name.is-empty{color:var(--ac-text-muted)}.caret{color:var(--ac-text-muted);font-size:11px}.panel{width:min(21rem,80vw)}.panel-head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.panel-head input{flex:1}.clear{padding:0 10px;height:34px;border:1px solid var(--ac-border);border-radius:8px;background:transparent;color:var(--ac-text-muted);font:inherit;font-size:12px;white-space:nowrap;cursor:pointer}.clear:hover{color:var(--ac-text)}.grid{display:grid;grid-template-columns:repeat(8,1fr);gap:4px;overflow-y:auto;max-height:16rem}.cell{display:inline-flex;align-items:center;justify-content:center;aspect-ratio:1;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--ac-text);font-size:15px;cursor:pointer}.cell:hover{background:color-mix(in srgb,var(--ac-text) 10%,transparent)}.cell.is-active{border-color:var(--ac-accent);background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-accent)}.empty{padding:18px 4px;color:var(--ac-text-muted);font-size:13px;text-align:center}@media(max-width:420px){.grid{grid-template-columns:repeat(6,1fr)}}\n"] }]
|
|
2668
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], panel: [{ type: i0.ViewChild, args: ['popover', { isSignal: true }] }], search: [{ type: i0.ViewChild, args: ['searchBox', { isSignal: true }] }] } });
|
|
2669
|
+
|
|
2670
|
+
const NUCLEUS_ACCENTS = ACCENTS.map((accent) => ({
|
|
2671
|
+
id: accent.id,
|
|
2672
|
+
light: accent.light,
|
|
2673
|
+
dark: accent.dark,
|
|
2674
|
+
}));
|
|
2675
|
+
class NucleusColorPicker {
|
|
2676
|
+
language = inject(NucleusLanguage);
|
|
2677
|
+
value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
2678
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
2679
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
2680
|
+
labels = computed(() => commonCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
2681
|
+
options = computed(() => {
|
|
2682
|
+
const names = themeCopyOf(this.language.lang()).accents;
|
|
2683
|
+
return NUCLEUS_ACCENTS.map((accent) => ({ color: accent.light, label: names[accent.id] }));
|
|
2684
|
+
}, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
2685
|
+
pick(color) {
|
|
2686
|
+
this.value.set(color);
|
|
2687
|
+
}
|
|
2688
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusColorPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2689
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusColorPicker, isStandalone: true, selector: "nucleus-color-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: `
|
|
2690
|
+
@let copy = labels();
|
|
2691
|
+
|
|
2692
|
+
<div class="row" role="radiogroup" [attr.aria-label]="ariaLabel() || copy.choose">
|
|
2693
|
+
<button
|
|
2694
|
+
type="button"
|
|
2695
|
+
class="none"
|
|
2696
|
+
role="radio"
|
|
2697
|
+
[class.is-active]="!value()"
|
|
2698
|
+
[attr.aria-checked]="!value()"
|
|
2699
|
+
[attr.aria-label]="copy.noColor"
|
|
2700
|
+
[title]="copy.noColor"
|
|
2701
|
+
[disabled]="disabled()"
|
|
2702
|
+
(click)="pick(null)"
|
|
2703
|
+
>
|
|
2704
|
+
<i class="pi pi-ban" aria-hidden="true"></i>
|
|
2705
|
+
</button>
|
|
2706
|
+
|
|
2707
|
+
@for (option of options(); track option.color) {
|
|
2708
|
+
<button
|
|
2709
|
+
type="button"
|
|
2710
|
+
class="swatch"
|
|
2711
|
+
role="radio"
|
|
2712
|
+
[class.is-active]="option.color === value()"
|
|
2713
|
+
[attr.aria-checked]="option.color === value()"
|
|
2714
|
+
[attr.aria-label]="option.label"
|
|
2715
|
+
[title]="option.label + ' · ' + option.color"
|
|
2716
|
+
[style.background]="option.color"
|
|
2717
|
+
[disabled]="disabled()"
|
|
2718
|
+
(click)="pick(option.color)"
|
|
2719
|
+
>
|
|
2720
|
+
@if (option.color === value()) {
|
|
2721
|
+
<i class="pi pi-check" aria-hidden="true"></i>
|
|
2722
|
+
}
|
|
2723
|
+
</button>
|
|
2724
|
+
}
|
|
2725
|
+
</div>
|
|
2726
|
+
`, isInline: true, styles: [":host{display:block}.row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-height:38px}.swatch{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:2px solid transparent;border-radius:50%;color:#fff;cursor:pointer;transition:transform .12s ease}.swatch:hover:not(:disabled){transform:scale(1.12)}.swatch:disabled{cursor:default;opacity:.6}.swatch.is-active{border-color:var(--ac-text);box-shadow:0 0 0 2px var(--ac-bg) inset}.swatch i{font-size:11px;text-shadow:0 0 3px rgba(0,0,0,.5)}.none{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:1px dashed var(--ac-border);border-radius:50%;background:transparent;color:var(--ac-text-muted);cursor:pointer}.none.is-active{border-style:solid;border-color:var(--ac-text);color:var(--ac-text)}.none i{font-size:11px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2727
|
+
}
|
|
2728
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusColorPicker, decorators: [{
|
|
2729
|
+
type: Component,
|
|
2730
|
+
args: [{ selector: 'nucleus-color-picker', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2731
|
+
@let copy = labels();
|
|
2732
|
+
|
|
2733
|
+
<div class="row" role="radiogroup" [attr.aria-label]="ariaLabel() || copy.choose">
|
|
2734
|
+
<button
|
|
2735
|
+
type="button"
|
|
2736
|
+
class="none"
|
|
2737
|
+
role="radio"
|
|
2738
|
+
[class.is-active]="!value()"
|
|
2739
|
+
[attr.aria-checked]="!value()"
|
|
2740
|
+
[attr.aria-label]="copy.noColor"
|
|
2741
|
+
[title]="copy.noColor"
|
|
2742
|
+
[disabled]="disabled()"
|
|
2743
|
+
(click)="pick(null)"
|
|
2744
|
+
>
|
|
2745
|
+
<i class="pi pi-ban" aria-hidden="true"></i>
|
|
2746
|
+
</button>
|
|
2747
|
+
|
|
2748
|
+
@for (option of options(); track option.color) {
|
|
2749
|
+
<button
|
|
2750
|
+
type="button"
|
|
2751
|
+
class="swatch"
|
|
2752
|
+
role="radio"
|
|
2753
|
+
[class.is-active]="option.color === value()"
|
|
2754
|
+
[attr.aria-checked]="option.color === value()"
|
|
2755
|
+
[attr.aria-label]="option.label"
|
|
2756
|
+
[title]="option.label + ' · ' + option.color"
|
|
2757
|
+
[style.background]="option.color"
|
|
2758
|
+
[disabled]="disabled()"
|
|
2759
|
+
(click)="pick(option.color)"
|
|
2760
|
+
>
|
|
2761
|
+
@if (option.color === value()) {
|
|
2762
|
+
<i class="pi pi-check" aria-hidden="true"></i>
|
|
2763
|
+
}
|
|
2764
|
+
</button>
|
|
2765
|
+
}
|
|
2766
|
+
</div>
|
|
2767
|
+
`, styles: [":host{display:block}.row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-height:38px}.swatch{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:2px solid transparent;border-radius:50%;color:#fff;cursor:pointer;transition:transform .12s ease}.swatch:hover:not(:disabled){transform:scale(1.12)}.swatch:disabled{cursor:default;opacity:.6}.swatch.is-active{border-color:var(--ac-text);box-shadow:0 0 0 2px var(--ac-bg) inset}.swatch i{font-size:11px;text-shadow:0 0 3px rgba(0,0,0,.5)}.none{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:1px dashed var(--ac-border);border-radius:50%;background:transparent;color:var(--ac-text-muted);cursor:pointer}.none.is-active{border-style:solid;border-color:var(--ac-text);color:var(--ac-text)}.none i{font-size:11px}\n"] }]
|
|
2768
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
1483
2769
|
|
|
1484
2770
|
class NucleusForm {
|
|
2771
|
+
language = inject(NucleusLanguage);
|
|
2772
|
+
host = inject(ElementRef);
|
|
2773
|
+
injector = inject(Injector);
|
|
1485
2774
|
fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
1486
2775
|
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1487
2776
|
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1488
2777
|
columns = input(null, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
2778
|
+
autofocus = input(false, ...(ngDevMode ? [{ debugName: "autofocus" }] : /* istanbul ignore next */ []));
|
|
1489
2779
|
valueChange = output();
|
|
2780
|
+
labels = computed(() => commonCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
2781
|
+
landed = false;
|
|
2782
|
+
constructor() {
|
|
2783
|
+
effect(() => {
|
|
2784
|
+
const ready = this.autofocus() && !this.readonly() && this.fields().length > 0;
|
|
2785
|
+
if (!ready || this.landed)
|
|
2786
|
+
return;
|
|
2787
|
+
this.landed = true;
|
|
2788
|
+
afterNextRender(() => {
|
|
2789
|
+
const first = fieldsIn(this.host.nativeElement)[0];
|
|
2790
|
+
if (first)
|
|
2791
|
+
moveTo(first);
|
|
2792
|
+
}, { injector: this.injector });
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
1490
2795
|
read(field) {
|
|
1491
2796
|
return this.value()[field.key] ?? null;
|
|
1492
2797
|
}
|
|
@@ -1494,6 +2799,10 @@ class NucleusForm {
|
|
|
1494
2799
|
const raw = this.value()[field.key];
|
|
1495
2800
|
return raw === null || raw === undefined ? '' : String(raw);
|
|
1496
2801
|
}
|
|
2802
|
+
nullableText(field) {
|
|
2803
|
+
const raw = this.value()[field.key];
|
|
2804
|
+
return raw === null || raw === undefined || raw === '' ? null : String(raw);
|
|
2805
|
+
}
|
|
1497
2806
|
checked(field) {
|
|
1498
2807
|
return this.value()[field.key] === true;
|
|
1499
2808
|
}
|
|
@@ -1503,6 +2812,9 @@ class NucleusForm {
|
|
|
1503
2812
|
isDisabled(field) {
|
|
1504
2813
|
return this.readonly() || field.disabled === true;
|
|
1505
2814
|
}
|
|
2815
|
+
isSearchable(field) {
|
|
2816
|
+
return isFieldSearchable(field);
|
|
2817
|
+
}
|
|
1506
2818
|
fieldClass(field) {
|
|
1507
2819
|
const span = field.span ?? 1;
|
|
1508
2820
|
return `is-span-${span === 'full' ? 'full' : span} is-size-${resolveFieldSize(field)}`;
|
|
@@ -1518,7 +2830,7 @@ class NucleusForm {
|
|
|
1518
2830
|
return field.kind ?? 'text';
|
|
1519
2831
|
}
|
|
1520
2832
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1521
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusForm, isStandalone: true, selector: "nucleus-form", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"ns-form-grid\" [class]=\"gridClass()\">\n @for (field of fields(); track field.key) {\n <div class=\"ns-field\" [class]=\"fieldClass(field)\">\n @if (kindOf(field) !== 'checkbox' && kindOf(field) !== 'switch') {\n <label [attr.for]=\"'nf-' + field.key\"
|
|
2833
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusForm, isStandalone: true, selector: "nucleus-form", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, hostDirectives: [{ directive: NucleusEnterAdvance }], ngImport: i0, template: "<div class=\"ns-form-grid\" [class]=\"gridClass()\">\n @for (field of fields(); track field.key) {\n <div class=\"ns-field\" [class]=\"fieldClass(field)\">\n @if (kindOf(field) !== 'checkbox' && kindOf(field) !== 'switch') {\n <label [attr.for]=\"kindOf(field) === 'color' ? null : 'nf-' + field.key\">\n {{ field.label }}\n </label>\n }\n\n @switch (kindOf(field)) {\n @case ('textarea') {\n <textarea\n pTextarea\n [id]=\"'nf-' + field.key\"\n [rows]=\"field.rows ?? 3\"\n [ngModel]=\"text(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [attr.maxlength]=\"field.maxlength ?? null\"\n [attr.placeholder]=\"field.placeholder ?? null\"\n [ngModelOptions]=\"{ standalone: true }\"\n fluid\n ></textarea>\n }\n @case ('select') {\n <p-select\n [inputId]=\"'nf-' + field.key\"\n [options]=\"optionsOf(field)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"read(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [placeholder]=\"field.placeholder ?? labels().choose\"\n [showClear]=\"field.clearable === true\"\n [filter]=\"isSearchable(field)\"\n filterBy=\"label\"\n [filterPlaceholder]=\"labels().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ngModelOptions]=\"{ standalone: true }\"\n fluid\n />\n }\n @case ('icon') {\n <nucleus-icon-picker\n [inputId]=\"'nf-' + field.key\"\n [value]=\"nullableText(field)\"\n (valueChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n />\n }\n @case ('color') {\n <nucleus-color-picker\n [value]=\"nullableText(field)\"\n (valueChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ariaLabel]=\"field.label\"\n />\n }\n @case ('checkbox') {\n <div class=\"ns-inline\">\n <p-checkbox\n [inputId]=\"'nf-' + field.key\"\n [binary]=\"true\"\n [ngModel]=\"checked(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n <label [attr.for]=\"'nf-' + field.key\" class=\"ns-inline-label\">{{ field.label }}</label>\n </div>\n }\n @case ('switch') {\n <div class=\"ns-inline\">\n <p-toggleswitch\n [inputId]=\"'nf-' + field.key\"\n [ngModel]=\"checked(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n <label [attr.for]=\"'nf-' + field.key\" class=\"ns-inline-label\">{{ field.label }}</label>\n </div>\n }\n @default {\n <input\n pInputText\n [id]=\"'nf-' + field.key\"\n [type]=\"kindOf(field) === 'number' ? 'number' : kindOf(field)\"\n [ngModel]=\"text(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [attr.maxlength]=\"field.maxlength ?? null\"\n [attr.placeholder]=\"field.placeholder ?? null\"\n [ngModelOptions]=\"{ standalone: true }\"\n autocomplete=\"off\"\n fluid\n />\n }\n }\n\n @if (field.hint) {\n <p class=\"ns-hint\">{{ field.hint }}</p>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i2$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i5.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i6.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusColorPicker, selector: "nucleus-color-picker", inputs: ["value", "disabled", "ariaLabel"], outputs: ["valueChange"] }, { kind: "component", type: NucleusIconPicker, selector: "nucleus-icon-picker", inputs: ["value", "disabled", "inputId"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1522
2834
|
}
|
|
1523
2835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusForm, decorators: [{
|
|
1524
2836
|
type: Component,
|
|
@@ -1529,8 +2841,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1529
2841
|
TextareaModule,
|
|
1530
2842
|
ToggleSwitchModule,
|
|
1531
2843
|
FormsModule,
|
|
1532
|
-
|
|
1533
|
-
|
|
2844
|
+
NucleusColorPicker,
|
|
2845
|
+
NucleusIconPicker,
|
|
2846
|
+
], hostDirectives: [NucleusEnterAdvance], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ns-form-grid\" [class]=\"gridClass()\">\n @for (field of fields(); track field.key) {\n <div class=\"ns-field\" [class]=\"fieldClass(field)\">\n @if (kindOf(field) !== 'checkbox' && kindOf(field) !== 'switch') {\n <label [attr.for]=\"kindOf(field) === 'color' ? null : 'nf-' + field.key\">\n {{ field.label }}\n </label>\n }\n\n @switch (kindOf(field)) {\n @case ('textarea') {\n <textarea\n pTextarea\n [id]=\"'nf-' + field.key\"\n [rows]=\"field.rows ?? 3\"\n [ngModel]=\"text(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [attr.maxlength]=\"field.maxlength ?? null\"\n [attr.placeholder]=\"field.placeholder ?? null\"\n [ngModelOptions]=\"{ standalone: true }\"\n fluid\n ></textarea>\n }\n @case ('select') {\n <p-select\n [inputId]=\"'nf-' + field.key\"\n [options]=\"optionsOf(field)\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"read(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [placeholder]=\"field.placeholder ?? labels().choose\"\n [showClear]=\"field.clearable === true\"\n [filter]=\"isSearchable(field)\"\n filterBy=\"label\"\n [filterPlaceholder]=\"labels().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ngModelOptions]=\"{ standalone: true }\"\n fluid\n />\n }\n @case ('icon') {\n <nucleus-icon-picker\n [inputId]=\"'nf-' + field.key\"\n [value]=\"nullableText(field)\"\n (valueChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n />\n }\n @case ('color') {\n <nucleus-color-picker\n [value]=\"nullableText(field)\"\n (valueChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ariaLabel]=\"field.label\"\n />\n }\n @case ('checkbox') {\n <div class=\"ns-inline\">\n <p-checkbox\n [inputId]=\"'nf-' + field.key\"\n [binary]=\"true\"\n [ngModel]=\"checked(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n <label [attr.for]=\"'nf-' + field.key\" class=\"ns-inline-label\">{{ field.label }}</label>\n </div>\n }\n @case ('switch') {\n <div class=\"ns-inline\">\n <p-toggleswitch\n [inputId]=\"'nf-' + field.key\"\n [ngModel]=\"checked(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n <label [attr.for]=\"'nf-' + field.key\" class=\"ns-inline-label\">{{ field.label }}</label>\n </div>\n }\n @default {\n <input\n pInputText\n [id]=\"'nf-' + field.key\"\n [type]=\"kindOf(field) === 'number' ? 'number' : kindOf(field)\"\n [ngModel]=\"text(field)\"\n (ngModelChange)=\"write(field, $event)\"\n [disabled]=\"isDisabled(field)\"\n [attr.maxlength]=\"field.maxlength ?? null\"\n [attr.placeholder]=\"field.placeholder ?? null\"\n [ngModelOptions]=\"{ standalone: true }\"\n autocomplete=\"off\"\n fluid\n />\n }\n }\n\n @if (field.hint) {\n <p class=\"ns-hint\">{{ field.hint }}</p>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}\n"] }]
|
|
2847
|
+
}], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
1534
2848
|
|
|
1535
2849
|
class NucleusCell {
|
|
1536
2850
|
nucleusCell = input.required(...(ngDevMode ? [{ debugName: "nucleusCell" }] : /* istanbul ignore next */ []));
|
|
@@ -1552,12 +2866,24 @@ class NucleusList {
|
|
|
1552
2866
|
empty = input('', ...(ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []));
|
|
1553
2867
|
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1554
2868
|
minWidth = input('640px', ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
|
|
2869
|
+
skeletonRows = input(5, ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
|
|
1555
2870
|
rowClass = input(() => '', ...(ngDevMode ? [{ debugName: "rowClass" }] : /* istanbul ignore next */ []));
|
|
1556
2871
|
action = output();
|
|
1557
2872
|
toggle = output();
|
|
1558
2873
|
cells = contentChildren(NucleusCell, ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
|
|
1559
2874
|
tableStyle = computed(() => ({ 'min-width': this.minWidth() }), ...(ngDevMode ? [{ debugName: "tableStyle" }] : /* istanbul ignore next */ []));
|
|
1560
2875
|
showEmpty = computed(() => !this.loading() && this.rows().length === 0, ...(ngDevMode ? [{ debugName: "showEmpty" }] : /* istanbul ignore next */ []));
|
|
2876
|
+
placeholders = computed(() => Array.from({ length: Math.max(this.skeletonRows(), 1) }, (_, index) => ({ index })), ...(ngDevMode ? [{ debugName: "placeholders" }] : /* istanbul ignore next */ []));
|
|
2877
|
+
data = computed(() => this.loading() ? this.placeholders() : this.rows(), ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
2878
|
+
skeletonWidth(column) {
|
|
2879
|
+
if (column.kind === 'switch')
|
|
2880
|
+
return '84px';
|
|
2881
|
+
if (column.align === 'right')
|
|
2882
|
+
return '40px';
|
|
2883
|
+
if (column.width)
|
|
2884
|
+
return '60%';
|
|
2885
|
+
return column.strong ? '58%' : '78%';
|
|
2886
|
+
}
|
|
1561
2887
|
cellTemplate(key) {
|
|
1562
2888
|
return this.cells().find((cell) => cell.nucleusCell() === key)?.template ?? null;
|
|
1563
2889
|
}
|
|
@@ -1573,6 +2899,9 @@ class NucleusList {
|
|
|
1573
2899
|
visibleActions(row) {
|
|
1574
2900
|
return this.actions().filter((entry) => !entry.visible || entry.visible(row));
|
|
1575
2901
|
}
|
|
2902
|
+
isActionLocked(entry, row) {
|
|
2903
|
+
return entry.disabled ? entry.disabled(row) : false;
|
|
2904
|
+
}
|
|
1576
2905
|
run(entry, row) {
|
|
1577
2906
|
this.action.emit({ id: entry.id, row });
|
|
1578
2907
|
}
|
|
@@ -1591,12 +2920,37 @@ class NucleusList {
|
|
|
1591
2920
|
this.toggle.emit({ key: column.key, row, value: !this.isChecked(column, row) });
|
|
1592
2921
|
}
|
|
1593
2922
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1594
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusList, isStandalone: true, selector: "nucleus-list", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action", toggle: "toggle" }, queries: [{ propertyName: "cells", predicate: NucleusCell, isSignal: true }], ngImport: i0, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"
|
|
2923
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusList, isStandalone: true, selector: "nucleus-list", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, skeletonRows: { classPropertyName: "skeletonRows", publicName: "skeletonRows", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action", toggle: "toggle" }, queries: [{ propertyName: "cells", predicate: NucleusCell, isSignal: true }], ngImport: i0, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"data()\" styleClass=\"ns-table\" [tableStyle]=\"tableStyle()\">\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1595
2924
|
}
|
|
1596
2925
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, decorators: [{
|
|
1597
2926
|
type: Component,
|
|
1598
|
-
args: [{ selector: 'nucleus-list', imports: [NgTemplateOutlet, ButtonModule, TableModule, TagModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"
|
|
1599
|
-
}], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], toggle: [{ type: i0.Output, args: ["toggle"] }], cells: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NucleusCell), { isSignal: true }] }] } });
|
|
2927
|
+
args: [{ selector: 'nucleus-list', imports: [NgTemplateOutlet, ButtonModule, SkeletonModule, TableModule, TagModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"data()\" styleClass=\"ns-table\" [tableStyle]=\"tableStyle()\">\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"] }]
|
|
2928
|
+
}], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], skeletonRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonRows", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], toggle: [{ type: i0.Output, args: ["toggle"] }], cells: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NucleusCell), { isSignal: true }] }] } });
|
|
2929
|
+
|
|
2930
|
+
function toggleCollapsed(collapsed, id) {
|
|
2931
|
+
const next = new Set(collapsed);
|
|
2932
|
+
if (!next.delete(id))
|
|
2933
|
+
next.add(id);
|
|
2934
|
+
return next;
|
|
2935
|
+
}
|
|
2936
|
+
function hasChildren(rows, id) {
|
|
2937
|
+
return rows.some((row) => row.parentId === id);
|
|
2938
|
+
}
|
|
2939
|
+
function visibleTreeRows(rows, collapsed) {
|
|
2940
|
+
if (collapsed.size === 0)
|
|
2941
|
+
return [...rows];
|
|
2942
|
+
const parents = new Map(rows.map((row) => [row.id, row.parentId]));
|
|
2943
|
+
const buried = (row) => {
|
|
2944
|
+
let parentId = row.parentId;
|
|
2945
|
+
while (parentId) {
|
|
2946
|
+
if (collapsed.has(parentId))
|
|
2947
|
+
return true;
|
|
2948
|
+
parentId = parents.get(parentId) ?? null;
|
|
2949
|
+
}
|
|
2950
|
+
return false;
|
|
2951
|
+
};
|
|
2952
|
+
return rows.filter((row) => !buried(row));
|
|
2953
|
+
}
|
|
1600
2954
|
|
|
1601
2955
|
const ORBIT = { rx: 78, ry: 196 };
|
|
1602
2956
|
class NucleusMark {
|
|
@@ -1650,6 +3004,238 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1650
3004
|
`, styles: [":host{display:block}svg{display:block}\n"] }]
|
|
1651
3005
|
}], propDecorators: { accent: [{ type: i0.Input, args: [{ isSignal: true, alias: "accent", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
1652
3006
|
|
|
3007
|
+
class NucleusAppearance {
|
|
3008
|
+
theme = inject(ThemeService);
|
|
3009
|
+
language = inject(NucleusLanguage);
|
|
3010
|
+
copy = computed(() => themeCopyOf(this.language.lang()), ...(ngDevMode ? [{ debugName: "copy" }] : /* istanbul ignore next */ []));
|
|
3011
|
+
appearance = this.theme.appearance;
|
|
3012
|
+
canvasId = this.theme.canvasId;
|
|
3013
|
+
frames = computed(() => {
|
|
3014
|
+
const labels = this.copy().frames;
|
|
3015
|
+
return FRAMES.map((frame) => ({ id: frame.id, label: labels[frame.id], tokens: frame.tokens }));
|
|
3016
|
+
}, ...(ngDevMode ? [{ debugName: "frames" }] : /* istanbul ignore next */ []));
|
|
3017
|
+
canvases = computed(() => {
|
|
3018
|
+
const labels = this.copy().canvases;
|
|
3019
|
+
const preview = (id) => {
|
|
3020
|
+
const resolved = canvasById(id === 'system' ? this.theme.systemCanvasId() : id);
|
|
3021
|
+
return {
|
|
3022
|
+
id,
|
|
3023
|
+
label: labels[id],
|
|
3024
|
+
bg: resolved.tokens.bg,
|
|
3025
|
+
surface: resolved.tokens.surface,
|
|
3026
|
+
text: resolved.tokens.text,
|
|
3027
|
+
icon: id === 'system' ? 'pi pi-desktop' : null,
|
|
3028
|
+
};
|
|
3029
|
+
};
|
|
3030
|
+
return [...CANVASES.map((canvas) => preview(canvas.id)), preview('system')];
|
|
3031
|
+
}, ...(ngDevMode ? [{ debugName: "canvases" }] : /* istanbul ignore next */ []));
|
|
3032
|
+
accents = computed(() => {
|
|
3033
|
+
const labels = this.copy().accents;
|
|
3034
|
+
const canvas = this.canvasId();
|
|
3035
|
+
const auto = accentOn(accentById(this.theme.frame().accent), canvas);
|
|
3036
|
+
return [
|
|
3037
|
+
{ id: 'auto', label: labels.auto, color: auto, ink: readableOn(auto) },
|
|
3038
|
+
...ACCENTS.map((accent) => {
|
|
3039
|
+
const color = accentOn(accent, canvas);
|
|
3040
|
+
return { id: accent.id, label: labels[accent.id], color, ink: readableOn(color) };
|
|
3041
|
+
}),
|
|
3042
|
+
];
|
|
3043
|
+
}, ...(ngDevMode ? [{ debugName: "accents" }] : /* istanbul ignore next */ []));
|
|
3044
|
+
densities = computed(() => {
|
|
3045
|
+
const labels = this.copy().densities;
|
|
3046
|
+
return DENSITIES.map((density) => ({
|
|
3047
|
+
id: density.id,
|
|
3048
|
+
label: labels[density.id],
|
|
3049
|
+
gap: Math.round(6 * density.scale),
|
|
3050
|
+
}));
|
|
3051
|
+
}, ...(ngDevMode ? [{ debugName: "densities" }] : /* istanbul ignore next */ []));
|
|
3052
|
+
pickFrame(id) {
|
|
3053
|
+
this.theme.setFrame(id);
|
|
3054
|
+
}
|
|
3055
|
+
pickCanvas(id) {
|
|
3056
|
+
this.theme.setCanvas(id);
|
|
3057
|
+
}
|
|
3058
|
+
pickAccent(id) {
|
|
3059
|
+
this.theme.setAccent(id);
|
|
3060
|
+
}
|
|
3061
|
+
pickDensity(id) {
|
|
3062
|
+
this.theme.setDensity(id);
|
|
3063
|
+
}
|
|
3064
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusAppearance, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3065
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusAppearance, isStandalone: true, selector: "nucleus-appearance", ngImport: i0, template: "@let t = copy();\n@let current = appearance();\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.frameTitle }}</h3>\n <p class=\"group-hint\">{{ t.frameHint }}</p>\n\n <div class=\"frames\" role=\"radiogroup\" [attr.aria-label]=\"t.frameTitle\">\n @for (frame of frames(); track frame.id) {\n <button\n type=\"button\"\n class=\"frame\"\n role=\"radio\"\n [class.is-active]=\"frame.id === current.frame\"\n [attr.aria-checked]=\"frame.id === current.frame\"\n [attr.aria-label]=\"frame.label\"\n [style.--f-bg]=\"frame.tokens.bg\"\n [style.--f-raised]=\"frame.tokens.raised\"\n [style.--f-ink]=\"frame.tokens.ink\"\n [style.--f-muted]=\"frame.tokens.muted\"\n [style.--f-line]=\"frame.tokens.line\"\n [style.--f-glow]=\"frame.tokens.glow\"\n (click)=\"pickFrame(frame.id)\"\n >\n <span class=\"frame-chip\" aria-hidden=\"true\">\n <span class=\"frame-bar\">\n <span class=\"frame-dot\"></span>\n </span>\n <span class=\"frame-lower\">\n <span class=\"frame-rail\">\n <span class=\"frame-pin\"></span>\n <span class=\"frame-row is-on\"></span>\n <span class=\"frame-row\"></span>\n <span class=\"frame-row\"></span>\n </span>\n <span class=\"frame-canvas\"></span>\n </span>\n </span>\n <span class=\"frame-name\">{{ frame.label }}</span>\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.canvasTitle }}</h3>\n <p class=\"group-hint\">{{ t.canvasHint }}</p>\n\n <div class=\"canvases\" role=\"radiogroup\" [attr.aria-label]=\"t.canvasTitle\">\n @for (canvas of canvases(); track canvas.id) {\n <button\n type=\"button\"\n class=\"canvas\"\n role=\"radio\"\n [class.is-active]=\"canvas.id === current.canvas\"\n [attr.aria-checked]=\"canvas.id === current.canvas\"\n (click)=\"pickCanvas(canvas.id)\"\n >\n <span\n class=\"canvas-chip\"\n aria-hidden=\"true\"\n [style.background]=\"canvas.bg\"\n [style.color]=\"canvas.text\"\n >\n @if (canvas.icon) {\n <i [class]=\"canvas.icon\"></i>\n } @else {\n <span class=\"canvas-sheet\" [style.background]=\"canvas.surface\"></span>\n }\n </span>\n <span class=\"canvas-name\">{{ canvas.label }}</span>\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.accentTitle }}</h3>\n <p class=\"group-hint\">{{ t.accentHint }}</p>\n\n <div class=\"accents\" role=\"radiogroup\" [attr.aria-label]=\"t.accentTitle\">\n @for (accent of accents(); track accent.id) {\n <button\n type=\"button\"\n class=\"accent\"\n role=\"radio\"\n [class.is-auto]=\"accent.id === 'auto'\"\n [class.is-active]=\"accent.id === current.accent\"\n [attr.aria-checked]=\"accent.id === current.accent\"\n [attr.aria-label]=\"accent.label\"\n [title]=\"accent.label\"\n [style.background]=\"accent.color\"\n [style.color]=\"accent.ink\"\n (click)=\"pickAccent(accent.id)\"\n >\n @if (accent.id === current.accent) {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n } @else if (accent.id === 'auto') {\n <i class=\"pi pi-sparkles\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.densityTitle }}</h3>\n <p class=\"group-hint\">{{ t.densityHint }}</p>\n\n <div class=\"densities\" role=\"radiogroup\" [attr.aria-label]=\"t.densityTitle\">\n @for (density of densities(); track density.id) {\n <button\n type=\"button\"\n class=\"density\"\n role=\"radio\"\n [class.is-active]=\"density.id === current.density\"\n [attr.aria-checked]=\"density.id === current.density\"\n (click)=\"pickDensity(density.id)\"\n >\n <span class=\"density-chip\" aria-hidden=\"true\" [style.gap.px]=\"density.gap\">\n <span class=\"density-row\"></span>\n <span class=\"density-row\"></span>\n <span class=\"density-row\"></span>\n </span>\n <span class=\"density-name\">{{ density.label }}</span>\n </button>\n }\n </div>\n</section>\n", styles: [":host{display:flex;flex-direction:column;gap:26px}.group{display:flex;flex-direction:column;gap:2px}.group-title{margin:0;font-size:13px;font-weight:600;letter-spacing:.02em}.group-hint{margin:0 0 12px;color:var(--ac-text-muted);font-size:12px;font-weight:300;line-height:1.45}.frames{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:10px}.frame{display:flex;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.frame:hover{color:var(--ac-text)}.frame.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.frame-chip{display:flex;overflow:hidden;flex-direction:column;height:52px;border:1px solid var(--f-line);border-radius:6px;background:var(--f-bg)}.frame-bar{display:flex;flex:none;align-items:center;height:12px;padding-left:5px;border-bottom:1px solid var(--f-line)}.frame-dot{width:5px;height:5px;border-radius:50%;background:var(--f-glow)}.frame-lower{display:flex;flex:1;min-height:0}.frame-rail{display:flex;flex:none;flex-direction:column;gap:3px;width:22px;padding:4px 3px;border-right:1px solid var(--f-line)}.frame-pin{height:7px;border-left:2px solid var(--f-glow);border-radius:2px;background:var(--f-raised)}.frame-row{height:3px;border-radius:2px;background:var(--f-muted);opacity:.55}.frame-row.is-on{background:var(--f-glow);opacity:1}.frame-canvas{flex:1;background:var(--ac-bg)}.frame-name{overflow:hidden;font-size:11px;font-weight:500;text-align:center;text-overflow:ellipsis;white-space:nowrap}.canvases{display:grid;grid-template-columns:repeat(auto-fit,minmax(70px,1fr));gap:8px}.canvas{display:flex;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.canvas:hover{color:var(--ac-text)}.canvas.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.canvas-chip{display:flex;align-items:center;justify-content:center;height:40px;border:1px solid var(--ac-border);border-radius:6px}.canvas-chip i{font-size:15px}.canvas-sheet{width:60%;height:58%;border-radius:3px;box-shadow:0 1px 4px #00000038}.canvas-name{font-size:11px;font-weight:500;text-align:center}.accents{display:flex;flex-wrap:wrap;gap:9px}.accent{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:2px solid transparent;border-radius:50%;cursor:pointer;transition:transform .12s ease}.accent:hover{transform:scale(1.12)}.accent.is-auto{border-style:dashed;border-color:var(--ac-border)}.accent.is-active{border-style:solid;border-color:var(--ac-text);box-shadow:0 0 0 2px var(--ac-surface) inset}.accent i{font-size:11px}.densities{display:flex;gap:10px}.density{display:flex;flex:1;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.density:hover{color:var(--ac-text)}.density.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.density-chip{display:flex;flex-direction:column;justify-content:center;height:42px;padding:0 10px;border:1px solid var(--ac-border);border-radius:6px;background:var(--ac-surface)}.density-row{height:3px;border-radius:2px;background:var(--ac-text-muted);opacity:.5}.density-name{font-size:11px;font-weight:500;text-align:center}@media(prefers-reduced-motion:reduce){.accent{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3066
|
+
}
|
|
3067
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusAppearance, decorators: [{
|
|
3068
|
+
type: Component,
|
|
3069
|
+
args: [{ selector: 'nucleus-appearance', changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let current = appearance();\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.frameTitle }}</h3>\n <p class=\"group-hint\">{{ t.frameHint }}</p>\n\n <div class=\"frames\" role=\"radiogroup\" [attr.aria-label]=\"t.frameTitle\">\n @for (frame of frames(); track frame.id) {\n <button\n type=\"button\"\n class=\"frame\"\n role=\"radio\"\n [class.is-active]=\"frame.id === current.frame\"\n [attr.aria-checked]=\"frame.id === current.frame\"\n [attr.aria-label]=\"frame.label\"\n [style.--f-bg]=\"frame.tokens.bg\"\n [style.--f-raised]=\"frame.tokens.raised\"\n [style.--f-ink]=\"frame.tokens.ink\"\n [style.--f-muted]=\"frame.tokens.muted\"\n [style.--f-line]=\"frame.tokens.line\"\n [style.--f-glow]=\"frame.tokens.glow\"\n (click)=\"pickFrame(frame.id)\"\n >\n <span class=\"frame-chip\" aria-hidden=\"true\">\n <span class=\"frame-bar\">\n <span class=\"frame-dot\"></span>\n </span>\n <span class=\"frame-lower\">\n <span class=\"frame-rail\">\n <span class=\"frame-pin\"></span>\n <span class=\"frame-row is-on\"></span>\n <span class=\"frame-row\"></span>\n <span class=\"frame-row\"></span>\n </span>\n <span class=\"frame-canvas\"></span>\n </span>\n </span>\n <span class=\"frame-name\">{{ frame.label }}</span>\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.canvasTitle }}</h3>\n <p class=\"group-hint\">{{ t.canvasHint }}</p>\n\n <div class=\"canvases\" role=\"radiogroup\" [attr.aria-label]=\"t.canvasTitle\">\n @for (canvas of canvases(); track canvas.id) {\n <button\n type=\"button\"\n class=\"canvas\"\n role=\"radio\"\n [class.is-active]=\"canvas.id === current.canvas\"\n [attr.aria-checked]=\"canvas.id === current.canvas\"\n (click)=\"pickCanvas(canvas.id)\"\n >\n <span\n class=\"canvas-chip\"\n aria-hidden=\"true\"\n [style.background]=\"canvas.bg\"\n [style.color]=\"canvas.text\"\n >\n @if (canvas.icon) {\n <i [class]=\"canvas.icon\"></i>\n } @else {\n <span class=\"canvas-sheet\" [style.background]=\"canvas.surface\"></span>\n }\n </span>\n <span class=\"canvas-name\">{{ canvas.label }}</span>\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.accentTitle }}</h3>\n <p class=\"group-hint\">{{ t.accentHint }}</p>\n\n <div class=\"accents\" role=\"radiogroup\" [attr.aria-label]=\"t.accentTitle\">\n @for (accent of accents(); track accent.id) {\n <button\n type=\"button\"\n class=\"accent\"\n role=\"radio\"\n [class.is-auto]=\"accent.id === 'auto'\"\n [class.is-active]=\"accent.id === current.accent\"\n [attr.aria-checked]=\"accent.id === current.accent\"\n [attr.aria-label]=\"accent.label\"\n [title]=\"accent.label\"\n [style.background]=\"accent.color\"\n [style.color]=\"accent.ink\"\n (click)=\"pickAccent(accent.id)\"\n >\n @if (accent.id === current.accent) {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n } @else if (accent.id === 'auto') {\n <i class=\"pi pi-sparkles\" aria-hidden=\"true\"></i>\n }\n </button>\n }\n </div>\n</section>\n\n<section class=\"group\">\n <h3 class=\"group-title\">{{ t.densityTitle }}</h3>\n <p class=\"group-hint\">{{ t.densityHint }}</p>\n\n <div class=\"densities\" role=\"radiogroup\" [attr.aria-label]=\"t.densityTitle\">\n @for (density of densities(); track density.id) {\n <button\n type=\"button\"\n class=\"density\"\n role=\"radio\"\n [class.is-active]=\"density.id === current.density\"\n [attr.aria-checked]=\"density.id === current.density\"\n (click)=\"pickDensity(density.id)\"\n >\n <span class=\"density-chip\" aria-hidden=\"true\" [style.gap.px]=\"density.gap\">\n <span class=\"density-row\"></span>\n <span class=\"density-row\"></span>\n <span class=\"density-row\"></span>\n </span>\n <span class=\"density-name\">{{ density.label }}</span>\n </button>\n }\n </div>\n</section>\n", styles: [":host{display:flex;flex-direction:column;gap:26px}.group{display:flex;flex-direction:column;gap:2px}.group-title{margin:0;font-size:13px;font-weight:600;letter-spacing:.02em}.group-hint{margin:0 0 12px;color:var(--ac-text-muted);font-size:12px;font-weight:300;line-height:1.45}.frames{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:10px}.frame{display:flex;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.frame:hover{color:var(--ac-text)}.frame.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.frame-chip{display:flex;overflow:hidden;flex-direction:column;height:52px;border:1px solid var(--f-line);border-radius:6px;background:var(--f-bg)}.frame-bar{display:flex;flex:none;align-items:center;height:12px;padding-left:5px;border-bottom:1px solid var(--f-line)}.frame-dot{width:5px;height:5px;border-radius:50%;background:var(--f-glow)}.frame-lower{display:flex;flex:1;min-height:0}.frame-rail{display:flex;flex:none;flex-direction:column;gap:3px;width:22px;padding:4px 3px;border-right:1px solid var(--f-line)}.frame-pin{height:7px;border-left:2px solid var(--f-glow);border-radius:2px;background:var(--f-raised)}.frame-row{height:3px;border-radius:2px;background:var(--f-muted);opacity:.55}.frame-row.is-on{background:var(--f-glow);opacity:1}.frame-canvas{flex:1;background:var(--ac-bg)}.frame-name{overflow:hidden;font-size:11px;font-weight:500;text-align:center;text-overflow:ellipsis;white-space:nowrap}.canvases{display:grid;grid-template-columns:repeat(auto-fit,minmax(70px,1fr));gap:8px}.canvas{display:flex;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.canvas:hover{color:var(--ac-text)}.canvas.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.canvas-chip{display:flex;align-items:center;justify-content:center;height:40px;border:1px solid var(--ac-border);border-radius:6px}.canvas-chip i{font-size:15px}.canvas-sheet{width:60%;height:58%;border-radius:3px;box-shadow:0 1px 4px #00000038}.canvas-name{font-size:11px;font-weight:500;text-align:center}.accents{display:flex;flex-wrap:wrap;gap:9px}.accent{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:2px solid transparent;border-radius:50%;cursor:pointer;transition:transform .12s ease}.accent:hover{transform:scale(1.12)}.accent.is-auto{border-style:dashed;border-color:var(--ac-border)}.accent.is-active{border-style:solid;border-color:var(--ac-text);box-shadow:0 0 0 2px var(--ac-surface) inset}.accent i{font-size:11px}.densities{display:flex;gap:10px}.density{display:flex;flex:1;flex-direction:column;gap:6px;padding:4px;border:2px solid transparent;border-radius:10px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.density:hover{color:var(--ac-text)}.density.is-active{border-color:var(--ac-accent);color:var(--ac-text)}.density-chip{display:flex;flex-direction:column;justify-content:center;height:42px;padding:0 10px;border:1px solid var(--ac-border);border-radius:6px;background:var(--ac-surface)}.density-row{height:3px;border-radius:2px;background:var(--ac-text-muted);opacity:.5}.density-name{font-size:11px;font-weight:500;text-align:center}@media(prefers-reduced-motion:reduce){.accent{transition:none}}\n"] }]
|
|
3070
|
+
}] });
|
|
3071
|
+
|
|
3072
|
+
function menuTile(label, icon, color) {
|
|
3073
|
+
return { label, icon, color, ink: color ? readableOn(color) : null, ghost: false };
|
|
3074
|
+
}
|
|
3075
|
+
function menuGlyph(icon) {
|
|
3076
|
+
return { label: '', icon, color: null, ink: null, ghost: true };
|
|
3077
|
+
}
|
|
3078
|
+
function foldText(value) {
|
|
3079
|
+
return value
|
|
3080
|
+
.normalize('NFD')
|
|
3081
|
+
.replace(/[̀-ͯ]/g, '')
|
|
3082
|
+
.toLowerCase();
|
|
3083
|
+
}
|
|
3084
|
+
function matchesRow(row, term) {
|
|
3085
|
+
return foldText(row.name).includes(term) || foldText(row.meta ?? '').includes(term);
|
|
3086
|
+
}
|
|
3087
|
+
function selectableRows(sections) {
|
|
3088
|
+
return sections.flatMap((section) => section.rows).filter((row) => !row.disabled && !row.current);
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
class NucleusMenu {
|
|
3092
|
+
sections = input([], ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
|
|
3093
|
+
heading = input('', ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
3094
|
+
badge = input(null, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
|
|
3095
|
+
searchable = input(false, ...(ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []));
|
|
3096
|
+
searchPlaceholder = input('', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
|
|
3097
|
+
emptyText = input('', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
|
|
3098
|
+
keyboardHint = input('', ...(ngDevMode ? [{ debugName: "keyboardHint" }] : /* istanbul ignore next */ []));
|
|
3099
|
+
width = input(320, ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
3100
|
+
select = output();
|
|
3101
|
+
dismiss = output();
|
|
3102
|
+
term = signal('', ...(ngDevMode ? [{ debugName: "term" }] : /* istanbul ignore next */ []));
|
|
3103
|
+
searchBox = viewChild('searchBox', ...(ngDevMode ? [{ debugName: "searchBox" }] : /* istanbul ignore next */ []));
|
|
3104
|
+
root = viewChild.required('root');
|
|
3105
|
+
visible = computed(() => {
|
|
3106
|
+
const term = foldText(this.term().trim());
|
|
3107
|
+
if (!term)
|
|
3108
|
+
return this.sections();
|
|
3109
|
+
const rows = this.sections()
|
|
3110
|
+
.flatMap((section) => section.rows)
|
|
3111
|
+
.filter((row) => !row.current && matchesRow(row, term));
|
|
3112
|
+
return [{ id: 'results', label: null, rows }];
|
|
3113
|
+
}, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
3114
|
+
isEmpty = computed(() => this.visible().every((s) => s.rows.length === 0), ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
3115
|
+
showSearch = computed(() => this.searchable(), ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
|
|
3116
|
+
reachable = computed(() => selectableRows(this.visible()), ...(ngDevMode ? [{ debugName: "reachable" }] : /* istanbul ignore next */ []));
|
|
3117
|
+
activeId = signal(null, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
|
|
3118
|
+
syncActive = effect(() => {
|
|
3119
|
+
const rows = this.reachable();
|
|
3120
|
+
const current = this.activeId();
|
|
3121
|
+
if (rows.some((row) => row.id === current))
|
|
3122
|
+
return;
|
|
3123
|
+
this.activeId.set(rows[0]?.id ?? null);
|
|
3124
|
+
}, ...(ngDevMode ? [{ debugName: "syncActive" }] : /* istanbul ignore next */ []));
|
|
3125
|
+
constructor() {
|
|
3126
|
+
queueMicrotask(() => this.focusEntry());
|
|
3127
|
+
}
|
|
3128
|
+
isActive(row) {
|
|
3129
|
+
return row.id === this.activeId();
|
|
3130
|
+
}
|
|
3131
|
+
onTerm(value) {
|
|
3132
|
+
this.term.set(value);
|
|
3133
|
+
}
|
|
3134
|
+
choose(row) {
|
|
3135
|
+
if (row.disabled || row.current)
|
|
3136
|
+
return;
|
|
3137
|
+
this.select.emit(row);
|
|
3138
|
+
}
|
|
3139
|
+
onKeydown(event) {
|
|
3140
|
+
if (event.key === 'Escape') {
|
|
3141
|
+
event.preventDefault();
|
|
3142
|
+
this.dismiss.emit();
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
3145
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
3146
|
+
event.preventDefault();
|
|
3147
|
+
this.step(event.key === 'ArrowDown' ? 1 : -1);
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
if (event.key === 'Enter') {
|
|
3151
|
+
const row = this.reachable().find((item) => item.id === this.activeId());
|
|
3152
|
+
if (!row)
|
|
3153
|
+
return;
|
|
3154
|
+
event.preventDefault();
|
|
3155
|
+
this.choose(row);
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
step(delta) {
|
|
3159
|
+
const rows = this.reachable();
|
|
3160
|
+
if (!rows.length)
|
|
3161
|
+
return;
|
|
3162
|
+
const index = rows.findIndex((row) => row.id === this.activeId());
|
|
3163
|
+
const next = (index + delta + rows.length) % rows.length;
|
|
3164
|
+
this.activeId.set(rows[next].id);
|
|
3165
|
+
this.scrollIntoView(rows[next].id);
|
|
3166
|
+
}
|
|
3167
|
+
scrollIntoView(id) {
|
|
3168
|
+
const target = this.root().nativeElement.querySelector(`[data-row="${id}"]`);
|
|
3169
|
+
target?.scrollIntoView({ block: 'nearest' });
|
|
3170
|
+
}
|
|
3171
|
+
focusEntry() {
|
|
3172
|
+
const box = this.searchBox()?.nativeElement;
|
|
3173
|
+
if (box) {
|
|
3174
|
+
box.focus();
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
this.root().nativeElement.focus();
|
|
3178
|
+
}
|
|
3179
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3180
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusMenu, isStandalone: true, selector: "nucleus-menu", inputs: { sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, keyboardHint: { classPropertyName: "keyboardHint", publicName: "keyboardHint", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select", dismiss: "dismiss" }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "style.width.px": "width()" } }, viewQueries: [{ propertyName: "searchBox", first: true, predicate: ["searchBox"], descendants: true, isSignal: true }, { propertyName: "root", first: true, predicate: ["root"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"menu\" #root tabindex=\"-1\">\n @if (heading()) {\n <div class=\"menu-head\">\n <h4>{{ heading() }}</h4>\n @if (badge(); as count) {\n <span class=\"menu-badge\">{{ count }}</span>\n }\n </div>\n }\n\n @if (showSearch()) {\n <div class=\"menu-search\">\n <label class=\"menu-field\">\n <i class=\"pi pi-search\" aria-hidden=\"true\"></i>\n <input\n #searchBox\n type=\"text\"\n autocomplete=\"off\"\n [attr.aria-label]=\"searchPlaceholder()\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"term()\"\n (input)=\"onTerm($any($event.target).value)\"\n />\n </label>\n </div>\n }\n\n <div class=\"menu-body\" role=\"listbox\" [attr.aria-label]=\"heading()\">\n @if (isEmpty()) {\n <p class=\"menu-empty\">{{ emptyText() }}</p>\n }\n\n @for (section of visible(); track section.id) {\n @if (section.rows.length) {\n @if (section.label) {\n <p class=\"menu-section\">{{ section.label }}</p>\n }\n\n @for (row of section.rows; track row.id) {\n <button\n type=\"button\"\n class=\"menu-row\"\n role=\"option\"\n [attr.data-row]=\"row.id\"\n [class.is-current]=\"row.current\"\n [class.is-locked]=\"row.disabled\"\n [class.is-active]=\"isActive(row)\"\n [attr.aria-selected]=\"isActive(row)\"\n [disabled]=\"row.disabled || row.current\"\n (click)=\"choose(row)\"\n >\n @if (row.tile; as tile) {\n <span\n class=\"menu-tile\"\n [class.is-ghost]=\"tile.ghost\"\n [style.background]=\"tile.ghost ? null : tile.color\"\n [style.color]=\"tile.ghost ? null : tile.ink\"\n aria-hidden=\"true\"\n >\n @if (tile.icon) {\n <i [class]=\"tile.icon\"></i>\n } @else {\n {{ tile.label }}\n }\n </span>\n }\n\n <span class=\"menu-text\">\n <span class=\"menu-name\">{{ row.name }}</span>\n @if (row.meta) {\n <span class=\"menu-meta\">{{ row.meta }}</span>\n }\n </span>\n\n <span class=\"menu-end\">\n @if (row.tag) {\n <span class=\"menu-tag\" [class.is-current]=\"row.current\">{{ row.tag }}</span>\n } @else if (!row.disabled && !row.current) {\n <i class=\"pi pi-chevron-right menu-chevron\" aria-hidden=\"true\"></i>\n }\n </span>\n </button>\n }\n }\n }\n </div>\n\n @if (showSearch() && keyboardHint()) {\n <p class=\"menu-foot\">{{ keyboardHint() }}</p>\n }\n</div>\n", styles: [":host{display:block}.menu{display:flex;flex-direction:column;min-width:0}.menu:focus-visible{outline:none}.menu-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 15px 10px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.menu-head h4{margin:0;overflow:hidden;color:var(--ns-header-ink);font-size:11px;font-weight:600;letter-spacing:.08em;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.menu-badge{flex:none;padding:1px 8px;border-radius:var(--ac-radius-pill);background:var(--ac-surface-alt);color:var(--ac-text-muted);font-size:10px;font-variant-numeric:tabular-nums}.menu-search{padding:9px 10px;border-bottom:1px solid var(--ac-border)}.menu-field{position:relative;display:block}.menu-field i{position:absolute;top:50%;left:11px;color:var(--ac-text-muted);font-size:12px;pointer-events:none;transform:translateY(-50%)}.menu-field input{width:100%;box-sizing:border-box;padding:7px 10px 7px 31px;border:1px solid var(--ac-border);border-radius:7px;background:var(--ac-bg);color:var(--ac-text);font:inherit;font-size:13px}.menu-field input::placeholder{color:var(--ac-text-muted)}.menu-field input:focus-visible{border-color:var(--ac-accent);outline:2px solid color-mix(in srgb,var(--ac-accent) 45%,transparent);outline-offset:1px}.menu-body{display:flex;max-height:348px;flex-direction:column;gap:1px;padding:6px;overflow-y:auto}.menu-section{margin:0;padding:11px 9px 5px;color:var(--ac-text-muted);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.menu-empty{margin:0;padding:26px 14px;color:var(--ac-text-muted);font-size:12px;text-align:center}.menu-row{display:flex;width:100%;align-items:center;gap:11px;padding:9px 10px;border:0;border-radius:8px;background:transparent;color:var(--ac-text);font:inherit;text-align:left;cursor:pointer}.menu-row:hover:not(:disabled),.menu-row.is-active:not(:disabled){background:var(--ns-accent-wash);box-shadow:inset 2px 0 0 var(--ac-accent)}.menu-row:focus-visible{outline:none}.menu-row.is-current{background:var(--ac-surface-alt);box-shadow:none;cursor:default}.menu-row.is-locked{cursor:default;opacity:.55}.menu-tile{display:inline-flex;width:34px;height:34px;flex:none;align-items:center;justify-content:center;border-radius:9px;font-size:11.5px;font-weight:600;letter-spacing:.02em}.menu-tile i{font-size:14px}.menu-tile.is-ghost{background:var(--ac-surface-alt);color:var(--ac-text-muted)}.menu-row:hover:not(:disabled) .menu-tile.is-ghost,.menu-row.is-active:not(:disabled) .menu-tile.is-ghost{color:var(--ac-accent)}.menu-text{display:flex;min-width:0;flex-direction:column;gap:1px}.menu-name{overflow:hidden;font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.menu-meta{overflow:hidden;color:var(--ac-text-muted);font-size:11px;text-overflow:ellipsis;white-space:nowrap}.menu-end{display:flex;flex:none;align-items:center;margin-left:auto}.menu-chevron{color:var(--ac-accent);font-size:11px;opacity:0}.menu-row:hover:not(:disabled) .menu-chevron,.menu-row.is-active:not(:disabled) .menu-chevron{opacity:1}.menu-tag{padding:2px 7px;border-radius:4px;background:var(--ac-surface-alt);color:var(--ac-text-muted);font-size:9.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}.menu-tag.is-current{background:color-mix(in srgb,var(--ac-accent) 16%,transparent);color:var(--ac-accent)}.menu-foot{margin:0;padding:8px 14px;border-top:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:10.5px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3181
|
+
}
|
|
3182
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusMenu, decorators: [{
|
|
3183
|
+
type: Component,
|
|
3184
|
+
args: [{ selector: 'nucleus-menu', host: {
|
|
3185
|
+
'[style.width.px]': 'width()',
|
|
3186
|
+
'(keydown)': 'onKeydown($event)',
|
|
3187
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"menu\" #root tabindex=\"-1\">\n @if (heading()) {\n <div class=\"menu-head\">\n <h4>{{ heading() }}</h4>\n @if (badge(); as count) {\n <span class=\"menu-badge\">{{ count }}</span>\n }\n </div>\n }\n\n @if (showSearch()) {\n <div class=\"menu-search\">\n <label class=\"menu-field\">\n <i class=\"pi pi-search\" aria-hidden=\"true\"></i>\n <input\n #searchBox\n type=\"text\"\n autocomplete=\"off\"\n [attr.aria-label]=\"searchPlaceholder()\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"term()\"\n (input)=\"onTerm($any($event.target).value)\"\n />\n </label>\n </div>\n }\n\n <div class=\"menu-body\" role=\"listbox\" [attr.aria-label]=\"heading()\">\n @if (isEmpty()) {\n <p class=\"menu-empty\">{{ emptyText() }}</p>\n }\n\n @for (section of visible(); track section.id) {\n @if (section.rows.length) {\n @if (section.label) {\n <p class=\"menu-section\">{{ section.label }}</p>\n }\n\n @for (row of section.rows; track row.id) {\n <button\n type=\"button\"\n class=\"menu-row\"\n role=\"option\"\n [attr.data-row]=\"row.id\"\n [class.is-current]=\"row.current\"\n [class.is-locked]=\"row.disabled\"\n [class.is-active]=\"isActive(row)\"\n [attr.aria-selected]=\"isActive(row)\"\n [disabled]=\"row.disabled || row.current\"\n (click)=\"choose(row)\"\n >\n @if (row.tile; as tile) {\n <span\n class=\"menu-tile\"\n [class.is-ghost]=\"tile.ghost\"\n [style.background]=\"tile.ghost ? null : tile.color\"\n [style.color]=\"tile.ghost ? null : tile.ink\"\n aria-hidden=\"true\"\n >\n @if (tile.icon) {\n <i [class]=\"tile.icon\"></i>\n } @else {\n {{ tile.label }}\n }\n </span>\n }\n\n <span class=\"menu-text\">\n <span class=\"menu-name\">{{ row.name }}</span>\n @if (row.meta) {\n <span class=\"menu-meta\">{{ row.meta }}</span>\n }\n </span>\n\n <span class=\"menu-end\">\n @if (row.tag) {\n <span class=\"menu-tag\" [class.is-current]=\"row.current\">{{ row.tag }}</span>\n } @else if (!row.disabled && !row.current) {\n <i class=\"pi pi-chevron-right menu-chevron\" aria-hidden=\"true\"></i>\n }\n </span>\n </button>\n }\n }\n }\n </div>\n\n @if (showSearch() && keyboardHint()) {\n <p class=\"menu-foot\">{{ keyboardHint() }}</p>\n }\n</div>\n", styles: [":host{display:block}.menu{display:flex;flex-direction:column;min-width:0}.menu:focus-visible{outline:none}.menu-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 15px 10px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.menu-head h4{margin:0;overflow:hidden;color:var(--ns-header-ink);font-size:11px;font-weight:600;letter-spacing:.08em;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.menu-badge{flex:none;padding:1px 8px;border-radius:var(--ac-radius-pill);background:var(--ac-surface-alt);color:var(--ac-text-muted);font-size:10px;font-variant-numeric:tabular-nums}.menu-search{padding:9px 10px;border-bottom:1px solid var(--ac-border)}.menu-field{position:relative;display:block}.menu-field i{position:absolute;top:50%;left:11px;color:var(--ac-text-muted);font-size:12px;pointer-events:none;transform:translateY(-50%)}.menu-field input{width:100%;box-sizing:border-box;padding:7px 10px 7px 31px;border:1px solid var(--ac-border);border-radius:7px;background:var(--ac-bg);color:var(--ac-text);font:inherit;font-size:13px}.menu-field input::placeholder{color:var(--ac-text-muted)}.menu-field input:focus-visible{border-color:var(--ac-accent);outline:2px solid color-mix(in srgb,var(--ac-accent) 45%,transparent);outline-offset:1px}.menu-body{display:flex;max-height:348px;flex-direction:column;gap:1px;padding:6px;overflow-y:auto}.menu-section{margin:0;padding:11px 9px 5px;color:var(--ac-text-muted);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.menu-empty{margin:0;padding:26px 14px;color:var(--ac-text-muted);font-size:12px;text-align:center}.menu-row{display:flex;width:100%;align-items:center;gap:11px;padding:9px 10px;border:0;border-radius:8px;background:transparent;color:var(--ac-text);font:inherit;text-align:left;cursor:pointer}.menu-row:hover:not(:disabled),.menu-row.is-active:not(:disabled){background:var(--ns-accent-wash);box-shadow:inset 2px 0 0 var(--ac-accent)}.menu-row:focus-visible{outline:none}.menu-row.is-current{background:var(--ac-surface-alt);box-shadow:none;cursor:default}.menu-row.is-locked{cursor:default;opacity:.55}.menu-tile{display:inline-flex;width:34px;height:34px;flex:none;align-items:center;justify-content:center;border-radius:9px;font-size:11.5px;font-weight:600;letter-spacing:.02em}.menu-tile i{font-size:14px}.menu-tile.is-ghost{background:var(--ac-surface-alt);color:var(--ac-text-muted)}.menu-row:hover:not(:disabled) .menu-tile.is-ghost,.menu-row.is-active:not(:disabled) .menu-tile.is-ghost{color:var(--ac-accent)}.menu-text{display:flex;min-width:0;flex-direction:column;gap:1px}.menu-name{overflow:hidden;font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.menu-meta{overflow:hidden;color:var(--ac-text-muted);font-size:11px;text-overflow:ellipsis;white-space:nowrap}.menu-end{display:flex;flex:none;align-items:center;margin-left:auto}.menu-chevron{color:var(--ac-accent);font-size:11px;opacity:0}.menu-row:hover:not(:disabled) .menu-chevron,.menu-row.is-active:not(:disabled) .menu-chevron{opacity:1}.menu-tag{padding:2px 7px;border-radius:4px;background:var(--ac-surface-alt);color:var(--ac-text-muted);font-size:9.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}.menu-tag.is-current{background:color-mix(in srgb,var(--ac-accent) 16%,transparent);color:var(--ac-accent)}.menu-foot{margin:0;padding:8px 14px;border-top:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:10.5px}\n"] }]
|
|
3188
|
+
}], ctorParameters: () => [], propDecorators: { sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], keyboardHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboardHint", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], select: [{ type: i0.Output, args: ["select"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], searchBox: [{ type: i0.ViewChild, args: ['searchBox', { isSignal: true }] }], root: [{ type: i0.ViewChild, args: ['root', { isSignal: true }] }] } });
|
|
3189
|
+
|
|
3190
|
+
const STORAGE_KEY = 'nucleus_recent_apps';
|
|
3191
|
+
const KEEP = 8;
|
|
3192
|
+
function readIds() {
|
|
3193
|
+
try {
|
|
3194
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
3195
|
+
if (!raw)
|
|
3196
|
+
return [];
|
|
3197
|
+
const parsed = JSON.parse(raw);
|
|
3198
|
+
if (!Array.isArray(parsed))
|
|
3199
|
+
return [];
|
|
3200
|
+
return parsed.filter((value) => typeof value === 'string').slice(0, KEEP);
|
|
3201
|
+
}
|
|
3202
|
+
catch {
|
|
3203
|
+
return [];
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
class NucleusRecentApps {
|
|
3207
|
+
_ids = signal(readIds(), ...(ngDevMode ? [{ debugName: "_ids" }] : /* istanbul ignore next */ []));
|
|
3208
|
+
ids = this._ids.asReadonly();
|
|
3209
|
+
remember(appId) {
|
|
3210
|
+
const key = appId.toLowerCase();
|
|
3211
|
+
this._ids.update((ids) => [key, ...ids.filter((id) => id !== key)].slice(0, KEEP));
|
|
3212
|
+
try {
|
|
3213
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(this._ids()));
|
|
3214
|
+
}
|
|
3215
|
+
catch { }
|
|
3216
|
+
}
|
|
3217
|
+
pick(source, limit) {
|
|
3218
|
+
const byId = new Map(source.map((item) => [item.id.toLowerCase(), item]));
|
|
3219
|
+
return this._ids()
|
|
3220
|
+
.map((id) => byId.get(id))
|
|
3221
|
+
.filter((item) => item !== undefined)
|
|
3222
|
+
.slice(0, limit);
|
|
3223
|
+
}
|
|
3224
|
+
clear() {
|
|
3225
|
+
this._ids.set([]);
|
|
3226
|
+
try {
|
|
3227
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
3228
|
+
}
|
|
3229
|
+
catch { }
|
|
3230
|
+
}
|
|
3231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusRecentApps, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3232
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusRecentApps, providedIn: 'root' });
|
|
3233
|
+
}
|
|
3234
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusRecentApps, decorators: [{
|
|
3235
|
+
type: Injectable,
|
|
3236
|
+
args: [{ providedIn: 'root' }]
|
|
3237
|
+
}] });
|
|
3238
|
+
|
|
1653
3239
|
const W = 1280;
|
|
1654
3240
|
const H = 720;
|
|
1655
3241
|
const STARS = Array.from({ length: 130 }, (_, i) => {
|
|
@@ -1745,10 +3331,17 @@ const SHELL_COPY = {
|
|
|
1745
3331
|
navToggle: 'Mostrar u ocultar el menú',
|
|
1746
3332
|
appsLauncher: 'Aplicaciones',
|
|
1747
3333
|
appsEmpty: 'No hay otras aplicaciones',
|
|
3334
|
+
appsSwitch: 'Cambiar de aplicación',
|
|
3335
|
+
appsSearch: 'Buscar aplicación',
|
|
3336
|
+
appsNoMatch: 'Ninguna aplicación coincide con lo que escribiste.',
|
|
3337
|
+
appsRecent: 'Recientes',
|
|
3338
|
+
appsAll: 'Todas',
|
|
3339
|
+
appsAvailable: 'Disponibles',
|
|
3340
|
+
appsHere: 'Aquí',
|
|
3341
|
+
keyboardHint: '↑ ↓ moverse · ↵ abrir · esc cerrar',
|
|
3342
|
+
accountSwitchHint: 'Entrar con otro correo',
|
|
3343
|
+
signOutHint: 'Salir de la suite',
|
|
1748
3344
|
settingsTitle: 'Ajustes',
|
|
1749
|
-
settingsTheme: 'Tema',
|
|
1750
|
-
settingsThemeHint: 'Elige cómo se ve la suite en este dispositivo.',
|
|
1751
|
-
themeSystem: 'Del sistema',
|
|
1752
3345
|
signOut: 'Cerrar sesión',
|
|
1753
3346
|
accountManage: 'Gestionar mi cuenta',
|
|
1754
3347
|
accountSwitch: 'Cambiar de cuenta',
|
|
@@ -1757,19 +3350,15 @@ const SHELL_COPY = {
|
|
|
1757
3350
|
accountScope: 'Mi cuenta',
|
|
1758
3351
|
hintComingSoon: 'Próximamente',
|
|
1759
3352
|
hintNotDeployed: 'Sin publicar',
|
|
1760
|
-
|
|
1761
|
-
contextTitle: 'Compañía y sede',
|
|
1762
|
-
contextHint: 'Elige dónde quieres trabajar. El menú se carga según tu rol en la sede.',
|
|
3353
|
+
roleCaption: 'Tu rol',
|
|
1763
3354
|
companyLabel: 'Compañía',
|
|
1764
3355
|
companyPlaceholder: 'Selecciona una compañía',
|
|
1765
3356
|
siteLabel: 'Sede',
|
|
1766
3357
|
sitePlaceholder: 'Selecciona una sede',
|
|
1767
3358
|
sitesLoading: 'Cargando sedes...',
|
|
1768
3359
|
sitesEmpty: 'Esta compañía no tiene sedes a tu alcance',
|
|
1769
|
-
confirmSwitch: 'Cambiar',
|
|
1770
3360
|
relationOwner: 'Propietario',
|
|
1771
3361
|
relationCollaborator: 'Colaborador',
|
|
1772
|
-
noContext: 'Sin sede seleccionada',
|
|
1773
3362
|
noCompanyTitle: 'Te falta una compañía',
|
|
1774
3363
|
noCompanyHint: 'Para trabajar en las aplicaciones necesitas al menos una compañía con una sede, ' +
|
|
1775
3364
|
'propia o donde te hayan invitado. Puedes entrar igual, pero el menú saldrá vacío.',
|
|
@@ -1778,10 +3367,17 @@ const SHELL_COPY = {
|
|
|
1778
3367
|
navToggle: 'Show or hide the menu',
|
|
1779
3368
|
appsLauncher: 'Applications',
|
|
1780
3369
|
appsEmpty: 'No other applications',
|
|
3370
|
+
appsSwitch: 'Switch application',
|
|
3371
|
+
appsSearch: 'Search applications',
|
|
3372
|
+
appsNoMatch: 'No application matches what you typed.',
|
|
3373
|
+
appsRecent: 'Recent',
|
|
3374
|
+
appsAll: 'All',
|
|
3375
|
+
appsAvailable: 'Available',
|
|
3376
|
+
appsHere: 'Here',
|
|
3377
|
+
keyboardHint: '↑ ↓ move · ↵ open · esc close',
|
|
3378
|
+
accountSwitchHint: 'Sign in with another address',
|
|
3379
|
+
signOutHint: 'Leave the suite',
|
|
1781
3380
|
settingsTitle: 'Settings',
|
|
1782
|
-
settingsTheme: 'Theme',
|
|
1783
|
-
settingsThemeHint: 'Choose how the suite looks on this device.',
|
|
1784
|
-
themeSystem: 'System',
|
|
1785
3381
|
signOut: 'Sign out',
|
|
1786
3382
|
accountManage: 'Manage my account',
|
|
1787
3383
|
accountSwitch: 'Switch account',
|
|
@@ -1790,29 +3386,29 @@ const SHELL_COPY = {
|
|
|
1790
3386
|
accountScope: 'My account',
|
|
1791
3387
|
hintComingSoon: 'Coming soon',
|
|
1792
3388
|
hintNotDeployed: 'Not deployed',
|
|
1793
|
-
|
|
1794
|
-
contextTitle: 'Company and site',
|
|
1795
|
-
contextHint: 'Choose where you want to work. The menu loads from your role at that site.',
|
|
3389
|
+
roleCaption: 'Your role',
|
|
1796
3390
|
companyLabel: 'Company',
|
|
1797
3391
|
companyPlaceholder: 'Select a company',
|
|
1798
3392
|
siteLabel: 'Site',
|
|
1799
3393
|
sitePlaceholder: 'Select a site',
|
|
1800
3394
|
sitesLoading: 'Loading sites...',
|
|
1801
3395
|
sitesEmpty: 'This company has no sites available to you',
|
|
1802
|
-
confirmSwitch: 'Switch',
|
|
1803
3396
|
relationOwner: 'Owner',
|
|
1804
3397
|
relationCollaborator: 'Collaborator',
|
|
1805
|
-
noContext: 'No site selected',
|
|
1806
3398
|
noCompanyTitle: 'You need a company',
|
|
1807
3399
|
noCompanyHint: 'To work in the applications you need at least one company with a site, your own or ' +
|
|
1808
3400
|
'one you have been invited to. You can still open them, but the menu will be empty.',
|
|
1809
3401
|
},
|
|
1810
3402
|
};
|
|
1811
3403
|
|
|
3404
|
+
const LAUNCHER_RICH_THRESHOLD = 6;
|
|
3405
|
+
const RECENT_LIMIT = 3;
|
|
3406
|
+
const FALLBACK_APP_COLOR = '#64748b';
|
|
1812
3407
|
class NucleusShell {
|
|
1813
3408
|
session = inject(SessionService);
|
|
1814
3409
|
sessionAuth = inject(NucleusSessionAuth);
|
|
1815
3410
|
themes = inject(ThemeService);
|
|
3411
|
+
recents = inject(NucleusRecentApps);
|
|
1816
3412
|
language = inject(NucleusLanguage);
|
|
1817
3413
|
page = inject(NucleusPage);
|
|
1818
3414
|
config = inject(NUCLEUS_CONFIG);
|
|
@@ -1828,6 +3424,7 @@ class NucleusShell {
|
|
|
1828
3424
|
menuSelect = output();
|
|
1829
3425
|
lang = this.language.lang;
|
|
1830
3426
|
icons = NUCLEUS_ICONS;
|
|
3427
|
+
menuPlaceholders = ['64%', '80%', '52%', '72%', '58%'];
|
|
1831
3428
|
title = computed(() => this.heading() || this.page.heading(), ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1832
3429
|
subtitle = computed(() => this.lead() || this.page.lead(), ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
1833
3430
|
home = computed(() => ({ icon: 'pi pi-home', routerLink: '/' }), ...(ngDevMode ? [{ debugName: "home" }] : /* istanbul ignore next */ []));
|
|
@@ -1837,20 +3434,22 @@ class NucleusShell {
|
|
|
1837
3434
|
})), ...(ngDevMode ? [{ debugName: "crumbs" }] : /* istanbul ignore next */ []));
|
|
1838
3435
|
copy = computed(() => SHELL_COPY[this.lang()], ...(ngDevMode ? [{ debugName: "copy" }] : /* istanbul ignore next */ []));
|
|
1839
3436
|
starfield = this.themes.starfield;
|
|
1840
|
-
themePreference = this.themes.preference;
|
|
1841
3437
|
menu = this.session.menu;
|
|
3438
|
+
collapsedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "collapsedIds" }] : /* istanbul ignore next */ []));
|
|
1842
3439
|
menuLoading = this.session.loading;
|
|
1843
3440
|
menuError = this.session.error;
|
|
1844
3441
|
launcherApps = this.session.apps;
|
|
1845
3442
|
switching = this.session.switching;
|
|
1846
|
-
|
|
3443
|
+
narrowQuery = typeof window === 'undefined' ? null : window.matchMedia('(max-width: 720px)');
|
|
3444
|
+
narrow = signal(this.narrowQuery?.matches ?? false, ...(ngDevMode ? [{ debugName: "narrow" }] : /* istanbul ignore next */ []));
|
|
3445
|
+
sidebarOpen = signal(!(this.narrowQuery?.matches ?? false), ...(ngDevMode ? [{ debugName: "sidebarOpen" }] : /* istanbul ignore next */ []));
|
|
1847
3446
|
settingsOpen = signal(false, ...(ngDevMode ? [{ debugName: "settingsOpen" }] : /* istanbul ignore next */ []));
|
|
1848
|
-
contextOpen = signal(false, ...(ngDevMode ? [{ debugName: "contextOpen" }] : /* istanbul ignore next */ []));
|
|
1849
3447
|
companyChoice = signal(null, ...(ngDevMode ? [{ debugName: "companyChoice" }] : /* istanbul ignore next */ []));
|
|
1850
3448
|
siteChoice = signal(null, ...(ngDevMode ? [{ debugName: "siteChoice" }] : /* istanbul ignore next */ []));
|
|
1851
3449
|
siteOptions = signal([], ...(ngDevMode ? [{ debugName: "siteOptions" }] : /* istanbul ignore next */ []));
|
|
1852
3450
|
sitesLoading = signal(false, ...(ngDevMode ? [{ debugName: "sitesLoading" }] : /* istanbul ignore next */ []));
|
|
1853
3451
|
auth = this.injector.get(NucleusAuth, null);
|
|
3452
|
+
sitesCompanyId = null;
|
|
1854
3453
|
identity = computed(() => {
|
|
1855
3454
|
const me = this.session.me();
|
|
1856
3455
|
if (me) {
|
|
@@ -1879,26 +3478,102 @@ class NucleusShell {
|
|
|
1879
3478
|
tag: company.relation === 'owner' ? t.relationOwner : t.relationCollaborator,
|
|
1880
3479
|
}));
|
|
1881
3480
|
}, ...(ngDevMode ? [{ debugName: "companyChoices" }] : /* istanbul ignore next */ []));
|
|
1882
|
-
contextCaption = computed(() => {
|
|
1883
|
-
const role = this.session.roleName();
|
|
1884
|
-
const site = this.session.siteName();
|
|
1885
|
-
const company = this.session.companyName();
|
|
1886
|
-
if (role && site)
|
|
1887
|
-
return company ? `${company} · ${site}` : site;
|
|
1888
|
-
if (this.session.scope() === 'account')
|
|
1889
|
-
return this.copy().accountScope;
|
|
1890
|
-
return this.copy().noContext;
|
|
1891
|
-
}, ...(ngDevMode ? [{ debugName: "contextCaption" }] : /* istanbul ignore next */ []));
|
|
1892
3481
|
roleName = this.session.roleName;
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
3482
|
+
words = computed(() => commonCopyOf(this.lang()), ...(ngDevMode ? [{ debugName: "words" }] : /* istanbul ignore next */ []));
|
|
3483
|
+
hasSiteContext = computed(() => this.session.scope() === 'site', ...(ngDevMode ? [{ debugName: "hasSiteContext" }] : /* istanbul ignore next */ []));
|
|
3484
|
+
launcherRich = computed(() => this.launcherApps().length > LAUNCHER_RICH_THRESHOLD, ...(ngDevMode ? [{ debugName: "launcherRich" }] : /* istanbul ignore next */ []));
|
|
3485
|
+
launcherCount = computed(() => {
|
|
3486
|
+
const total = this.launcherApps().length + (this.session.currentApp() ? 1 : 0);
|
|
3487
|
+
return total > LAUNCHER_RICH_THRESHOLD ? String(total) : null;
|
|
3488
|
+
}, ...(ngDevMode ? [{ debugName: "launcherCount" }] : /* istanbul ignore next */ []));
|
|
3489
|
+
launcherSections = computed(() => {
|
|
3490
|
+
const t = this.copy();
|
|
3491
|
+
const apps = this.launcherApps();
|
|
3492
|
+
const current = this.session.currentApp();
|
|
3493
|
+
const sections = [];
|
|
3494
|
+
if (current) {
|
|
3495
|
+
sections.push({ id: 'current', label: null, rows: [this.appRow(current, true)] });
|
|
3496
|
+
}
|
|
3497
|
+
if (!apps.length)
|
|
3498
|
+
return sections;
|
|
3499
|
+
if (this.launcherRich()) {
|
|
3500
|
+
const recent = this.recents.pick(apps, RECENT_LIMIT);
|
|
3501
|
+
if (recent.length) {
|
|
3502
|
+
sections.push({
|
|
3503
|
+
id: 'recent',
|
|
3504
|
+
label: t.appsRecent,
|
|
3505
|
+
rows: recent.map((app) => this.appRow(app, false)),
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
sections.push({
|
|
3509
|
+
id: 'all',
|
|
3510
|
+
label: t.appsAll,
|
|
3511
|
+
rows: apps.map((app) => this.appRow(app, false)),
|
|
3512
|
+
});
|
|
3513
|
+
return sections;
|
|
3514
|
+
}
|
|
3515
|
+
sections.push({
|
|
3516
|
+
id: 'all',
|
|
3517
|
+
label: current ? t.appsAvailable : null,
|
|
3518
|
+
rows: apps.map((app) => this.appRow(app, false)),
|
|
3519
|
+
});
|
|
3520
|
+
return sections;
|
|
3521
|
+
}, ...(ngDevMode ? [{ debugName: "launcherSections" }] : /* istanbul ignore next */ []));
|
|
3522
|
+
accountSections = computed(() => {
|
|
3523
|
+
const t = this.copy();
|
|
3524
|
+
const rows = [];
|
|
3525
|
+
if (this.canSwitchAccount()) {
|
|
3526
|
+
rows.push({
|
|
3527
|
+
id: 'switch',
|
|
3528
|
+
name: t.accountSwitch,
|
|
3529
|
+
meta: t.accountSwitchHint,
|
|
3530
|
+
tile: menuGlyph('pi pi-users'),
|
|
3531
|
+
tag: null,
|
|
3532
|
+
current: false,
|
|
3533
|
+
disabled: false,
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
rows.push({
|
|
3537
|
+
id: 'signout',
|
|
3538
|
+
name: t.signOut,
|
|
3539
|
+
meta: t.signOutHint,
|
|
3540
|
+
tile: menuGlyph('pi pi-sign-out'),
|
|
3541
|
+
tag: null,
|
|
3542
|
+
current: false,
|
|
3543
|
+
disabled: false,
|
|
3544
|
+
});
|
|
3545
|
+
return [{ id: 'actions', label: null, rows }];
|
|
3546
|
+
}, ...(ngDevMode ? [{ debugName: "accountSections" }] : /* istanbul ignore next */ []));
|
|
3547
|
+
accountContext = computed(() => {
|
|
3548
|
+
const company = this.companyChoices().find((item) => item.id === this.companyChoice());
|
|
3549
|
+
const site = this.session.siteName();
|
|
3550
|
+
if (company && site)
|
|
3551
|
+
return `${company.name} · ${site}`;
|
|
3552
|
+
if (company)
|
|
3553
|
+
return company.name;
|
|
3554
|
+
return this.copy().accountScope;
|
|
3555
|
+
}, ...(ngDevMode ? [{ debugName: "accountContext" }] : /* istanbul ignore next */ []));
|
|
3556
|
+
sitePlaceholder = computed(() => {
|
|
3557
|
+
const t = this.copy();
|
|
3558
|
+
if (this.sitesLoading())
|
|
3559
|
+
return t.sitesLoading;
|
|
3560
|
+
if (!this.siteOptions().length)
|
|
3561
|
+
return t.sitesEmpty;
|
|
3562
|
+
return t.sitePlaceholder;
|
|
3563
|
+
}, ...(ngDevMode ? [{ debugName: "sitePlaceholder" }] : /* istanbul ignore next */ []));
|
|
3564
|
+
topbarHeight = computed(() => this.narrow() && this.hasSiteContext() ? '100px' : '56px', ...(ngDevMode ? [{ debugName: "topbarHeight" }] : /* istanbul ignore next */ []));
|
|
3565
|
+
syncContext = effect(() => {
|
|
3566
|
+
const companyId = this.session.companyId();
|
|
3567
|
+
const siteId = this.session.siteId();
|
|
3568
|
+
untracked(() => {
|
|
3569
|
+
this.companyChoice.set(companyId);
|
|
3570
|
+
this.siteChoice.set(siteId);
|
|
3571
|
+
if (companyId)
|
|
3572
|
+
void this.loadSites(companyId);
|
|
3573
|
+
else
|
|
3574
|
+
this.siteOptions.set([]);
|
|
3575
|
+
});
|
|
3576
|
+
}, ...(ngDevMode ? [{ debugName: "syncContext" }] : /* istanbul ignore next */ []));
|
|
1902
3577
|
loadSession = effect(() => {
|
|
1903
3578
|
const language = this.lang();
|
|
1904
3579
|
const user = this.auth?.user() ?? null;
|
|
@@ -1906,49 +3581,97 @@ class NucleusShell {
|
|
|
1906
3581
|
return;
|
|
1907
3582
|
untracked(() => void this.session.load(language));
|
|
1908
3583
|
}, ...(ngDevMode ? [{ debugName: "loadSession" }] : /* istanbul ignore next */ []));
|
|
3584
|
+
constructor() {
|
|
3585
|
+
const query = this.narrowQuery;
|
|
3586
|
+
if (!query)
|
|
3587
|
+
return;
|
|
3588
|
+
const onChange = (event) => {
|
|
3589
|
+
this.narrow.set(event.matches);
|
|
3590
|
+
this.sidebarOpen.set(!event.matches);
|
|
3591
|
+
};
|
|
3592
|
+
query.addEventListener('change', onChange);
|
|
3593
|
+
inject(DestroyRef).onDestroy(() => query.removeEventListener('change', onChange));
|
|
3594
|
+
}
|
|
1909
3595
|
setLang(lang) {
|
|
1910
3596
|
this.language.setLang(lang);
|
|
1911
3597
|
}
|
|
1912
3598
|
toggleSidebar() {
|
|
1913
3599
|
this.sidebarOpen.update((open) => !open);
|
|
1914
3600
|
}
|
|
1915
|
-
selectTheme(preference) {
|
|
1916
|
-
if (preference)
|
|
1917
|
-
this.themes.setPreference(preference);
|
|
1918
|
-
}
|
|
1919
3601
|
selectNav(id) {
|
|
1920
3602
|
this.navSelect.emit(id);
|
|
3603
|
+
this.closeSidebarOnNarrow();
|
|
3604
|
+
}
|
|
3605
|
+
isGroup(node) {
|
|
3606
|
+
return node.children.length > 0;
|
|
3607
|
+
}
|
|
3608
|
+
isExpanded(node) {
|
|
3609
|
+
return !this.collapsedIds().has(node.id);
|
|
1921
3610
|
}
|
|
1922
3611
|
selectMenu(node) {
|
|
3612
|
+
if (this.isGroup(node)) {
|
|
3613
|
+
this.collapsedIds.update((ids) => {
|
|
3614
|
+
const next = new Set(ids);
|
|
3615
|
+
if (!next.delete(node.id))
|
|
3616
|
+
next.add(node.id);
|
|
3617
|
+
return next;
|
|
3618
|
+
});
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
if (!node.route)
|
|
3622
|
+
return;
|
|
1923
3623
|
this.menuSelect.emit(node);
|
|
1924
|
-
|
|
1925
|
-
|
|
3624
|
+
void this.router.navigateByUrl(node.route);
|
|
3625
|
+
this.closeSidebarOnNarrow();
|
|
3626
|
+
}
|
|
3627
|
+
closeSidebarOnNarrow() {
|
|
3628
|
+
if (this.narrow())
|
|
3629
|
+
this.sidebarOpen.set(false);
|
|
1926
3630
|
}
|
|
1927
3631
|
isSelected(node) {
|
|
1928
3632
|
return this.selectedMenuId() === node.id;
|
|
1929
3633
|
}
|
|
1930
|
-
async openContext() {
|
|
1931
|
-
const companyId = this.session.companyId() ?? this.companyChoices()[0]?.id ?? null;
|
|
1932
|
-
this.companyChoice.set(companyId);
|
|
1933
|
-
this.siteChoice.set(this.session.siteId());
|
|
1934
|
-
this.contextOpen.set(true);
|
|
1935
|
-
if (companyId)
|
|
1936
|
-
await this.loadSites(companyId);
|
|
1937
|
-
}
|
|
1938
3634
|
async onCompanyChange(companyId) {
|
|
3635
|
+
if (!companyId || companyId === this.session.companyId())
|
|
3636
|
+
return;
|
|
1939
3637
|
this.companyChoice.set(companyId);
|
|
1940
3638
|
this.siteChoice.set(null);
|
|
1941
|
-
this.
|
|
1942
|
-
|
|
1943
|
-
|
|
3639
|
+
const sites = await this.loadSites(companyId);
|
|
3640
|
+
const first = sites[0]?.id ?? null;
|
|
3641
|
+
this.siteChoice.set(first);
|
|
3642
|
+
if (first)
|
|
3643
|
+
await this.session.switchContext(companyId, first);
|
|
1944
3644
|
}
|
|
1945
|
-
async
|
|
3645
|
+
async onSiteChange(siteId) {
|
|
1946
3646
|
const companyId = this.companyChoice();
|
|
1947
|
-
|
|
1948
|
-
if (!companyId || !siteId)
|
|
3647
|
+
if (!siteId || !companyId || siteId === this.session.siteId())
|
|
1949
3648
|
return;
|
|
3649
|
+
this.siteChoice.set(siteId);
|
|
1950
3650
|
await this.session.switchContext(companyId, siteId);
|
|
1951
|
-
|
|
3651
|
+
}
|
|
3652
|
+
appRow(app, current) {
|
|
3653
|
+
const hint = current ? this.copy().appsHere : this.launchHint(app);
|
|
3654
|
+
return {
|
|
3655
|
+
id: app.id,
|
|
3656
|
+
name: app.name,
|
|
3657
|
+
meta: app.description,
|
|
3658
|
+
tile: menuTile(initialsOf(app.name), app.icon, app.color ?? FALLBACK_APP_COLOR),
|
|
3659
|
+
tag: hint || null,
|
|
3660
|
+
current,
|
|
3661
|
+
disabled: !current && !this.canOpen(app),
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
onLauncherSelect(row) {
|
|
3665
|
+
const app = this.launcherApps().find((item) => item.id === row.id);
|
|
3666
|
+
if (app)
|
|
3667
|
+
this.launch(app);
|
|
3668
|
+
}
|
|
3669
|
+
onAccountSelect(row) {
|
|
3670
|
+
if (row.id === 'switch') {
|
|
3671
|
+
void this.switchAccount();
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
void this.signOut();
|
|
1952
3675
|
}
|
|
1953
3676
|
canOpen(app) {
|
|
1954
3677
|
return app.status === 1 && !!app.url;
|
|
@@ -1956,6 +3679,7 @@ class NucleusShell {
|
|
|
1956
3679
|
launch(app) {
|
|
1957
3680
|
if (!this.canOpen(app))
|
|
1958
3681
|
return;
|
|
3682
|
+
this.recents.remember(app.id);
|
|
1959
3683
|
window.location.href = app.url;
|
|
1960
3684
|
}
|
|
1961
3685
|
appInitials(app) {
|
|
@@ -1980,24 +3704,27 @@ class NucleusShell {
|
|
|
1980
3704
|
window.location.href = nucleusLogoutUrl(landing);
|
|
1981
3705
|
}
|
|
1982
3706
|
async loadSites(companyId) {
|
|
3707
|
+
if (this.sitesCompanyId === companyId)
|
|
3708
|
+
return this.siteOptions();
|
|
3709
|
+
this.sitesCompanyId = companyId;
|
|
1983
3710
|
this.sitesLoading.set(true);
|
|
1984
3711
|
try {
|
|
1985
3712
|
const sites = await this.session.accessibleSites(companyId);
|
|
1986
3713
|
this.siteOptions.set(sites);
|
|
1987
|
-
|
|
1988
|
-
this.siteChoice.set(sites[0]?.id ?? null);
|
|
1989
|
-
}
|
|
3714
|
+
return sites;
|
|
1990
3715
|
}
|
|
1991
3716
|
catch (error) {
|
|
3717
|
+
this.sitesCompanyId = null;
|
|
1992
3718
|
this.siteOptions.set([]);
|
|
1993
3719
|
console.error('[shell] no se pudieron cargar las sedes', error);
|
|
3720
|
+
return [];
|
|
1994
3721
|
}
|
|
1995
3722
|
finally {
|
|
1996
3723
|
this.sitesLoading.set(false);
|
|
1997
3724
|
}
|
|
1998
3725
|
}
|
|
1999
3726
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2000
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusShell, isStandalone: true, selector: "nucleus-shell", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, lead: { classPropertyName: "lead", publicName: "lead", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, activeNavId: { classPropertyName: "activeNavId", publicName: "activeNavId", isSignal: true, isRequired: false, transformFunction: null }, selectedMenuId: { classPropertyName: "selectedMenuId", publicName: "selectedMenuId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navSelect: "navSelect", menuSelect: "menuSelect" }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n </div>\n\n <div class=\"side\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-context\">\n @if (roleName()) {\n <p-tag [value]=\"roleName()!\" severity=\"secondary\" />\n }\n <p class=\"account-place\">{{ contextCaption() }}</p>\n <p-button\n [label]=\"t.switchSite\"\n icon=\"pi pi-building\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"openContext(); account.hide()\"\n />\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <p class=\"access-state\">{{ t.accessLoading }}</p>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n </button>\n\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"contextOpen()\"\n (visibleChange)=\"contextOpen.set($event)\"\n position=\"right\"\n [header]=\"t.contextTitle\"\n [style]=\"{ width: '380px' }\"\n>\n <p class=\"settings-hint\">{{ t.contextHint }}</p>\n\n <label class=\"field-label\" for=\"nucleus-company\">{{ t.companyLabel }}</label>\n <p-select\n inputId=\"nucleus-company\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [fluid]=\"true\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <label class=\"field-label\" for=\"nucleus-site\">{{ t.siteLabel }}</label>\n\n @if (sitesLoading()) {\n <p class=\"access-state\">{{ t.sitesLoading }}</p>\n } @else if (!siteOptions().length) {\n <p class=\"access-state\">{{ t.sitesEmpty }}</p>\n } @else {\n <p-select\n inputId=\"nucleus-site\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"siteChoice.set($event)\"\n [placeholder]=\"t.sitePlaceholder\"\n [fluid]=\"true\"\n />\n }\n\n <div class=\"context-actions\">\n <p-button\n [label]=\"t.confirmSwitch\"\n icon=\"pi pi-arrow-right\"\n [disabled]=\"!canConfirm()\"\n [loading]=\"switching()\"\n [fluid]=\"true\"\n (onClick)=\"confirmSwitch()\"\n />\n </div>\n</p-drawer>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:auto}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}.account-context{display:flex;flex-direction:column;align-items:center;gap:8px;padding:14px 0;border-top:1px solid var(--ac-border);border-bottom:1px solid var(--ac-border)}.account-place{margin:0;color:var(--ac-text-muted);font-size:12px;text-align:center}.field-label{display:block;margin:18px 0 6px;color:var(--ac-text-muted);font-size:12px;letter-spacing:.04em;text-transform:uppercase}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.context-actions{margin-top:24px}.launcher-notice{display:flex;gap:10px;margin-bottom:14px;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}\n"], dependencies: [{ kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1$3.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i2$2.Breadcrumb, selector: "p-breadcrumb", inputs: ["model", "style", "styleClass", "home", "homeAriaLabel"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ConfirmDialogModule }, { kind: "component", type: i4$2.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i5$1.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i6$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: ListboxModule }, { kind: "component", type: i7.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["hostName", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "dropListData", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i8.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i3.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusLordicon, selector: "nucleus-lordicon", inputs: ["src", "trigger", "state", "size", "tone"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3727
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusShell, isStandalone: true, selector: "nucleus-shell", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, lead: { classPropertyName: "lead", publicName: "lead", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, activeNavId: { classPropertyName: "activeNavId", publicName: "activeNavId", isSignal: true, isRequired: false, transformFunction: null }, selectedMenuId: { classPropertyName: "selectedMenuId", publicName: "selectedMenuId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navSelect: "navSelect", menuSelect: "menuSelect" }, host: { properties: { "style.--ns-topbar-h": "topbarHeight()" } }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"], dependencies: [{ kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1$3.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i2$3.Breadcrumb, selector: "p-breadcrumb", inputs: ["model", "style", "styleClass", "home", "homeAriaLabel"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ConfirmDialogModule }, { kind: "component", type: i4$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i5$2.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i6$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusAppearance, selector: "nucleus-appearance" }, { kind: "component", type: NucleusLordicon, selector: "nucleus-lordicon", inputs: ["src", "trigger", "state", "size", "tone"] }, { kind: "component", type: NucleusMenu, selector: "nucleus-menu", inputs: ["sections", "heading", "badge", "searchable", "searchPlaceholder", "emptyText", "keyboardHint", "width"], outputs: ["select", "dismiss"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2001
3728
|
}
|
|
2002
3729
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, decorators: [{
|
|
2003
3730
|
type: Component,
|
|
@@ -2008,17 +3735,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
2008
3735
|
ConfirmDialogModule,
|
|
2009
3736
|
ToastModule,
|
|
2010
3737
|
DrawerModule,
|
|
2011
|
-
ListboxModule,
|
|
2012
3738
|
PopoverModule,
|
|
2013
3739
|
SelectModule,
|
|
3740
|
+
SkeletonModule,
|
|
2014
3741
|
TagModule,
|
|
2015
3742
|
TooltipModule,
|
|
2016
3743
|
FormsModule,
|
|
3744
|
+
NucleusAppearance,
|
|
2017
3745
|
NucleusLordicon,
|
|
3746
|
+
NucleusMenu,
|
|
2018
3747
|
NucleusMark,
|
|
2019
3748
|
NucleusStarfield,
|
|
2020
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n </div>\n\n <div class=\"side\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-context\">\n @if (roleName()) {\n <p-tag [value]=\"roleName()!\" severity=\"secondary\" />\n }\n <p class=\"account-place\">{{ contextCaption() }}</p>\n <p-button\n [label]=\"t.switchSite\"\n icon=\"pi pi-building\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"openContext(); account.hide()\"\n />\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <p class=\"access-state\">{{ t.accessLoading }}</p>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n </button>\n\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"contextOpen()\"\n (visibleChange)=\"contextOpen.set($event)\"\n position=\"right\"\n [header]=\"t.contextTitle\"\n [style]=\"{ width: '380px' }\"\n>\n <p class=\"settings-hint\">{{ t.contextHint }}</p>\n\n <label class=\"field-label\" for=\"nucleus-company\">{{ t.companyLabel }}</label>\n <p-select\n inputId=\"nucleus-company\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [fluid]=\"true\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <label class=\"field-label\" for=\"nucleus-site\">{{ t.siteLabel }}</label>\n\n @if (sitesLoading()) {\n <p class=\"access-state\">{{ t.sitesLoading }}</p>\n } @else if (!siteOptions().length) {\n <p class=\"access-state\">{{ t.sitesEmpty }}</p>\n } @else {\n <p-select\n inputId=\"nucleus-site\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"siteChoice.set($event)\"\n [placeholder]=\"t.sitePlaceholder\"\n [fluid]=\"true\"\n />\n }\n\n <div class=\"context-actions\">\n <p-button\n [label]=\"t.confirmSwitch\"\n icon=\"pi pi-arrow-right\"\n [disabled]=\"!canConfirm()\"\n [loading]=\"switching()\"\n [fluid]=\"true\"\n (onClick)=\"confirmSwitch()\"\n />\n </div>\n</p-drawer>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:auto}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}.account-context{display:flex;flex-direction:column;align-items:center;gap:8px;padding:14px 0;border-top:1px solid var(--ac-border);border-bottom:1px solid var(--ac-border)}.account-place{margin:0;color:var(--ac-text-muted);font-size:12px;text-align:center}.field-label{display:block;margin:18px 0 6px;color:var(--ac-text-muted);font-size:12px;letter-spacing:.04em;text-transform:uppercase}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.context-actions{margin-top:24px}.launcher-notice{display:flex;gap:10px;margin-bottom:14px;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}\n"] }]
|
|
2021
|
-
}], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], lead: [{ type: i0.Input, args: [{ isSignal: true, alias: "lead", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], activeNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeNavId", required: false }] }], selectedMenuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMenuId", required: false }] }], navSelect: [{ type: i0.Output, args: ["navSelect"] }], menuSelect: [{ type: i0.Output, args: ["menuSelect"] }] } });
|
|
3749
|
+
], host: { '[style.--ns-topbar-h]': 'topbarHeight()' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"] }]
|
|
3750
|
+
}], ctorParameters: () => [], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], lead: [{ type: i0.Input, args: [{ isSignal: true, alias: "lead", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], activeNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeNavId", required: false }] }], selectedMenuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMenuId", required: false }] }], navSelect: [{ type: i0.Output, args: ["navSelect"] }], menuSelect: [{ type: i0.Output, args: ["menuSelect"] }] } });
|
|
2022
3751
|
function initialsOf(source) {
|
|
2023
3752
|
const parts = source.trim().split(/\s+/).filter(Boolean);
|
|
2024
3753
|
if (!parts.length)
|
|
@@ -2032,5 +3761,5 @@ function initialsOf(source) {
|
|
|
2032
3761
|
* Generated bundle index. Do not edit.
|
|
2033
3762
|
*/
|
|
2034
3763
|
|
|
2035
|
-
export {
|
|
3764
|
+
export { ACCENTS, CANVASES, COMMON_COPY, CONTROL_CONTRAST, DEFAULT_APPEARANCE, DENSITIES, EMPTY_GEO_VALUE, FIELD_TARGETS, FRAMES, GEO_COPY, LEGACY_THEMES, NO_PERMISSIONS, NUCLEUS_ACCENTS, NUCLEUS_CONFIG, NUCLEUS_DARK_SELECTOR, NUCLEUS_FORCE_BEARER, NUCLEUS_ICONS, NUCLEUS_ICON_CATALOG, NUCLEUS_LANGS, NUCLEUS_LOGOUT_PARAM, NUCLEUS_SKIP_AUTH, NucleusAppearance, NucleusAuth, NucleusCache, NucleusCard, NucleusCell, NucleusColorPicker, NucleusEnterAdvance, NucleusFeedback, NucleusForm, NucleusGeo, NucleusGeoSelect, NucleusIconPicker, NucleusLanguage, NucleusList, NucleusLordicon, NucleusMark, NucleusMenu, NucleusMetric, NucleusMetrics, NucleusPage, NucleusPreset, NucleusRecentApps, NucleusSessionAuth, NucleusShell, NucleusShortcut, NucleusShortcuts, NucleusStarfield, NucleusTabs, SHELL_COPY, SessionService, TEXT_CONTRAST, THEME_COPY, ThemeService, UserSyncService, accentById, accentLabelOf, accentOn, ariaKeysOf, auditAppearance, canvasById, clock, comboOf, commonCopyOf, consumeNucleusLogout, contrastIssue, contrastRatio, day, densityById, endOfDayUtc, fieldsIn, findMenuByRoute, flagOf, foldText, frameById, geoCopyOf, hasChildren, hasModifier, iconClassOf, iconNameOf, integer, isAccentPreference, isApplePlatform, isBeforeToday, isCanvasPreference, isDarkCanvas, isDensityId, isFieldSearchable, isFrameId, isNucleusConfigured, isOnOrBeforeToday, isReachable, isToday, isTypingTarget, isoDay, levelLabel, loadLordicon, loadNucleusConfig, matchesRow, meetsContrast, menuGlyph, menuTile, minutesAsClock, mixHex, money, moveTo, normalizeKeys, nucleusAuthInterceptor, nucleusErrorMessage, nucleusLang, nucleusLogoutUrl, nucleusParseDate, ownsUrl, percent, permissionsOf, provideNucleus, provideNucleusFeedback, provideNucleusTheme, qty, readableOn, relativeLuminance, resolveAccent, resolveFieldSize, searchIcons, selectableRows, shiftDay, shortcutHint, signedMoney, stamp, startOfDayUtc, themeCopyOf, toNumber, toggleCollapsed, visibleTreeRows };
|
|
2036
3765
|
//# sourceMappingURL=nucleus-suite-pe-core.mjs.map
|