@nucleus-suite-pe/core 0.6.1 → 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 +2399 -344
- package/fesm2022/nucleus-suite-pe-core.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/tokens.css +338 -8
- package/types/nucleus-suite-pe-core.d.ts +588 -51
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, Injector, signal, computed, Injectable, effect, input,
|
|
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 { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
6
|
+
import * as i1 from '@angular/forms';
|
|
7
|
+
import { FormsModule } from '@angular/forms';
|
|
8
|
+
import * as i2 from 'primeng/select';
|
|
9
|
+
import { SelectModule } from 'primeng/select';
|
|
5
10
|
import { definePreset } from '@primeuix/themes';
|
|
6
11
|
import Lara from '@primeuix/themes/lara';
|
|
7
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';
|
|
8
16
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
9
|
-
import * as
|
|
10
|
-
import { FormsModule } from '@angular/forms';
|
|
11
|
-
import * as i1 from 'primeng/checkbox';
|
|
12
|
-
import { CheckboxModule } from 'primeng/checkbox';
|
|
13
|
-
import * as i2 from 'primeng/inputtext';
|
|
17
|
+
import * as i2$1 from 'primeng/inputtext';
|
|
14
18
|
import { InputTextModule } from 'primeng/inputtext';
|
|
15
|
-
import * as i3 from 'primeng/
|
|
16
|
-
import {
|
|
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
|
|
22
|
-
import * as i1$1 from 'primeng/button';
|
|
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
|
|
31
|
-
import * as i1$2 from 'primeng/avatar';
|
|
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,352 +656,1310 @@ 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
|
|
|
488
|
-
const
|
|
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
|
+
|
|
1028
|
+
const EMPTY_GEO_VALUE = { countryId: null, divisionId: null };
|
|
1029
|
+
function flagOf(code) {
|
|
1030
|
+
if (code.length !== 2)
|
|
1031
|
+
return '';
|
|
1032
|
+
return String.fromCodePoint(...[...code.toUpperCase()].map((letter) => 0x1f1a5 + letter.charCodeAt(0)));
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const GEO_COPY = {
|
|
1036
|
+
es: {
|
|
1037
|
+
country: 'País',
|
|
1038
|
+
levels: ['Región', 'Provincia', 'Distrito', 'Zona'],
|
|
1039
|
+
kinds: {
|
|
1040
|
+
department: 'Departamento',
|
|
1041
|
+
province: 'Provincia',
|
|
1042
|
+
district: 'Distrito',
|
|
1043
|
+
region: 'Región',
|
|
1044
|
+
state: 'Estado',
|
|
1045
|
+
municipality: 'Municipio',
|
|
1046
|
+
county: 'Condado',
|
|
1047
|
+
parish: 'Parroquia',
|
|
1048
|
+
prefecture: 'Prefectura',
|
|
1049
|
+
governorate: 'Gobernación',
|
|
1050
|
+
territory: 'Territorio',
|
|
1051
|
+
canton: 'Cantón',
|
|
1052
|
+
emirate: 'Emirato',
|
|
1053
|
+
oblast: 'Óblast',
|
|
1054
|
+
city: 'Ciudad',
|
|
1055
|
+
division: 'División',
|
|
1056
|
+
republic: 'República',
|
|
1057
|
+
'autonomous community': 'Comunidad autónoma',
|
|
1058
|
+
'federal district': 'Distrito federal',
|
|
1059
|
+
'capital city': 'Ciudad capital',
|
|
1060
|
+
'metropolitan department': 'Departamento metropolitano',
|
|
1061
|
+
'unitary authority': 'Autoridad unitaria',
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
en: {
|
|
1065
|
+
country: 'Country',
|
|
1066
|
+
levels: ['Region', 'Province', 'District', 'Area'],
|
|
1067
|
+
kinds: {
|
|
1068
|
+
department: 'Department',
|
|
1069
|
+
province: 'Province',
|
|
1070
|
+
district: 'District',
|
|
1071
|
+
region: 'Region',
|
|
1072
|
+
state: 'State',
|
|
1073
|
+
municipality: 'Municipality',
|
|
1074
|
+
county: 'County',
|
|
1075
|
+
parish: 'Parish',
|
|
1076
|
+
prefecture: 'Prefecture',
|
|
1077
|
+
governorate: 'Governorate',
|
|
1078
|
+
territory: 'Territory',
|
|
1079
|
+
canton: 'Canton',
|
|
1080
|
+
emirate: 'Emirate',
|
|
1081
|
+
oblast: 'Oblast',
|
|
1082
|
+
city: 'City',
|
|
1083
|
+
division: 'Division',
|
|
1084
|
+
republic: 'Republic',
|
|
1085
|
+
'autonomous community': 'Autonomous community',
|
|
1086
|
+
'federal district': 'Federal district',
|
|
1087
|
+
'capital city': 'Capital city',
|
|
1088
|
+
'metropolitan department': 'Metropolitan department',
|
|
1089
|
+
'unitary authority': 'Unitary authority',
|
|
1090
|
+
},
|
|
1091
|
+
},
|
|
1092
|
+
};
|
|
1093
|
+
function geoCopyOf(lang) {
|
|
1094
|
+
return lang === 'en' ? GEO_COPY.en : GEO_COPY.es;
|
|
1095
|
+
}
|
|
1096
|
+
function levelLabel(copy, level, kind) {
|
|
1097
|
+
if (kind) {
|
|
1098
|
+
const known = copy.kinds[kind.toLowerCase()];
|
|
1099
|
+
if (known)
|
|
1100
|
+
return known;
|
|
1101
|
+
}
|
|
1102
|
+
const index = Math.min(Math.max(level, 1), copy.levels.length) - 1;
|
|
1103
|
+
return copy.levels[index];
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const EMPTY_LEVEL = { level: 0, kind: null, items: [] };
|
|
1107
|
+
class NucleusGeo {
|
|
1108
|
+
http = inject(HttpClient);
|
|
1109
|
+
config = inject(NUCLEUS_CONFIG);
|
|
1110
|
+
cache = new Map();
|
|
1111
|
+
countries(language) {
|
|
1112
|
+
return this.once(`countries|${language}`, () => firstValueFrom(this.http.get(`${this.config.securityApiUrl}/geo/countries`, {
|
|
1113
|
+
params: { language },
|
|
1114
|
+
})));
|
|
1115
|
+
}
|
|
1116
|
+
divisions(countryId, parentId, language) {
|
|
1117
|
+
const params = { language };
|
|
1118
|
+
if (parentId)
|
|
1119
|
+
params['parentId'] = parentId;
|
|
1120
|
+
return this.once(`divisions|${countryId}|${parentId ?? ''}|${language}`, () => firstValueFrom(this.http.get(`${this.config.securityApiUrl}/geo/countries/${countryId}/divisions`, { params })));
|
|
1121
|
+
}
|
|
1122
|
+
place(divisionId, language) {
|
|
1123
|
+
return this.once(`place|${divisionId}|${language}`, () => firstValueFrom(this.http.get(`${this.config.securityApiUrl}/geo/divisions/${divisionId}/place`, { params: { language } })));
|
|
1124
|
+
}
|
|
1125
|
+
async levelOf(countryId, parentId, language) {
|
|
1126
|
+
try {
|
|
1127
|
+
return await this.divisions(countryId, parentId, language);
|
|
1128
|
+
}
|
|
1129
|
+
catch {
|
|
1130
|
+
return EMPTY_LEVEL;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
once(key, load) {
|
|
1134
|
+
const cached = this.cache.get(key);
|
|
1135
|
+
if (cached)
|
|
1136
|
+
return cached;
|
|
1137
|
+
const started = load().catch((error) => {
|
|
1138
|
+
this.cache.delete(key);
|
|
1139
|
+
throw error;
|
|
1140
|
+
});
|
|
1141
|
+
this.cache.set(key, started);
|
|
1142
|
+
return started;
|
|
1143
|
+
}
|
|
1144
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1145
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeo, providedIn: 'root' });
|
|
1146
|
+
}
|
|
1147
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeo, decorators: [{
|
|
1148
|
+
type: Injectable,
|
|
1149
|
+
args: [{ providedIn: 'root' }]
|
|
1150
|
+
}] });
|
|
1151
|
+
|
|
1152
|
+
class NucleusGeoSelect {
|
|
1153
|
+
geo = inject(NucleusGeo);
|
|
1154
|
+
language = inject(NucleusLanguage);
|
|
1155
|
+
value = model(EMPTY_GEO_VALUE, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1156
|
+
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1157
|
+
columns = input(null, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
1158
|
+
countries = signal([], ...(ngDevMode ? [{ debugName: "countries" }] : /* istanbul ignore next */ []));
|
|
1159
|
+
steps = signal([], ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
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 */ []));
|
|
1162
|
+
syncedKey = null;
|
|
1163
|
+
constructor() {
|
|
1164
|
+
effect(() => {
|
|
1165
|
+
const lang = this.language.lang();
|
|
1166
|
+
const current = this.value();
|
|
1167
|
+
const key = `${lang}|${current.countryId ?? ''}|${current.divisionId ?? ''}`;
|
|
1168
|
+
if (key === this.syncedKey)
|
|
1169
|
+
return;
|
|
1170
|
+
this.syncedKey = key;
|
|
1171
|
+
untracked(() => void this.sync(current, lang));
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
gridClass() {
|
|
1175
|
+
const columns = this.columns();
|
|
1176
|
+
return columns ? `is-cap-${Math.min(Math.max(columns, 1), 4)}` : '';
|
|
1177
|
+
}
|
|
1178
|
+
nameOf(step) {
|
|
1179
|
+
return levelLabel(this.labels(), step.level, step.kind);
|
|
1180
|
+
}
|
|
1181
|
+
async pickCountry(countryId) {
|
|
1182
|
+
const lang = this.language.lang();
|
|
1183
|
+
this.syncedKey = `${lang}|${countryId ?? ''}|`;
|
|
1184
|
+
this.value.set({ countryId, divisionId: null });
|
|
1185
|
+
if (!countryId) {
|
|
1186
|
+
this.steps.set([]);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
const first = await this.geo.levelOf(countryId, null, lang);
|
|
1190
|
+
this.steps.set(first.items.length ? [toStep(first.level, first.kind, first.items, null)] : []);
|
|
1191
|
+
}
|
|
1192
|
+
async pick(index, divisionId) {
|
|
1193
|
+
const countryId = this.value().countryId;
|
|
1194
|
+
if (!countryId)
|
|
1195
|
+
return;
|
|
1196
|
+
const lang = this.language.lang();
|
|
1197
|
+
const next = this.steps().slice(0, index + 1);
|
|
1198
|
+
const current = next[index];
|
|
1199
|
+
if (!current)
|
|
1200
|
+
return;
|
|
1201
|
+
next[index] = { ...current, selectedId: divisionId };
|
|
1202
|
+
if (divisionId) {
|
|
1203
|
+
const child = await this.geo.levelOf(countryId, divisionId, lang);
|
|
1204
|
+
if (child.items.length) {
|
|
1205
|
+
next.push(toStep(child.level, child.kind, child.items, null));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
this.steps.set(next);
|
|
1209
|
+
const deepest = deepestOf(next);
|
|
1210
|
+
this.syncedKey = `${lang}|${countryId}|${deepest ?? ''}`;
|
|
1211
|
+
this.value.set({ countryId, divisionId: deepest });
|
|
1212
|
+
}
|
|
1213
|
+
async sync(value, lang) {
|
|
1214
|
+
this.countries.set([...(await this.geo.countries(lang))]);
|
|
1215
|
+
if (!value.countryId) {
|
|
1216
|
+
this.steps.set([]);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
if (!value.divisionId) {
|
|
1220
|
+
const first = await this.geo.levelOf(value.countryId, null, lang);
|
|
1221
|
+
this.steps.set(first.items.length ? [toStep(first.level, first.kind, first.items, null)] : []);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const place = await this.geo.place(value.divisionId, lang);
|
|
1225
|
+
const built = [];
|
|
1226
|
+
let parentId = null;
|
|
1227
|
+
for (const node of place.trail) {
|
|
1228
|
+
const level = await this.geo.levelOf(place.countryId, parentId, lang);
|
|
1229
|
+
if (!level.items.length)
|
|
1230
|
+
break;
|
|
1231
|
+
built.push(toStep(level.level, level.kind, level.items, node.id));
|
|
1232
|
+
parentId = node.id;
|
|
1233
|
+
}
|
|
1234
|
+
const tail = await this.geo.levelOf(place.countryId, parentId, lang);
|
|
1235
|
+
if (tail.items.length) {
|
|
1236
|
+
built.push(toStep(tail.level, tail.kind, tail.items, null));
|
|
1237
|
+
}
|
|
1238
|
+
this.steps.set(built);
|
|
1239
|
+
}
|
|
1240
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeoSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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: `
|
|
1242
|
+
@let copy = labels();
|
|
1243
|
+
@let shared = words();
|
|
1244
|
+
|
|
1245
|
+
<div class="ns-form-grid" [class]="gridClass()">
|
|
1246
|
+
<div class="ns-field is-span-1 is-size-md">
|
|
1247
|
+
<label for="ns-geo-country">{{ copy.country }}</label>
|
|
1248
|
+
<p-select
|
|
1249
|
+
inputId="ns-geo-country"
|
|
1250
|
+
[options]="countries()"
|
|
1251
|
+
optionLabel="name"
|
|
1252
|
+
optionValue="id"
|
|
1253
|
+
[ngModel]="value().countryId"
|
|
1254
|
+
(ngModelChange)="pickCountry($event)"
|
|
1255
|
+
[disabled]="readonly()"
|
|
1256
|
+
[filter]="true"
|
|
1257
|
+
filterBy="name"
|
|
1258
|
+
[filterPlaceholder]="shared.search"
|
|
1259
|
+
[autofocusFilter]="true"
|
|
1260
|
+
[resetFilterOnHide]="true"
|
|
1261
|
+
[showClear]="true"
|
|
1262
|
+
[placeholder]="shared.choose"
|
|
1263
|
+
[ngModelOptions]="{ standalone: true }"
|
|
1264
|
+
fluid
|
|
1265
|
+
/>
|
|
1266
|
+
</div>
|
|
1267
|
+
|
|
1268
|
+
@for (step of steps(); track step.level; let index = $index) {
|
|
1269
|
+
<div class="ns-field is-span-1 is-size-md">
|
|
1270
|
+
<label [attr.for]="'ns-geo-' + step.level">{{ nameOf(step) }}</label>
|
|
1271
|
+
<p-select
|
|
1272
|
+
[inputId]="'ns-geo-' + step.level"
|
|
1273
|
+
[options]="step.items"
|
|
1274
|
+
optionLabel="name"
|
|
1275
|
+
optionValue="id"
|
|
1276
|
+
[ngModel]="step.selectedId"
|
|
1277
|
+
(ngModelChange)="pick(index, $event)"
|
|
1278
|
+
[disabled]="readonly()"
|
|
1279
|
+
[filter]="true"
|
|
1280
|
+
filterBy="name"
|
|
1281
|
+
[filterPlaceholder]="shared.search"
|
|
1282
|
+
[autofocusFilter]="true"
|
|
1283
|
+
[resetFilterOnHide]="true"
|
|
1284
|
+
[showClear]="true"
|
|
1285
|
+
[placeholder]="shared.choose"
|
|
1286
|
+
[ngModelOptions]="{ standalone: true }"
|
|
1287
|
+
fluid
|
|
1288
|
+
/>
|
|
1289
|
+
</div>
|
|
1290
|
+
}
|
|
1291
|
+
</div>
|
|
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 });
|
|
1293
|
+
}
|
|
1294
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusGeoSelect, decorators: [{
|
|
1295
|
+
type: Component,
|
|
1296
|
+
args: [{ selector: 'nucleus-geo-select', imports: [SelectModule, FormsModule], hostDirectives: [NucleusEnterAdvance], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1297
|
+
@let copy = labels();
|
|
1298
|
+
@let shared = words();
|
|
1299
|
+
|
|
1300
|
+
<div class="ns-form-grid" [class]="gridClass()">
|
|
1301
|
+
<div class="ns-field is-span-1 is-size-md">
|
|
1302
|
+
<label for="ns-geo-country">{{ copy.country }}</label>
|
|
1303
|
+
<p-select
|
|
1304
|
+
inputId="ns-geo-country"
|
|
1305
|
+
[options]="countries()"
|
|
1306
|
+
optionLabel="name"
|
|
1307
|
+
optionValue="id"
|
|
1308
|
+
[ngModel]="value().countryId"
|
|
1309
|
+
(ngModelChange)="pickCountry($event)"
|
|
1310
|
+
[disabled]="readonly()"
|
|
1311
|
+
[filter]="true"
|
|
1312
|
+
filterBy="name"
|
|
1313
|
+
[filterPlaceholder]="shared.search"
|
|
1314
|
+
[autofocusFilter]="true"
|
|
1315
|
+
[resetFilterOnHide]="true"
|
|
1316
|
+
[showClear]="true"
|
|
1317
|
+
[placeholder]="shared.choose"
|
|
1318
|
+
[ngModelOptions]="{ standalone: true }"
|
|
1319
|
+
fluid
|
|
1320
|
+
/>
|
|
1321
|
+
</div>
|
|
1322
|
+
|
|
1323
|
+
@for (step of steps(); track step.level; let index = $index) {
|
|
1324
|
+
<div class="ns-field is-span-1 is-size-md">
|
|
1325
|
+
<label [attr.for]="'ns-geo-' + step.level">{{ nameOf(step) }}</label>
|
|
1326
|
+
<p-select
|
|
1327
|
+
[inputId]="'ns-geo-' + step.level"
|
|
1328
|
+
[options]="step.items"
|
|
1329
|
+
optionLabel="name"
|
|
1330
|
+
optionValue="id"
|
|
1331
|
+
[ngModel]="step.selectedId"
|
|
1332
|
+
(ngModelChange)="pick(index, $event)"
|
|
1333
|
+
[disabled]="readonly()"
|
|
1334
|
+
[filter]="true"
|
|
1335
|
+
filterBy="name"
|
|
1336
|
+
[filterPlaceholder]="shared.search"
|
|
1337
|
+
[autofocusFilter]="true"
|
|
1338
|
+
[resetFilterOnHide]="true"
|
|
1339
|
+
[showClear]="true"
|
|
1340
|
+
[placeholder]="shared.choose"
|
|
1341
|
+
[ngModelOptions]="{ standalone: true }"
|
|
1342
|
+
fluid
|
|
1343
|
+
/>
|
|
1344
|
+
</div>
|
|
1345
|
+
}
|
|
1346
|
+
</div>
|
|
1347
|
+
`, styles: [":host{display:block}\n"] }]
|
|
1348
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }] } });
|
|
1349
|
+
function toStep(level, kind, items, selectedId) {
|
|
1350
|
+
return { level, kind, items: [...items], selectedId };
|
|
1351
|
+
}
|
|
1352
|
+
function deepestOf(steps) {
|
|
1353
|
+
for (let index = steps.length - 1; index >= 0; index -= 1) {
|
|
1354
|
+
const selected = steps[index]?.selectedId;
|
|
1355
|
+
if (selected)
|
|
1356
|
+
return selected;
|
|
1357
|
+
}
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
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 = [
|
|
489
1402
|
{
|
|
490
1403
|
id: 'nucleus',
|
|
491
1404
|
scheme: 'dark',
|
|
492
|
-
swatch: '#7c6cf5',
|
|
493
1405
|
starfield: true,
|
|
1406
|
+
accent: 'violet',
|
|
494
1407
|
tokens: {
|
|
495
|
-
bg: '#
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
accent: '#7c6cf5',
|
|
502
|
-
onAccent: '#ffffff',
|
|
1408
|
+
bg: '#16132b',
|
|
1409
|
+
raised: '#241f42',
|
|
1410
|
+
ink: '#ece9fb',
|
|
1411
|
+
muted: '#9d95c6',
|
|
1412
|
+
line: '#302a54',
|
|
1413
|
+
glow: '#8c7ff0',
|
|
503
1414
|
},
|
|
504
1415
|
},
|
|
505
1416
|
{
|
|
506
|
-
id: '
|
|
507
|
-
scheme: '
|
|
508
|
-
swatch: '#f5f5f5',
|
|
1417
|
+
id: 'midnight',
|
|
1418
|
+
scheme: 'dark',
|
|
509
1419
|
starfield: false,
|
|
1420
|
+
accent: 'blue',
|
|
510
1421
|
tokens: {
|
|
511
|
-
bg: '#
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
accent: '#0078d4',
|
|
518
|
-
onAccent: '#ffffff',
|
|
1422
|
+
bg: '#131f33',
|
|
1423
|
+
raised: '#1e2e48',
|
|
1424
|
+
ink: '#e3ecfa',
|
|
1425
|
+
muted: '#94a9c9',
|
|
1426
|
+
line: '#2b3d5d',
|
|
1427
|
+
glow: '#6ea8f0',
|
|
519
1428
|
},
|
|
520
1429
|
},
|
|
521
1430
|
{
|
|
522
|
-
id: '
|
|
1431
|
+
id: 'ocean',
|
|
523
1432
|
scheme: 'dark',
|
|
524
|
-
swatch: '#1e1e1e',
|
|
525
1433
|
starfield: false,
|
|
1434
|
+
accent: 'cyan',
|
|
526
1435
|
tokens: {
|
|
527
|
-
bg: '#
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
accent: '#0097fb',
|
|
534
|
-
onAccent: '#ffffff',
|
|
1436
|
+
bg: '#0d2a30',
|
|
1437
|
+
raised: '#153c45',
|
|
1438
|
+
ink: '#e0f2f4',
|
|
1439
|
+
muted: '#8cb3b9',
|
|
1440
|
+
line: '#1d4e58',
|
|
1441
|
+
glow: '#4cc5e8',
|
|
535
1442
|
},
|
|
536
1443
|
},
|
|
537
1444
|
{
|
|
538
|
-
id: '
|
|
539
|
-
scheme: '
|
|
540
|
-
swatch: '#f7b8d4',
|
|
1445
|
+
id: 'forest',
|
|
1446
|
+
scheme: 'dark',
|
|
541
1447
|
starfield: false,
|
|
1448
|
+
accent: 'green',
|
|
542
1449
|
tokens: {
|
|
543
|
-
bg: '#
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
accent: '#d6337f',
|
|
550
|
-
onAccent: '#ffffff',
|
|
1450
|
+
bg: '#122419',
|
|
1451
|
+
raised: '#1d3526',
|
|
1452
|
+
ink: '#e2f1e7',
|
|
1453
|
+
muted: '#93b8a1',
|
|
1454
|
+
line: '#28462f',
|
|
1455
|
+
glow: '#5cc47f',
|
|
551
1456
|
},
|
|
552
1457
|
},
|
|
553
1458
|
{
|
|
554
|
-
id: '
|
|
555
|
-
scheme: '
|
|
556
|
-
swatch: '#a8d5e8',
|
|
1459
|
+
id: 'plum',
|
|
1460
|
+
scheme: 'dark',
|
|
557
1461
|
starfield: false,
|
|
1462
|
+
accent: 'rose',
|
|
558
1463
|
tokens: {
|
|
559
|
-
bg: '#
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
accent: '#0f7ba8',
|
|
566
|
-
onAccent: '#ffffff',
|
|
1464
|
+
bg: '#241330',
|
|
1465
|
+
raised: '#341e43',
|
|
1466
|
+
ink: '#f3e8f7',
|
|
1467
|
+
muted: '#b596c3',
|
|
1468
|
+
line: '#452851',
|
|
1469
|
+
glow: '#cf7fdb',
|
|
567
1470
|
},
|
|
568
1471
|
},
|
|
569
1472
|
{
|
|
570
|
-
id: '
|
|
1473
|
+
id: 'ember',
|
|
571
1474
|
scheme: 'dark',
|
|
572
|
-
swatch: '#3b4a5c',
|
|
573
1475
|
starfield: false,
|
|
1476
|
+
accent: 'red',
|
|
574
1477
|
tokens: {
|
|
575
|
-
bg: '#
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
accent: '#5fa8e8',
|
|
582
|
-
onAccent: '#0b1118',
|
|
1478
|
+
bg: '#2a1113',
|
|
1479
|
+
raised: '#3d1b1f',
|
|
1480
|
+
ink: '#f8e6e7',
|
|
1481
|
+
muted: '#c9979c',
|
|
1482
|
+
line: '#4d2429',
|
|
1483
|
+
glow: '#f0797b',
|
|
583
1484
|
},
|
|
584
1485
|
},
|
|
585
1486
|
{
|
|
586
|
-
id: '
|
|
587
|
-
scheme: '
|
|
588
|
-
swatch: '#c4ece5',
|
|
1487
|
+
id: 'sand',
|
|
1488
|
+
scheme: 'dark',
|
|
589
1489
|
starfield: false,
|
|
1490
|
+
accent: 'amber',
|
|
590
1491
|
tokens: {
|
|
591
|
-
bg: '#
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
accent: '#0d8f7d',
|
|
598
|
-
onAccent: '#ffffff',
|
|
1492
|
+
bg: '#2b2415',
|
|
1493
|
+
raised: '#3d3320',
|
|
1494
|
+
ink: '#f6efdd',
|
|
1495
|
+
muted: '#c2b18b',
|
|
1496
|
+
line: '#4b3f28',
|
|
1497
|
+
glow: '#e0a33a',
|
|
599
1498
|
},
|
|
600
1499
|
},
|
|
601
1500
|
{
|
|
602
|
-
id: '
|
|
1501
|
+
id: 'slate',
|
|
603
1502
|
scheme: 'dark',
|
|
604
|
-
swatch: '#6b3a7a',
|
|
605
1503
|
starfield: false,
|
|
1504
|
+
accent: 'blue',
|
|
606
1505
|
tokens: {
|
|
607
|
-
bg: '#
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
accent: '#c86dd7',
|
|
614
|
-
onAccent: '#1a0d20',
|
|
1506
|
+
bg: '#232b36',
|
|
1507
|
+
raised: '#313c4b',
|
|
1508
|
+
ink: '#e6ecf4',
|
|
1509
|
+
muted: '#9fb0c4',
|
|
1510
|
+
line: '#3d4a5b',
|
|
1511
|
+
glow: '#6fb2ea',
|
|
615
1512
|
},
|
|
616
1513
|
},
|
|
617
1514
|
{
|
|
618
|
-
id: '
|
|
619
|
-
scheme: '
|
|
620
|
-
swatch: '#f7c48a',
|
|
1515
|
+
id: 'ink',
|
|
1516
|
+
scheme: 'dark',
|
|
621
1517
|
starfield: false,
|
|
1518
|
+
accent: 'blue',
|
|
622
1519
|
tokens: {
|
|
623
|
-
bg: '#
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
accent: '#e07314',
|
|
630
|
-
onAccent: '#ffffff',
|
|
1520
|
+
bg: '#101318',
|
|
1521
|
+
raised: '#1b2129',
|
|
1522
|
+
ink: '#edf0f4',
|
|
1523
|
+
muted: '#97a1ae',
|
|
1524
|
+
line: '#262d37',
|
|
1525
|
+
glow: '#7fb3d9',
|
|
631
1526
|
},
|
|
632
1527
|
},
|
|
633
1528
|
{
|
|
634
|
-
id: '
|
|
1529
|
+
id: 'graphite',
|
|
635
1530
|
scheme: 'dark',
|
|
636
|
-
swatch: '#1f3a5f',
|
|
637
1531
|
starfield: false,
|
|
1532
|
+
accent: 'violet',
|
|
638
1533
|
tokens: {
|
|
639
|
-
bg: '#
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
accent: '#6ea8f0',
|
|
646
|
-
onAccent: '#0a1020',
|
|
1534
|
+
bg: '#17171a',
|
|
1535
|
+
raised: '#232327',
|
|
1536
|
+
ink: '#eeeef0',
|
|
1537
|
+
muted: '#9c9ca4',
|
|
1538
|
+
line: '#2f2f35',
|
|
1539
|
+
glow: '#b9b9c2',
|
|
647
1540
|
},
|
|
648
1541
|
},
|
|
649
1542
|
{
|
|
650
|
-
id: '
|
|
651
|
-
scheme: '
|
|
652
|
-
|
|
1543
|
+
id: 'paper',
|
|
1544
|
+
scheme: 'light',
|
|
1545
|
+
starfield: false,
|
|
1546
|
+
accent: 'violet',
|
|
1547
|
+
tokens: {
|
|
1548
|
+
bg: '#ffffff',
|
|
1549
|
+
raised: '#f1f0f5',
|
|
1550
|
+
ink: '#16151d',
|
|
1551
|
+
muted: '#5e5c6b',
|
|
1552
|
+
line: '#e3e2ea',
|
|
1553
|
+
glow: '#5b4fd6',
|
|
1554
|
+
},
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
id: 'mist',
|
|
1558
|
+
scheme: 'light',
|
|
653
1559
|
starfield: false,
|
|
1560
|
+
accent: 'blue',
|
|
654
1561
|
tokens: {
|
|
655
|
-
bg: '#
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
accent: '#4caf70',
|
|
662
|
-
onAccent: '#0a1610',
|
|
1562
|
+
bg: '#eef1f6',
|
|
1563
|
+
raised: '#e2e7ef',
|
|
1564
|
+
ink: '#141a24',
|
|
1565
|
+
muted: '#576274',
|
|
1566
|
+
line: '#d3dae5',
|
|
1567
|
+
glow: '#1160b8',
|
|
663
1568
|
},
|
|
664
1569
|
},
|
|
1570
|
+
];
|
|
1571
|
+
const CANVASES = [
|
|
665
1572
|
{
|
|
666
|
-
id: '
|
|
667
|
-
scheme: 'light',
|
|
668
|
-
swatch: '#f3c2d6',
|
|
669
|
-
starfield: false,
|
|
1573
|
+
id: 'light',
|
|
670
1574
|
tokens: {
|
|
671
|
-
bg: '#
|
|
672
|
-
surface: '#
|
|
673
|
-
surfaceAlt: '#
|
|
674
|
-
text: '#
|
|
675
|
-
textMuted: '#
|
|
676
|
-
border: '#
|
|
677
|
-
|
|
678
|
-
|
|
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',
|
|
679
1593
|
},
|
|
680
1594
|
},
|
|
681
1595
|
{
|
|
682
|
-
id: '
|
|
683
|
-
scheme: 'dark',
|
|
684
|
-
swatch: '#8f1f24',
|
|
685
|
-
starfield: false,
|
|
1596
|
+
id: 'dim',
|
|
686
1597
|
tokens: {
|
|
687
|
-
bg: '#
|
|
688
|
-
surface: '#
|
|
689
|
-
surfaceAlt: '#
|
|
690
|
-
text: '#
|
|
691
|
-
textMuted: '#
|
|
692
|
-
border: '#
|
|
693
|
-
|
|
694
|
-
|
|
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',
|
|
695
1616
|
},
|
|
696
1617
|
},
|
|
697
1618
|
{
|
|
698
|
-
id: '
|
|
699
|
-
scheme: 'light',
|
|
700
|
-
swatch: '#efe4b0',
|
|
701
|
-
starfield: false,
|
|
1619
|
+
id: 'dark',
|
|
702
1620
|
tokens: {
|
|
703
|
-
bg: '#
|
|
704
|
-
surface: '#
|
|
705
|
-
surfaceAlt: '#
|
|
706
|
-
text: '#
|
|
707
|
-
textMuted: '#
|
|
708
|
-
border: '#
|
|
709
|
-
|
|
710
|
-
|
|
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',
|
|
711
1639
|
},
|
|
712
1640
|
},
|
|
713
1641
|
];
|
|
714
|
-
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 = {
|
|
1752
|
+
nucleus: 'Nucleus',
|
|
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',
|
|
1764
|
+
};
|
|
1765
|
+
const FRAMES_EN = {
|
|
715
1766
|
nucleus: 'Nucleus',
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
+
},
|
|
729
1834
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
function themeById(id) {
|
|
733
|
-
return BY_ID.get(id) ?? BY_ID.get(DEFAULT_THEME);
|
|
1835
|
+
function themeCopyOf(lang) {
|
|
1836
|
+
return THEME_COPY[lang] ?? THEME_COPY.es;
|
|
734
1837
|
}
|
|
735
|
-
function
|
|
736
|
-
return
|
|
1838
|
+
function accentLabelOf(lang, id) {
|
|
1839
|
+
return themeCopyOf(lang).accents[id];
|
|
737
1840
|
}
|
|
738
1841
|
|
|
739
|
-
const STORAGE_KEY = '
|
|
740
|
-
|
|
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;
|
|
741
1848
|
try {
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
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
|
+
}
|
|
745
1877
|
}
|
|
746
1878
|
catch { }
|
|
747
|
-
return
|
|
1879
|
+
return DEFAULT_APPEARANCE;
|
|
748
1880
|
}
|
|
749
1881
|
function prefersDark() {
|
|
750
|
-
return globalThis.matchMedia?.(
|
|
1882
|
+
return globalThis.matchMedia?.(DARK_QUERY).matches ?? false;
|
|
751
1883
|
}
|
|
752
1884
|
class ThemeService {
|
|
753
|
-
|
|
1885
|
+
_appearance = signal(readStored(), ...(ngDevMode ? [{ debugName: "_appearance" }] : /* istanbul ignore next */ []));
|
|
754
1886
|
systemDark = signal(prefersDark(), ...(ngDevMode ? [{ debugName: "systemDark" }] : /* istanbul ignore next */ []));
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
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 */ []));
|
|
763
1901
|
constructor() {
|
|
764
1902
|
globalThis
|
|
765
|
-
.matchMedia?.(
|
|
1903
|
+
.matchMedia?.(DARK_QUERY)
|
|
766
1904
|
.addEventListener('change', (event) => this.systemDark.set(event.matches));
|
|
767
1905
|
effect(() => {
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
this.applyToDom(theme);
|
|
1906
|
+
const appearance = this._appearance();
|
|
1907
|
+
this.applyToDom();
|
|
771
1908
|
try {
|
|
772
|
-
localStorage.setItem(STORAGE_KEY,
|
|
1909
|
+
localStorage.setItem(STORAGE_KEY$1, JSON.stringify(appearance));
|
|
773
1910
|
}
|
|
774
1911
|
catch { }
|
|
775
1912
|
});
|
|
776
1913
|
}
|
|
777
|
-
|
|
778
|
-
this.
|
|
1914
|
+
setFrame(frame) {
|
|
1915
|
+
this.patch({ frame });
|
|
1916
|
+
}
|
|
1917
|
+
setCanvas(canvas) {
|
|
1918
|
+
this.patch({ canvas });
|
|
1919
|
+
}
|
|
1920
|
+
setAccent(accent) {
|
|
1921
|
+
this.patch({ accent });
|
|
779
1922
|
}
|
|
780
|
-
|
|
1923
|
+
setDensity(density) {
|
|
1924
|
+
this.patch({ density });
|
|
1925
|
+
}
|
|
1926
|
+
reset() {
|
|
1927
|
+
this._appearance.set(DEFAULT_APPEARANCE);
|
|
1928
|
+
}
|
|
1929
|
+
patch(change) {
|
|
1930
|
+
this._appearance.update((appearance) => ({ ...appearance, ...change }));
|
|
1931
|
+
}
|
|
1932
|
+
applyToDom() {
|
|
781
1933
|
const root = document.documentElement;
|
|
782
|
-
|
|
783
|
-
|
|
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);
|
|
784
1944
|
}
|
|
785
|
-
root.
|
|
786
|
-
root.
|
|
787
|
-
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';
|
|
788
1963
|
}
|
|
789
1964
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
790
1965
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ThemeService, providedIn: 'root' });
|
|
@@ -793,12 +1968,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
793
1968
|
type: Injectable,
|
|
794
1969
|
args: [{ providedIn: 'root' }]
|
|
795
1970
|
}], ctorParameters: () => [] });
|
|
1971
|
+
function kebab(name) {
|
|
1972
|
+
return name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
1973
|
+
}
|
|
796
1974
|
|
|
797
1975
|
const MASK = {
|
|
798
1976
|
background: 'rgba(0, 0, 0, 0.55)',
|
|
799
1977
|
color: 'var(--ac-text)',
|
|
800
1978
|
};
|
|
801
|
-
const SHADOW = '0 10px 40px
|
|
1979
|
+
const SHADOW = 'var(--ns-shadow-2, 0 10px 40px rgb(0 0 0 / 45%))';
|
|
802
1980
|
const PANEL = {
|
|
803
1981
|
background: 'var(--ac-surface)',
|
|
804
1982
|
borderColor: 'var(--ac-border)',
|
|
@@ -848,6 +2026,7 @@ const schemeTokens = {
|
|
|
848
2026
|
},
|
|
849
2027
|
},
|
|
850
2028
|
formField: {
|
|
2029
|
+
paddingY: 'var(--ns-control-y, 0.55rem)',
|
|
851
2030
|
background: 'var(--ac-bg)',
|
|
852
2031
|
disabledBackground: 'var(--ac-surface-alt)',
|
|
853
2032
|
filledBackground: 'var(--ac-surface-alt)',
|
|
@@ -953,9 +2132,9 @@ class NucleusLordicon {
|
|
|
953
2132
|
size = input(48, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
954
2133
|
tone = input('accent', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
955
2134
|
colors = computed(() => {
|
|
956
|
-
const
|
|
957
|
-
const primary = this.tone() === 'danger' ?
|
|
958
|
-
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}`;
|
|
959
2138
|
}, ...(ngDevMode ? [{ debugName: "colors" }] : /* istanbul ignore next */ []));
|
|
960
2139
|
constructor() {
|
|
961
2140
|
void loadLordicon();
|
|
@@ -990,8 +2169,23 @@ class NucleusCard {
|
|
|
990
2169
|
heading = input('', ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
991
2170
|
subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
992
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 */ []));
|
|
993
2187
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
994
|
-
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: `
|
|
995
2189
|
@if (heading() || subtitle()) {
|
|
996
2190
|
<div class="head">
|
|
997
2191
|
@if (heading()) {
|
|
@@ -1003,16 +2197,29 @@ class NucleusCard {
|
|
|
1003
2197
|
</div>
|
|
1004
2198
|
}
|
|
1005
2199
|
|
|
1006
|
-
|
|
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>
|
|
1007
2214
|
|
|
1008
2215
|
<div class="actions">
|
|
1009
2216
|
<ng-content select="[nucleusCardActions]" />
|
|
1010
2217
|
</div>
|
|
1011
|
-
`, 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 });
|
|
1012
2219
|
}
|
|
1013
2220
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusCard, decorators: [{
|
|
1014
2221
|
type: Component,
|
|
1015
|
-
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: `
|
|
1016
2223
|
@if (heading() || subtitle()) {
|
|
1017
2224
|
<div class="head">
|
|
1018
2225
|
@if (heading()) {
|
|
@@ -1024,13 +2231,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1024
2231
|
</div>
|
|
1025
2232
|
}
|
|
1026
2233
|
|
|
1027
|
-
|
|
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>
|
|
1028
2248
|
|
|
1029
2249
|
<div class="actions">
|
|
1030
2250
|
<ng-content select="[nucleusCardActions]" />
|
|
1031
2251
|
</div>
|
|
1032
|
-
`, styles: [":host{display:block;padding:22px 24px;border:1px solid var(--ac-border);border-radius:12px;background:
|
|
1033
|
-
}], 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 }] }] } });
|
|
1034
2333
|
|
|
1035
2334
|
class NucleusTabs {
|
|
1036
2335
|
tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
@@ -1139,8 +2438,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1139
2438
|
function resolveFieldSize(field) {
|
|
1140
2439
|
if (field.size)
|
|
1141
2440
|
return field.size;
|
|
1142
|
-
if (field.kind === 'textarea')
|
|
2441
|
+
if (field.kind === 'textarea' || field.kind === 'color')
|
|
1143
2442
|
return 'full';
|
|
2443
|
+
if (field.kind === 'icon')
|
|
2444
|
+
return 'md';
|
|
1144
2445
|
const max = field.maxlength;
|
|
1145
2446
|
if (max === undefined)
|
|
1146
2447
|
return 'md';
|
|
@@ -1154,13 +2455,343 @@ function resolveFieldSize(field) {
|
|
|
1154
2455
|
return 'lg';
|
|
1155
2456
|
return 'full';
|
|
1156
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 }] }] } });
|
|
1157
2769
|
|
|
1158
2770
|
class NucleusForm {
|
|
2771
|
+
language = inject(NucleusLanguage);
|
|
2772
|
+
host = inject(ElementRef);
|
|
2773
|
+
injector = inject(Injector);
|
|
1159
2774
|
fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
1160
2775
|
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1161
2776
|
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1162
2777
|
columns = input(null, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
2778
|
+
autofocus = input(false, ...(ngDevMode ? [{ debugName: "autofocus" }] : /* istanbul ignore next */ []));
|
|
1163
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
|
+
}
|
|
1164
2795
|
read(field) {
|
|
1165
2796
|
return this.value()[field.key] ?? null;
|
|
1166
2797
|
}
|
|
@@ -1168,6 +2799,10 @@ class NucleusForm {
|
|
|
1168
2799
|
const raw = this.value()[field.key];
|
|
1169
2800
|
return raw === null || raw === undefined ? '' : String(raw);
|
|
1170
2801
|
}
|
|
2802
|
+
nullableText(field) {
|
|
2803
|
+
const raw = this.value()[field.key];
|
|
2804
|
+
return raw === null || raw === undefined || raw === '' ? null : String(raw);
|
|
2805
|
+
}
|
|
1171
2806
|
checked(field) {
|
|
1172
2807
|
return this.value()[field.key] === true;
|
|
1173
2808
|
}
|
|
@@ -1177,6 +2812,9 @@ class NucleusForm {
|
|
|
1177
2812
|
isDisabled(field) {
|
|
1178
2813
|
return this.readonly() || field.disabled === true;
|
|
1179
2814
|
}
|
|
2815
|
+
isSearchable(field) {
|
|
2816
|
+
return isFieldSearchable(field);
|
|
2817
|
+
}
|
|
1180
2818
|
fieldClass(field) {
|
|
1181
2819
|
const span = field.span ?? 1;
|
|
1182
2820
|
return `is-span-${span === 'full' ? 'full' : span} is-size-${resolveFieldSize(field)}`;
|
|
@@ -1192,7 +2830,7 @@ class NucleusForm {
|
|
|
1192
2830
|
return field.kind ?? 'text';
|
|
1193
2831
|
}
|
|
1194
2832
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1195
|
-
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=\"grid\" [class]=\"gridClass()\">\n @for (field of fields(); track field.key) {\n <div class=\"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 });
|
|
1196
2834
|
}
|
|
1197
2835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusForm, decorators: [{
|
|
1198
2836
|
type: Component,
|
|
@@ -1203,8 +2841,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1203
2841
|
TextareaModule,
|
|
1204
2842
|
ToggleSwitchModule,
|
|
1205
2843
|
FormsModule,
|
|
1206
|
-
|
|
1207
|
-
|
|
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"] }] } });
|
|
1208
2848
|
|
|
1209
2849
|
class NucleusCell {
|
|
1210
2850
|
nucleusCell = input.required(...(ngDevMode ? [{ debugName: "nucleusCell" }] : /* istanbul ignore next */ []));
|
|
@@ -1226,12 +2866,24 @@ class NucleusList {
|
|
|
1226
2866
|
empty = input('', ...(ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []));
|
|
1227
2867
|
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1228
2868
|
minWidth = input('640px', ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
|
|
2869
|
+
skeletonRows = input(5, ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
|
|
1229
2870
|
rowClass = input(() => '', ...(ngDevMode ? [{ debugName: "rowClass" }] : /* istanbul ignore next */ []));
|
|
1230
2871
|
action = output();
|
|
1231
2872
|
toggle = output();
|
|
1232
2873
|
cells = contentChildren(NucleusCell, ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
|
|
1233
2874
|
tableStyle = computed(() => ({ 'min-width': this.minWidth() }), ...(ngDevMode ? [{ debugName: "tableStyle" }] : /* istanbul ignore next */ []));
|
|
1234
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
|
+
}
|
|
1235
2887
|
cellTemplate(key) {
|
|
1236
2888
|
return this.cells().find((cell) => cell.nucleusCell() === key)?.template ?? null;
|
|
1237
2889
|
}
|
|
@@ -1247,6 +2899,9 @@ class NucleusList {
|
|
|
1247
2899
|
visibleActions(row) {
|
|
1248
2900
|
return this.actions().filter((entry) => !entry.visible || entry.visible(row));
|
|
1249
2901
|
}
|
|
2902
|
+
isActionLocked(entry, row) {
|
|
2903
|
+
return entry.disabled ? entry.disabled(row) : false;
|
|
2904
|
+
}
|
|
1250
2905
|
run(entry, row) {
|
|
1251
2906
|
this.action.emit({ id: entry.id, row });
|
|
1252
2907
|
}
|
|
@@ -1265,12 +2920,37 @@ class NucleusList {
|
|
|
1265
2920
|
this.toggle.emit({ key: column.key, row, value: !this.isChecked(column, row) });
|
|
1266
2921
|
}
|
|
1267
2922
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1268
|
-
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 });
|
|
1269
2924
|
}
|
|
1270
2925
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, decorators: [{
|
|
1271
2926
|
type: Component,
|
|
1272
|
-
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]=\"
|
|
1273
|
-
}], 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
|
+
}
|
|
1274
2954
|
|
|
1275
2955
|
const ORBIT = { rx: 78, ry: 196 };
|
|
1276
2956
|
class NucleusMark {
|
|
@@ -1324,6 +3004,238 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1324
3004
|
`, styles: [":host{display:block}svg{display:block}\n"] }]
|
|
1325
3005
|
}], propDecorators: { accent: [{ type: i0.Input, args: [{ isSignal: true, alias: "accent", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
1326
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
|
+
|
|
1327
3239
|
const W = 1280;
|
|
1328
3240
|
const H = 720;
|
|
1329
3241
|
const STARS = Array.from({ length: 130 }, (_, i) => {
|
|
@@ -1419,10 +3331,17 @@ const SHELL_COPY = {
|
|
|
1419
3331
|
navToggle: 'Mostrar u ocultar el menú',
|
|
1420
3332
|
appsLauncher: 'Aplicaciones',
|
|
1421
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',
|
|
1422
3344
|
settingsTitle: 'Ajustes',
|
|
1423
|
-
settingsTheme: 'Tema',
|
|
1424
|
-
settingsThemeHint: 'Elige cómo se ve la suite en este dispositivo.',
|
|
1425
|
-
themeSystem: 'Del sistema',
|
|
1426
3345
|
signOut: 'Cerrar sesión',
|
|
1427
3346
|
accountManage: 'Gestionar mi cuenta',
|
|
1428
3347
|
accountSwitch: 'Cambiar de cuenta',
|
|
@@ -1431,19 +3350,15 @@ const SHELL_COPY = {
|
|
|
1431
3350
|
accountScope: 'Mi cuenta',
|
|
1432
3351
|
hintComingSoon: 'Próximamente',
|
|
1433
3352
|
hintNotDeployed: 'Sin publicar',
|
|
1434
|
-
|
|
1435
|
-
contextTitle: 'Compañía y sede',
|
|
1436
|
-
contextHint: 'Elige dónde quieres trabajar. El menú se carga según tu rol en la sede.',
|
|
3353
|
+
roleCaption: 'Tu rol',
|
|
1437
3354
|
companyLabel: 'Compañía',
|
|
1438
3355
|
companyPlaceholder: 'Selecciona una compañía',
|
|
1439
3356
|
siteLabel: 'Sede',
|
|
1440
3357
|
sitePlaceholder: 'Selecciona una sede',
|
|
1441
3358
|
sitesLoading: 'Cargando sedes...',
|
|
1442
3359
|
sitesEmpty: 'Esta compañía no tiene sedes a tu alcance',
|
|
1443
|
-
confirmSwitch: 'Cambiar',
|
|
1444
3360
|
relationOwner: 'Propietario',
|
|
1445
3361
|
relationCollaborator: 'Colaborador',
|
|
1446
|
-
noContext: 'Sin sede seleccionada',
|
|
1447
3362
|
noCompanyTitle: 'Te falta una compañía',
|
|
1448
3363
|
noCompanyHint: 'Para trabajar en las aplicaciones necesitas al menos una compañía con una sede, ' +
|
|
1449
3364
|
'propia o donde te hayan invitado. Puedes entrar igual, pero el menú saldrá vacío.',
|
|
@@ -1452,10 +3367,17 @@ const SHELL_COPY = {
|
|
|
1452
3367
|
navToggle: 'Show or hide the menu',
|
|
1453
3368
|
appsLauncher: 'Applications',
|
|
1454
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',
|
|
1455
3380
|
settingsTitle: 'Settings',
|
|
1456
|
-
settingsTheme: 'Theme',
|
|
1457
|
-
settingsThemeHint: 'Choose how the suite looks on this device.',
|
|
1458
|
-
themeSystem: 'System',
|
|
1459
3381
|
signOut: 'Sign out',
|
|
1460
3382
|
accountManage: 'Manage my account',
|
|
1461
3383
|
accountSwitch: 'Switch account',
|
|
@@ -1464,29 +3386,29 @@ const SHELL_COPY = {
|
|
|
1464
3386
|
accountScope: 'My account',
|
|
1465
3387
|
hintComingSoon: 'Coming soon',
|
|
1466
3388
|
hintNotDeployed: 'Not deployed',
|
|
1467
|
-
|
|
1468
|
-
contextTitle: 'Company and site',
|
|
1469
|
-
contextHint: 'Choose where you want to work. The menu loads from your role at that site.',
|
|
3389
|
+
roleCaption: 'Your role',
|
|
1470
3390
|
companyLabel: 'Company',
|
|
1471
3391
|
companyPlaceholder: 'Select a company',
|
|
1472
3392
|
siteLabel: 'Site',
|
|
1473
3393
|
sitePlaceholder: 'Select a site',
|
|
1474
3394
|
sitesLoading: 'Loading sites...',
|
|
1475
3395
|
sitesEmpty: 'This company has no sites available to you',
|
|
1476
|
-
confirmSwitch: 'Switch',
|
|
1477
3396
|
relationOwner: 'Owner',
|
|
1478
3397
|
relationCollaborator: 'Collaborator',
|
|
1479
|
-
noContext: 'No site selected',
|
|
1480
3398
|
noCompanyTitle: 'You need a company',
|
|
1481
3399
|
noCompanyHint: 'To work in the applications you need at least one company with a site, your own or ' +
|
|
1482
3400
|
'one you have been invited to. You can still open them, but the menu will be empty.',
|
|
1483
3401
|
},
|
|
1484
3402
|
};
|
|
1485
3403
|
|
|
3404
|
+
const LAUNCHER_RICH_THRESHOLD = 6;
|
|
3405
|
+
const RECENT_LIMIT = 3;
|
|
3406
|
+
const FALLBACK_APP_COLOR = '#64748b';
|
|
1486
3407
|
class NucleusShell {
|
|
1487
3408
|
session = inject(SessionService);
|
|
1488
3409
|
sessionAuth = inject(NucleusSessionAuth);
|
|
1489
3410
|
themes = inject(ThemeService);
|
|
3411
|
+
recents = inject(NucleusRecentApps);
|
|
1490
3412
|
language = inject(NucleusLanguage);
|
|
1491
3413
|
page = inject(NucleusPage);
|
|
1492
3414
|
config = inject(NUCLEUS_CONFIG);
|
|
@@ -1502,6 +3424,7 @@ class NucleusShell {
|
|
|
1502
3424
|
menuSelect = output();
|
|
1503
3425
|
lang = this.language.lang;
|
|
1504
3426
|
icons = NUCLEUS_ICONS;
|
|
3427
|
+
menuPlaceholders = ['64%', '80%', '52%', '72%', '58%'];
|
|
1505
3428
|
title = computed(() => this.heading() || this.page.heading(), ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1506
3429
|
subtitle = computed(() => this.lead() || this.page.lead(), ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
1507
3430
|
home = computed(() => ({ icon: 'pi pi-home', routerLink: '/' }), ...(ngDevMode ? [{ debugName: "home" }] : /* istanbul ignore next */ []));
|
|
@@ -1511,20 +3434,22 @@ class NucleusShell {
|
|
|
1511
3434
|
})), ...(ngDevMode ? [{ debugName: "crumbs" }] : /* istanbul ignore next */ []));
|
|
1512
3435
|
copy = computed(() => SHELL_COPY[this.lang()], ...(ngDevMode ? [{ debugName: "copy" }] : /* istanbul ignore next */ []));
|
|
1513
3436
|
starfield = this.themes.starfield;
|
|
1514
|
-
themePreference = this.themes.preference;
|
|
1515
3437
|
menu = this.session.menu;
|
|
3438
|
+
collapsedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "collapsedIds" }] : /* istanbul ignore next */ []));
|
|
1516
3439
|
menuLoading = this.session.loading;
|
|
1517
3440
|
menuError = this.session.error;
|
|
1518
3441
|
launcherApps = this.session.apps;
|
|
1519
3442
|
switching = this.session.switching;
|
|
1520
|
-
|
|
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 */ []));
|
|
1521
3446
|
settingsOpen = signal(false, ...(ngDevMode ? [{ debugName: "settingsOpen" }] : /* istanbul ignore next */ []));
|
|
1522
|
-
contextOpen = signal(false, ...(ngDevMode ? [{ debugName: "contextOpen" }] : /* istanbul ignore next */ []));
|
|
1523
3447
|
companyChoice = signal(null, ...(ngDevMode ? [{ debugName: "companyChoice" }] : /* istanbul ignore next */ []));
|
|
1524
3448
|
siteChoice = signal(null, ...(ngDevMode ? [{ debugName: "siteChoice" }] : /* istanbul ignore next */ []));
|
|
1525
3449
|
siteOptions = signal([], ...(ngDevMode ? [{ debugName: "siteOptions" }] : /* istanbul ignore next */ []));
|
|
1526
3450
|
sitesLoading = signal(false, ...(ngDevMode ? [{ debugName: "sitesLoading" }] : /* istanbul ignore next */ []));
|
|
1527
3451
|
auth = this.injector.get(NucleusAuth, null);
|
|
3452
|
+
sitesCompanyId = null;
|
|
1528
3453
|
identity = computed(() => {
|
|
1529
3454
|
const me = this.session.me();
|
|
1530
3455
|
if (me) {
|
|
@@ -1553,26 +3478,102 @@ class NucleusShell {
|
|
|
1553
3478
|
tag: company.relation === 'owner' ? t.relationOwner : t.relationCollaborator,
|
|
1554
3479
|
}));
|
|
1555
3480
|
}, ...(ngDevMode ? [{ debugName: "companyChoices" }] : /* istanbul ignore next */ []));
|
|
1556
|
-
contextCaption = computed(() => {
|
|
1557
|
-
const role = this.session.roleName();
|
|
1558
|
-
const site = this.session.siteName();
|
|
1559
|
-
const company = this.session.companyName();
|
|
1560
|
-
if (role && site)
|
|
1561
|
-
return company ? `${company} · ${site}` : site;
|
|
1562
|
-
if (this.session.scope() === 'account')
|
|
1563
|
-
return this.copy().accountScope;
|
|
1564
|
-
return this.copy().noContext;
|
|
1565
|
-
}, ...(ngDevMode ? [{ debugName: "contextCaption" }] : /* istanbul ignore next */ []));
|
|
1566
3481
|
roleName = this.session.roleName;
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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 */ []));
|
|
1576
3577
|
loadSession = effect(() => {
|
|
1577
3578
|
const language = this.lang();
|
|
1578
3579
|
const user = this.auth?.user() ?? null;
|
|
@@ -1580,49 +3581,97 @@ class NucleusShell {
|
|
|
1580
3581
|
return;
|
|
1581
3582
|
untracked(() => void this.session.load(language));
|
|
1582
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
|
+
}
|
|
1583
3595
|
setLang(lang) {
|
|
1584
3596
|
this.language.setLang(lang);
|
|
1585
3597
|
}
|
|
1586
3598
|
toggleSidebar() {
|
|
1587
3599
|
this.sidebarOpen.update((open) => !open);
|
|
1588
3600
|
}
|
|
1589
|
-
selectTheme(preference) {
|
|
1590
|
-
if (preference)
|
|
1591
|
-
this.themes.setPreference(preference);
|
|
1592
|
-
}
|
|
1593
3601
|
selectNav(id) {
|
|
1594
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);
|
|
1595
3610
|
}
|
|
1596
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;
|
|
1597
3623
|
this.menuSelect.emit(node);
|
|
1598
|
-
|
|
1599
|
-
|
|
3624
|
+
void this.router.navigateByUrl(node.route);
|
|
3625
|
+
this.closeSidebarOnNarrow();
|
|
3626
|
+
}
|
|
3627
|
+
closeSidebarOnNarrow() {
|
|
3628
|
+
if (this.narrow())
|
|
3629
|
+
this.sidebarOpen.set(false);
|
|
1600
3630
|
}
|
|
1601
3631
|
isSelected(node) {
|
|
1602
3632
|
return this.selectedMenuId() === node.id;
|
|
1603
3633
|
}
|
|
1604
|
-
async openContext() {
|
|
1605
|
-
const companyId = this.session.companyId() ?? this.companyChoices()[0]?.id ?? null;
|
|
1606
|
-
this.companyChoice.set(companyId);
|
|
1607
|
-
this.siteChoice.set(this.session.siteId());
|
|
1608
|
-
this.contextOpen.set(true);
|
|
1609
|
-
if (companyId)
|
|
1610
|
-
await this.loadSites(companyId);
|
|
1611
|
-
}
|
|
1612
3634
|
async onCompanyChange(companyId) {
|
|
3635
|
+
if (!companyId || companyId === this.session.companyId())
|
|
3636
|
+
return;
|
|
1613
3637
|
this.companyChoice.set(companyId);
|
|
1614
3638
|
this.siteChoice.set(null);
|
|
1615
|
-
this.
|
|
1616
|
-
|
|
1617
|
-
|
|
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);
|
|
1618
3644
|
}
|
|
1619
|
-
async
|
|
3645
|
+
async onSiteChange(siteId) {
|
|
1620
3646
|
const companyId = this.companyChoice();
|
|
1621
|
-
|
|
1622
|
-
if (!companyId || !siteId)
|
|
3647
|
+
if (!siteId || !companyId || siteId === this.session.siteId())
|
|
1623
3648
|
return;
|
|
3649
|
+
this.siteChoice.set(siteId);
|
|
1624
3650
|
await this.session.switchContext(companyId, siteId);
|
|
1625
|
-
|
|
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();
|
|
1626
3675
|
}
|
|
1627
3676
|
canOpen(app) {
|
|
1628
3677
|
return app.status === 1 && !!app.url;
|
|
@@ -1630,6 +3679,7 @@ class NucleusShell {
|
|
|
1630
3679
|
launch(app) {
|
|
1631
3680
|
if (!this.canOpen(app))
|
|
1632
3681
|
return;
|
|
3682
|
+
this.recents.remember(app.id);
|
|
1633
3683
|
window.location.href = app.url;
|
|
1634
3684
|
}
|
|
1635
3685
|
appInitials(app) {
|
|
@@ -1654,24 +3704,27 @@ class NucleusShell {
|
|
|
1654
3704
|
window.location.href = nucleusLogoutUrl(landing);
|
|
1655
3705
|
}
|
|
1656
3706
|
async loadSites(companyId) {
|
|
3707
|
+
if (this.sitesCompanyId === companyId)
|
|
3708
|
+
return this.siteOptions();
|
|
3709
|
+
this.sitesCompanyId = companyId;
|
|
1657
3710
|
this.sitesLoading.set(true);
|
|
1658
3711
|
try {
|
|
1659
3712
|
const sites = await this.session.accessibleSites(companyId);
|
|
1660
3713
|
this.siteOptions.set(sites);
|
|
1661
|
-
|
|
1662
|
-
this.siteChoice.set(sites[0]?.id ?? null);
|
|
1663
|
-
}
|
|
3714
|
+
return sites;
|
|
1664
3715
|
}
|
|
1665
3716
|
catch (error) {
|
|
3717
|
+
this.sitesCompanyId = null;
|
|
1666
3718
|
this.siteOptions.set([]);
|
|
1667
3719
|
console.error('[shell] no se pudieron cargar las sedes', error);
|
|
3720
|
+
return [];
|
|
1668
3721
|
}
|
|
1669
3722
|
finally {
|
|
1670
3723
|
this.sitesLoading.set(false);
|
|
1671
3724
|
}
|
|
1672
3725
|
}
|
|
1673
3726
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1674
|
-
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$2.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$1.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: i3.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$1.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 });
|
|
1675
3728
|
}
|
|
1676
3729
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, decorators: [{
|
|
1677
3730
|
type: Component,
|
|
@@ -1682,17 +3735,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1682
3735
|
ConfirmDialogModule,
|
|
1683
3736
|
ToastModule,
|
|
1684
3737
|
DrawerModule,
|
|
1685
|
-
ListboxModule,
|
|
1686
3738
|
PopoverModule,
|
|
1687
3739
|
SelectModule,
|
|
3740
|
+
SkeletonModule,
|
|
1688
3741
|
TagModule,
|
|
1689
3742
|
TooltipModule,
|
|
1690
3743
|
FormsModule,
|
|
3744
|
+
NucleusAppearance,
|
|
1691
3745
|
NucleusLordicon,
|
|
3746
|
+
NucleusMenu,
|
|
1692
3747
|
NucleusMark,
|
|
1693
3748
|
NucleusStarfield,
|
|
1694
|
-
], 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"] }]
|
|
1695
|
-
}], 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"] }] } });
|
|
1696
3751
|
function initialsOf(source) {
|
|
1697
3752
|
const parts = source.trim().split(/\s+/).filter(Boolean);
|
|
1698
3753
|
if (!parts.length)
|
|
@@ -1706,5 +3761,5 @@ function initialsOf(source) {
|
|
|
1706
3761
|
* Generated bundle index. Do not edit.
|
|
1707
3762
|
*/
|
|
1708
3763
|
|
|
1709
|
-
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 };
|
|
1710
3765
|
//# sourceMappingURL=nucleus-suite-pe-core.mjs.map
|