@esfaenza/flow-builder 20.3.5 → 20.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, inject, signal, computed, Injectable, DestroyRef, input, output, viewChild, ChangeDetectionStrategy, Component, effect, untracked, ElementRef, Directive } from '@angular/core';
3
+ import { InjectionToken, inject, signal, effect, computed, Injectable, DestroyRef, input, output, viewChild, ChangeDetectionStrategy, Component, untracked, ElementRef, Directive } from '@angular/core';
4
4
  import { firstValueFrom } from 'rxjs';
5
5
  import dagre from 'dagre';
6
6
  import * as i1 from '@foblex/flow';
@@ -800,7 +800,7 @@ function elementIcon(type, label, variant) {
800
800
  if (icon) {
801
801
  return icon;
802
802
  }
803
- const fallback = (label || type).trim().charAt(0).toUpperCase();
803
+ const fallback = (label || type)?.trim().charAt(0).toUpperCase();
804
804
  return fallback || '•';
805
805
  }
806
806
 
@@ -865,7 +865,7 @@ function isValidFlowName(name) {
865
865
  * `currentName` esclude se stesso dal controllo, per non segnalare un elemento in modifica.
866
866
  */
867
867
  function checkFlowName(name, usedNames, currentName) {
868
- const trimmed = (name ?? '').trim();
868
+ const trimmed = (name ?? '')?.trim();
869
869
  if (!trimmed) {
870
870
  return { isValid: false, problem: 'empty', message: 'Il nome tecnico e’ obbligatorio.' };
871
871
  }
@@ -896,7 +896,7 @@ function checkFlowName(name, usedNames, currentName) {
896
896
  * Va lasciato modificabile finche' l'elemento e' nuovo.
897
897
  */
898
898
  function slugifyFlowName(label) {
899
- const source = (label ?? '').trim();
899
+ const source = (label ?? '')?.trim();
900
900
  if (!source) {
901
901
  return '';
902
902
  }
@@ -938,7 +938,7 @@ function uniqueFlowName(base, usedNames) {
938
938
  * `Cliente.Email` → `Cliente`; `$Record.Stato` → `$Record`; `Importo` → `Importo`.
939
939
  */
940
940
  function referenceRoot(reference) {
941
- const value = (reference ?? '').trim();
941
+ const value = (reference ?? '')?.trim();
942
942
  const dot = value.indexOf('.');
943
943
  return dot < 0 ? value : value.slice(0, dot);
944
944
  }
@@ -960,7 +960,7 @@ function isCustomConditionLogic(logic) {
960
960
  if (!logic) {
961
961
  return false;
962
962
  }
963
- const normalized = logic.trim().toLowerCase();
963
+ const normalized = logic?.trim().toLowerCase();
964
964
  return normalized !== 'and' && normalized !== 'or' && normalized !== 'formula';
965
965
  }
966
966
  /**
@@ -1047,7 +1047,7 @@ function moveCondition(holder, from, to) {
1047
1047
  * del backend (`CONDITION_LOGIC_INVALID`).
1048
1048
  */
1049
1049
  function checkConditionLogic(logic, conditionCount) {
1050
- const trimmed = logic.trim();
1050
+ const trimmed = logic?.trim();
1051
1051
  if (!trimmed) {
1052
1052
  return null;
1053
1053
  }
@@ -1154,7 +1154,7 @@ function areTypesComparable(left, right) {
1154
1154
  * scrive — e la traduce, invece di lasciare che `parseFloat` tronchi silenziosamente a `1`.
1155
1155
  */
1156
1156
  function parseInvariantNumber(raw) {
1157
- const trimmed = raw.trim();
1157
+ const trimmed = raw?.trim();
1158
1158
  if (!trimmed) {
1159
1159
  return undefined;
1160
1160
  }
@@ -1209,7 +1209,7 @@ function stageStepOutputReferenced(reference, steps) {
1209
1209
  if (!reference) {
1210
1210
  return undefined;
1211
1211
  }
1212
- const root = referenceRoot(reference.trim());
1212
+ const root = referenceRoot(reference?.trim());
1213
1213
  return steps.find((step) => !!step.name && step.name === root)?.name;
1214
1214
  }
1215
1215
  /**
@@ -1264,7 +1264,7 @@ function severityBucket(severity) {
1264
1264
  if (!severity) {
1265
1265
  return 'Info';
1266
1266
  }
1267
- return BUCKET_BY_NAME.get(severity.trim().toLowerCase()) ?? 'Info';
1267
+ return BUCKET_BY_NAME.get(severity?.trim().toLowerCase()) ?? 'Info';
1268
1268
  }
1269
1269
  /** L'ordine in cui si presentano: prima cio' che blocca l'attivazione. */
1270
1270
  const SEVERITY_BUCKETS = ['Error', 'Warning', 'Info'];
@@ -1285,6 +1285,260 @@ const SEVERITY_ICON = {
1285
1285
  Info: 'i',
1286
1286
  };
1287
1287
 
1288
+ /**
1289
+ * Navigazione dei percorsi puntati — FRONTEND.md §4.4, §4.7, §4.7.1, §5.2, §5.7.
1290
+ *
1291
+ * La specifica e' cambiata su questo punto: i percorsi con il punto **si verificano**. Un percorso
1292
+ * di relazione (`Cliente.Citta`) si naviga un segmento alla volta e l'ultimo si verifica come un
1293
+ * campo semplice; dentro un'istanza di classe i membri annidati (`Richiesta.Cliente.Nome`) si
1294
+ * verificano **fino in fondo**, seguendo `objectType` di classe in classe e passando ai campi
1295
+ * dello schema quando il membro porta un record. Dove la catena si interrompe il rilievo e'
1296
+ * l'avviso `PATH_NOT_VERIFIABLE`, che significa "non lo so" e non "sbagliato".
1297
+ *
1298
+ * Perche' sta qui e non in tre pickers: la catena e' la stessa per campi e membri — cambiano solo
1299
+ * la primitiva che descrive la tappa (`listFields` contro `describeStructure`) e le parole
1300
+ * dell'avviso. Tenerla in un posto solo e' anche il modo di non far divergere l'editor dal
1301
+ * validatore, che naviga la stessa catena.
1302
+ *
1303
+ * Le due cose da non confondere:
1304
+ *
1305
+ * - **`objectType` di un membro** porta il nome di una classe se il membro e' `Structure`, il nome
1306
+ * di un'**entita' dello schema** se il membro e' `Object` — e da lì in poi si continua con i
1307
+ * campi, non con i membri (§4.7.1). Un `Enum` non e' un contenitore: `objectType` lì e' un tipo
1308
+ * di enumerazione e il percorso finisce.
1309
+ * - **scrivibile** non vuol dire la stessa cosa nei due mondi: un membro porta il proprio
1310
+ * `isWritable`, un campo no — fuori da un Create o da un Update il contesto non c'e', quindi
1311
+ * basta che sia `isCreateable` **oppure** `isUpdateable`, e un campo che non e' ne' l'uno ne'
1312
+ * l'altro e' `TARGET_NOT_WRITABLE` in qualunque contesto (§5.7).
1313
+ */
1314
+ /** Il `dataType` porta un contenitore solo se e' `Object` o `Structure`, e non su una collection. */
1315
+ function containerOf(dataType, objectType, isCollection) {
1316
+ if (isCollection || !objectType) {
1317
+ return undefined;
1318
+ }
1319
+ if (dataType === 'Structure') {
1320
+ return { kind: 'structure', name: objectType };
1321
+ }
1322
+ if (dataType === 'Object') {
1323
+ return { kind: 'object', name: objectType };
1324
+ }
1325
+ // `Enum` porta un tipo di enumerazione in `objectType`: non e' una tappa navigabile.
1326
+ return undefined;
1327
+ }
1328
+ function fieldEntry(field) {
1329
+ return {
1330
+ name: field.name,
1331
+ label: field.label ?? null,
1332
+ dataType: field.dataType,
1333
+ objectType: field.objectType ?? null,
1334
+ // §5.7 — fuori da un Create o da un Update il contesto non c'e': basta scrivibile in un modo.
1335
+ // Ne' createable ne' updateable = non scrivibile in nessun contesto (`TARGET_NOT_WRITABLE`).
1336
+ isWritable: field.isCreateable !== false || field.isUpdateable !== false,
1337
+ next: containerOf(field.dataType, field.objectType, false),
1338
+ description: field.isRequired ? 'obbligatorio' : null,
1339
+ };
1340
+ }
1341
+ function memberEntry(member) {
1342
+ return {
1343
+ name: member.name,
1344
+ label: member.label ?? null,
1345
+ description: member.description ?? null,
1346
+ dataType: member.dataType,
1347
+ objectType: member.objectType ?? null,
1348
+ isCollection: member.isCollection ?? false,
1349
+ isWritable: member.isWritable !== false,
1350
+ next: containerOf(member.dataType, member.objectType, member.isCollection),
1351
+ };
1352
+ }
1353
+ /**
1354
+ * Descrive una tappa. `usage` vale solo per le entita' e solo dove serve: i segmenti **intermedi**
1355
+ * si chiedono sempre con `any`, perche' l'uso riguarda l'ultimo — un filtro su `Cliente.Citta`
1356
+ * esige che `Citta` sia filtrabile, non che lo sia la relazione (§4.4).
1357
+ */
1358
+ async function loadPathLevel(catalog, container, usage = 'any') {
1359
+ if (!container.name) {
1360
+ return { container, status: 'unknown-catalog', entries: [] };
1361
+ }
1362
+ if (container.kind === 'structure') {
1363
+ // `describeStructure` non rifiuta: gli esiti sono dati, non errori (§4.7.1).
1364
+ const described = await catalog.describeStructure(container.name);
1365
+ return {
1366
+ container,
1367
+ label: described.label ?? null,
1368
+ status: described.status === 'unknown-class' ? 'unknown-container' : described.status,
1369
+ entries: described.members.map(memberEntry),
1370
+ };
1371
+ }
1372
+ try {
1373
+ const fields = (await catalog.listFields(container.name, usage)) ?? [];
1374
+ // Lista vuota = "non lo so", non "nessun campo": lì non si accusa nessuno (§7).
1375
+ return { container, status: fields.length ? 'declared' : 'undeclared', entries: fields.map(fieldEntry) };
1376
+ }
1377
+ catch {
1378
+ return { container, status: 'unknown-catalog', entries: [] };
1379
+ }
1380
+ }
1381
+ /** Da uno stato di tappa non autorevole al motivo per cui il percorso non e' verificabile. */
1382
+ function reasonOf(status) {
1383
+ return status === 'declared' ? 'unknown-segment' : status;
1384
+ }
1385
+ /**
1386
+ * Naviga `path` a partire da `root`, un segmento alla volta. Non rifiuta mai: un catalogo assente
1387
+ * e' un esito (`unverifiable`), non un errore da gestire in ogni chiamante.
1388
+ */
1389
+ async function resolvePath(catalog, root, path, options) {
1390
+ const trimmed = (path ?? '')?.trim();
1391
+ const segments = trimmed.split('.').map((segment) => segment?.trim());
1392
+ const levels = [];
1393
+ const resolved = [];
1394
+ const base = { root, path: trimmed, segments, levels, resolved };
1395
+ let container = root;
1396
+ for (let index = 0; index < segments.length; index += 1) {
1397
+ const segment = segments[index];
1398
+ const isLast = index === segments.length - 1;
1399
+ const prefix = segments.slice(0, index).join('.');
1400
+ const level = await loadPathLevel(catalog, container, isLast ? (options?.usage ?? 'any') : 'any');
1401
+ levels.push(level);
1402
+ if (isLast) {
1403
+ const tail = { level, prefix, segment };
1404
+ if (!segment) {
1405
+ // Percorso vuoto, o appena aperto col punto: non c'e' niente da verificare, c'e' da proporre.
1406
+ return { ...base, status: 'empty', tail };
1407
+ }
1408
+ if (level.status !== 'declared') {
1409
+ return {
1410
+ ...base,
1411
+ status: 'unverifiable',
1412
+ reason: reasonOf(level.status),
1413
+ stoppedAt: { index, segment },
1414
+ tail,
1415
+ };
1416
+ }
1417
+ const entry = level.entries.find((candidate) => candidate.name === segment);
1418
+ if (!entry) {
1419
+ return { ...base, status: 'unknown', reason: 'unknown-segment', stoppedAt: { index, segment }, tail };
1420
+ }
1421
+ resolved.push(entry);
1422
+ return { ...base, status: 'resolved', last: entry, tail };
1423
+ }
1424
+ if (!segment) {
1425
+ // Un segmento intermedio vuoto (`Cliente..Citta`) non designa niente e non si propone.
1426
+ return { ...base, status: 'empty' };
1427
+ }
1428
+ if (level.status !== 'declared') {
1429
+ return { ...base, status: 'unverifiable', reason: reasonOf(level.status), stoppedAt: { index, segment } };
1430
+ }
1431
+ const entry = level.entries.find((candidate) => candidate.name === segment);
1432
+ if (!entry) {
1433
+ return { ...base, status: 'unknown', reason: 'unknown-segment', stoppedAt: { index, segment } };
1434
+ }
1435
+ resolved.push(entry);
1436
+ const next = entry.isCollection ? undefined : entry.next;
1437
+ if (!next) {
1438
+ // Il resto del percorso resta non verificato: e' un "non lo so", non un errore (§4.4).
1439
+ return {
1440
+ ...base,
1441
+ status: 'unverifiable',
1442
+ reason: entry.isCollection ? 'collection' : 'no-object-type',
1443
+ stoppedAt: { index: index + 1, segment: segments[index + 1] },
1444
+ };
1445
+ }
1446
+ container = next;
1447
+ }
1448
+ // Irraggiungibile: `split` restituisce sempre almeno un segmento.
1449
+ return { ...base, status: 'empty' };
1450
+ }
1451
+ /** Come chiamare una tappa in un messaggio: la classe ha una label, l'entita' il proprio nome. */
1452
+ function pathContainerLabel(level) {
1453
+ if (!level) {
1454
+ return '';
1455
+ }
1456
+ return level.label || level.container.name;
1457
+ }
1458
+ /**
1459
+ * Il testo dell'avviso `PATH_NOT_VERIFIABLE`, con il **motivo**: "non verificabile" senza dire
1460
+ * perche' e' indistinguibile da un difetto dell'editor.
1461
+ */
1462
+ function pathNotVerifiableMessage(resolution) {
1463
+ const level = resolution.levels[resolution.levels.length - 1];
1464
+ const stopped = resolution.stoppedAt?.segment;
1465
+ const before = resolution.resolved[resolution.resolved.length - 1]?.name;
1466
+ const tail = stopped ? `«${stopped}»` : 'il resto del percorso';
1467
+ switch (resolution.reason) {
1468
+ case 'collection':
1469
+ return `«${before}» e’ una collection: dentro una collection il percorso non si verifica, ${tail} resta non verificato (PATH_NOT_VERIFIABLE).`;
1470
+ case 'no-object-type':
1471
+ return `«${before}» non dichiara dove il percorso continua: ${tail} resta non verificato (PATH_NOT_VERIFIABLE).`;
1472
+ case 'unknown-container':
1473
+ return `La classe ${level?.container.name} non e’ registrata: correggi la classe, non il percorso (STRUCTURE_TYPE_UNKNOWN).`;
1474
+ case 'undeclared':
1475
+ return level?.container.kind === 'structure'
1476
+ ? `${pathContainerLabel(level)} non dichiara i suoi membri: ${tail} non e’ verificabile (PATH_NOT_VERIFIABLE).`
1477
+ : `Il catalogo dei campi di ${pathContainerLabel(level)} non e’ popolato: ${tail} non e’ verificabile (PATH_NOT_VERIFIABLE).`;
1478
+ case 'unknown-catalog':
1479
+ return `Catalogo non disponibile: ${tail} non e’ verificabile (PATH_NOT_VERIFIABLE).`;
1480
+ default:
1481
+ return `${tail} non e’ verificabile (PATH_NOT_VERIFIABLE).`;
1482
+ }
1483
+ }
1484
+ /** I nomi proponibili nella tappa dove un segmento non e' stato trovato: il messaggio li elenca. */
1485
+ function pathAvailableNames(resolution) {
1486
+ const level = resolution.levels[resolution.levels.length - 1];
1487
+ return (level?.entries ?? []).map((entry) => entry.name);
1488
+ }
1489
+ /**
1490
+ * La riga di descrizione di un segmento proponibile. `showWritability` la chiede chi sta
1491
+ * compilando una **destinazione**: in un filtro "sola lettura" sarebbe rumore, in un campo di
1492
+ * destinazione e' l'informazione che evita `TARGET_NOT_WRITABLE`.
1493
+ */
1494
+ function describePathEntry(entry, options) {
1495
+ const parts = [];
1496
+ if (entry.label && entry.label !== entry.name) {
1497
+ parts.push(entry.label);
1498
+ }
1499
+ if (entry.dataType) {
1500
+ const type = entry.isCollection ? `${entry.dataType}[]` : entry.dataType;
1501
+ parts.push(entry.objectType ? `${type} · ${entry.objectType}` : type);
1502
+ }
1503
+ if (options?.showWritability && !entry.isWritable) {
1504
+ parts.push('sola lettura');
1505
+ }
1506
+ if (entry.description) {
1507
+ parts.push(entry.description);
1508
+ }
1509
+ return parts.join(' · ');
1510
+ }
1511
+ /**
1512
+ * La navigazione come coppia di signal, per i pickers. Da chiamare in un **contesto di
1513
+ * iniezione** (il costruttore del componente), perche' dentro crea un `effect`.
1514
+ *
1515
+ * Il token di sequenza non e' cosmetico: le tappe si caricano una alla volta e una risposta
1516
+ * superata che arriva dopo mostrerebbe l'avviso di un percorso che l'utente ha già cambiato.
1517
+ */
1518
+ function navigatePath(catalog, request) {
1519
+ const resolution = signal(null, ...(ngDevMode ? [{ debugName: "resolution" }] : []));
1520
+ const isPending = signal(false, ...(ngDevMode ? [{ debugName: "isPending" }] : []));
1521
+ let sequence = 0;
1522
+ effect(() => {
1523
+ const current = request();
1524
+ const token = ++sequence;
1525
+ if (!current?.root?.name) {
1526
+ resolution.set(null);
1527
+ isPending.set(false);
1528
+ return;
1529
+ }
1530
+ isPending.set(true);
1531
+ void resolvePath(catalog, current.root, current.path, { usage: current.usage }).then((result) => {
1532
+ if (token !== sequence) {
1533
+ return;
1534
+ }
1535
+ resolution.set(result);
1536
+ isPending.set(false);
1537
+ });
1538
+ });
1539
+ return { resolution: resolution.asReadonly(), isPending: isPending.asReadonly() };
1540
+ }
1541
+
1288
1542
  /**
1289
1543
  * Lo store del documento in lavorazione.
1290
1544
  *
@@ -3287,7 +3541,7 @@ class ElementPaletteComponent {
3287
3541
  return items;
3288
3542
  }, ...(ngDevMode ? [{ debugName: "items" }] : []));
3289
3543
  groups = computed(() => {
3290
- const needle = this.filter().trim().toLowerCase();
3544
+ const needle = this.filter()?.trim().toLowerCase();
3291
3545
  const groups = [];
3292
3546
  for (const item of this.items()) {
3293
3547
  // La ricerca guarda anche l'etichetta del tipo: cercando «ordina o filtra» si trovano
@@ -3375,7 +3629,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3375
3629
  }], propDecorators: { processType: [{ type: i0.Input, args: [{ isSignal: true, alias: "processType", required: false }] }], elementPicked: [{ type: i0.Output, args: ["elementPicked"] }] } });
3376
3630
 
3377
3631
  /**
3378
- * Selettore di riferimento — FRONTEND.md §4.1, §4.5, §6.4, §13.3.
3632
+ * Selettore di riferimento — FRONTEND.md §4.1, §4.5, §4.7, §5.2, §6.4, §13.3.
3379
3633
  *
3380
3634
  * Un riferimento e' testo libero, e un nome sbagliato e' indistinguibile da uno giusto se
3381
3635
  * non si conosce l'insieme dei nomi definiti. Questo componente chiede l'insieme a
@@ -3386,18 +3640,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3386
3640
  * pur non essendo dichiarati in nessuna collection di risorse: la primitiva li restituisce
3387
3641
  * già, ed e' il motivo per cui non si ricostruisce l'elenco lato client.
3388
3642
  *
3389
- * Resta possibile scrivere a mano: i percorsi di relazione e i campi di un record non sono
3390
- * enumerabili. Gli scope del sistema ospite (`$User.X`), invece, sono verificabili **quando
3391
- * l'host li dichiara**: se `globalVariables` porta almeno un percorso di quello scope, un
3392
- * percorso che non c'e' e' `GLOBAL_UNKNOWN`; se non ne porta nessuno, lo scope resta non
3393
- * verificabileche significa "non lo so", non "non esiste" (§4.1).
3394
- *
3395
- * I **membri di una classe** sono l'eccezione ai percorsi non enumerabili (§4.7): quando la
3396
- * radice e' una risorsa `Structure` i membri arrivano da
3397
- * `GET /catalog/structures/{className}/members`, quindi si propongono come `Richiesta.Ragione`
3398
- * e il **primo** segmento si verifica un membro che non c'e' e'
3399
- * `STRUCTURE_MEMBER_UNKNOWN`, e in un campo di destinazione un membro calcolato e'
3400
- * `TARGET_NOT_WRITABLE`. Oltre il primo segmento resta valida la regola generale: nessuno lo sa.
3643
+ * I **percorsi navigati** non sono piu' tutti uguali. Quando la radice e' una risorsa dichiarata
3644
+ * di cui si conosce la forma — una `Structure` con la sua classe, un record con la sua entita' — il
3645
+ * percorso si naviga **fino in fondo** (§4.4, §4.7.1): i segmenti si propongono un livello alla
3646
+ * volta, un nome che non c'e' e' `STRUCTURE_MEMBER_UNKNOWN` o `FIELD_UNKNOWN`, e in un campo di
3647
+ * destinazione un membro calcolato o un campo ne' `createable` ne' `updateable` e'
3648
+ * `TARGET_NOT_WRITABLE` (§5.7). Dove la catena si interrompe l'avviso e' `PATH_NOT_VERIFIABLE`.
3649
+ *
3650
+ * Restano non verificabili due cose, e per motivi diversi: i percorsi radicati sul **risultato
3651
+ * automatico** di un elemento (`Leggi_Contatti.Owner.Name`), di cui il documento non dichiara la
3652
+ * forma, e gli scope del sistema ospite che non dichiarano i propri percorsi (`$User.X`) — lì
3653
+ * "non lo so" non e' "non esiste" (§4.1).
3401
3654
  */
3402
3655
  class ReferencePickerComponent {
3403
3656
  api = inject(FlowBuilderApi);
@@ -3472,62 +3725,76 @@ class ReferencePickerComponent {
3472
3725
  })
3473
3726
  .finally(() => this.isLoading.set(false));
3474
3727
  });
3475
- // I membri della classe da cui si sta navigando: caricati solo quando servono, cioe' quando
3476
- // la radice del valore e' davvero una `Structure` (§4.7).
3477
- effect(() => {
3478
- const className = this.structureRoot()?.objectType;
3479
- if (!className) {
3480
- this.members.set([]);
3481
- return;
3482
- }
3483
- // §4.7.1 — solo un elenco `declared` autorizza a dire che un membro non esiste: membri non
3484
- // dichiarati, classe non registrata e catalogo assente danno tutti e tre nessuna proposta e
3485
- // nessuna accusa, ma per la classe non registrata l'errore e' già sulla risorsa.
3486
- void this.catalog
3487
- .describeStructure(className)
3488
- .then((result) => this.members.set(result.status === 'declared' ? result.members : []));
3489
- });
3490
3728
  }
3491
3729
  /**
3492
- * §4.7 — la risorsa `Structure` da cui il valore corrente sta navigando, se c'e'. È quella
3493
- * che rende i membri proponibili: senza classe non c'e' catalogo da interrogare.
3730
+ * La risorsa da cui il valore corrente sta navigando, quando la sua forma e' **dichiarata**: una
3731
+ * `Structure` con la classe, un record con l'entita'. È cio' che rende il percorso verificabile.
3732
+ *
3733
+ * Un output automatico e' escluso di proposito: la primitiva ne dichiara il tipo, ma la
3734
+ * specifica tiene i percorsi che ne partono fuori dalla validazione (§7, ultimo capoverso), e
3735
+ * l'editor non deve accusare cio' che il backend non verifica.
3494
3736
  */
3495
- structureRoot = computed(() => {
3496
- const typed = (this.query().trim() || this.value() || '').trim();
3497
- const root = typed ? referenceRoot(typed) : '';
3737
+ navigableRoot = computed(() => {
3738
+ const root = referenceRoot((this.value() ?? '')?.trim());
3498
3739
  if (!root) {
3499
3740
  return undefined;
3500
3741
  }
3501
- return this.references().find((reference) => reference.name === root && this.dictionaries.isStructure(reference.dataType) && !!reference.objectType);
3502
- }, ...(ngDevMode ? [{ debugName: "structureRoot" }] : []));
3503
- members = signal([], ...(ngDevMode ? [{ debugName: "members" }] : []));
3504
- /** I membri come riferimenti navigati: `Richiesta.Ragione`, col tipo del membro. */
3505
- memberReferences = computed(() => {
3506
- const root = this.structureRoot();
3507
- if (!root) {
3742
+ const reference = this.references().find((candidate) => candidate.name === root);
3743
+ if (!reference || reference.kind === 'ElementOutput' || !reference.objectType || reference.isCollection) {
3744
+ return undefined;
3745
+ }
3746
+ if (this.dictionaries.isStructure(reference.dataType)) {
3747
+ return { reference, container: { kind: 'structure', name: reference.objectType } };
3748
+ }
3749
+ return reference.dataType === 'Object'
3750
+ ? { reference, container: { kind: 'object', name: reference.objectType } }
3751
+ : undefined;
3752
+ }, ...(ngDevMode ? [{ debugName: "navigableRoot" }] : []));
3753
+ /** Il percorso dopo la radice: `Cliente.Email` di `Richiesta.Cliente.Email`. */
3754
+ navigatedPath = computed(() => {
3755
+ const root = this.navigableRoot()?.reference.name;
3756
+ const value = (this.value() ?? '')?.trim();
3757
+ return root && value.length > root.length ? value.slice(root.length + 1) : '';
3758
+ }, ...(ngDevMode ? [{ debugName: "navigatedPath" }] : []));
3759
+ navigation = navigatePath(this.catalog, () => {
3760
+ const root = this.navigableRoot();
3761
+ return root ? { root: root.container, path: this.navigatedPath() } : null;
3762
+ });
3763
+ resolution = this.navigation.resolution;
3764
+ /** La tappa in cui si sta scrivendo: da qui vengono le proposte dei segmenti. */
3765
+ tail = computed(() => this.resolution()?.tail, ...(ngDevMode ? [{ debugName: "tail" }] : []));
3766
+ /**
3767
+ * I segmenti come riferimenti navigati: `Richiesta.Cliente.Email`, col tipo del segmento. Un
3768
+ * segmento di sola lettura non si propone in un campo di destinazione, a meno che da lì il
3769
+ * percorso continui: `Richiesta.Cliente` non si assegna, ma `Richiesta.Cliente.Email` sì (§5.7).
3770
+ */
3771
+ pathReferences = computed(() => {
3772
+ const root = this.navigableRoot();
3773
+ const tail = this.tail();
3774
+ if (!root || !tail) {
3508
3775
  return [];
3509
3776
  }
3510
- return this.members()
3511
- // In un campo di destinazione un membro calcolato non si propone: assegnarlo e'
3512
- // `TARGET_NOT_WRITABLE`, e proporlo sarebbe un invito a sbagliare (§4.7).
3513
- .filter((member) => !this.writableOnly() || member.isWritable !== false)
3514
- .map((member) => ({
3515
- name: `${root.name}.${member.name}`,
3516
- label: member.label ?? null,
3517
- kind: 'StructureMember',
3518
- dataType: member.dataType ?? null,
3519
- isCollection: member.isCollection ?? false,
3520
- objectType: member.objectType ?? null,
3521
- isWritable: member.isWritable !== false,
3522
- description: member.isWritable === false ? 'sola lettura' : (member.description ?? null),
3777
+ const prefix = tail.prefix ? `${root.reference.name}.${tail.prefix}` : root.reference.name;
3778
+ const kind = tail.level.container.kind === 'object' ? 'RecordField' : 'StructureMember';
3779
+ return tail.level.entries
3780
+ .filter((entry) => !this.writableOnly() || entry.isWritable || !!entry.next)
3781
+ .map((entry) => ({
3782
+ name: `${prefix}.${entry.name}`,
3783
+ label: entry.label ?? null,
3784
+ kind,
3785
+ dataType: entry.dataType ?? null,
3786
+ isCollection: entry.isCollection ?? false,
3787
+ objectType: entry.objectType ?? null,
3788
+ isWritable: entry.isWritable,
3789
+ description: entry.isWritable ? (entry.description ?? null) : 'sola lettura',
3523
3790
  }));
3524
- }, ...(ngDevMode ? [{ debugName: "memberReferences" }] : []));
3791
+ }, ...(ngDevMode ? [{ debugName: "pathReferences" }] : []));
3525
3792
  options = computed(() => {
3526
- const needle = this.query().trim().toLowerCase();
3793
+ const needle = this.query()?.trim().toLowerCase();
3527
3794
  // Le globali assegnabili — `$Flow.CurrentStage`, `$Flow.ActiveStages` e quelle che l'host
3528
3795
  // dichiara scrivibili — le comprende già `POST /flows/references/writable`: aggiungerle
3529
3796
  // qui le duplicherebbe (§5.2, §6.4).
3530
- const all = [...this.references(), ...this.memberReferences()];
3797
+ const all = [...this.references(), ...this.pathReferences()];
3531
3798
  if (!needle) {
3532
3799
  return all;
3533
3800
  }
@@ -3569,6 +3836,8 @@ class ReferencePickerComponent {
3569
3836
  return 'Output di elementi';
3570
3837
  case 'StructureMember':
3571
3838
  return 'Membri della classe';
3839
+ case 'RecordField':
3840
+ return 'Campi del record';
3572
3841
  case 'Global':
3573
3842
  return 'Variabili globali';
3574
3843
  default:
@@ -3578,21 +3847,21 @@ class ReferencePickerComponent {
3578
3847
  /**
3579
3848
  * Stato del valore corrente rispetto all'elenco.
3580
3849
  * - `known`: presente nell'elenco;
3581
- * - `navigated`: la radice e' nota ma c'e' un percorso dopo il punto (campo di record,
3582
- * output automatico navigato): la validazione non verifica i percorsi con il punto,
3583
- * quindi si accetta senza segnalare;
3850
+ * - `navigated`: la radice e' nota ma la sua forma non e' dichiarata (output automatico di un
3851
+ * elemento): il percorso resta fuori dalla validazione, quindi si accetta senza segnalare;
3584
3852
  * - `host`: scope del sistema ospite che **non dichiara percorsi**: non verificabile (§4.1);
3585
- * - `member`: membro di una classe, esistente: qui il percorso **e'** verificato (§4.7);
3586
- * - `memberUnknown`: la radice e' una `Structure` ma il membro non c'e': e'
3587
- * `STRUCTURE_MEMBER_UNKNOWN`, e a differenza di un campo di record si sa per certo;
3588
- * - `memberNotWritable`: il membro esiste, e' calcolato, e questo campo vuole una
3853
+ * - `member`: percorso dentro una forma dichiarata, verificato fino in fondo (§4.4, §4.7.1);
3854
+ * - `memberUnknown`: un segmento non esiste nella sua tappa: `STRUCTURE_MEMBER_UNKNOWN` su una
3855
+ * classe, `FIELD_UNKNOWN` su un'entita', e a differenza di prima si sa per certo;
3856
+ * - `memberNotWritable`: il segmento esiste, non e' scrivibile, e questo campo vuole una
3589
3857
  * destinazione: `TARGET_NOT_WRITABLE`;
3858
+ * - `pathUnverified`: la catena si interrompe (`PATH_NOT_VERIFIABLE`): avviso, non errore;
3590
3859
  * - `unknown`: nessuna corrispondenza: si avvisa, senza bloccare. Ci finisce anche il
3591
3860
  * percorso inesistente di uno scope che i percorsi li dichiara, perche' lì il backend
3592
3861
  * risponderebbe `GLOBAL_UNKNOWN`.
3593
3862
  */
3594
3863
  valueState = computed(() => {
3595
- const value = (this.value() ?? '').trim();
3864
+ const value = (this.value() ?? '')?.trim();
3596
3865
  if (!value) {
3597
3866
  return 'empty';
3598
3867
  }
@@ -3604,19 +3873,20 @@ class ReferencePickerComponent {
3604
3873
  if (this.dictionaries.globalScope(root) && !this.dictionaries.isGlobalScopeVerifiable(root)) {
3605
3874
  return 'host';
3606
3875
  }
3607
- // Dentro una classe il primo segmento e' verificabile: dirlo "non verificato" sarebbe falso.
3608
- const structure = this.structureRoot();
3609
- if (structure && structure.name === root && this.members().length) {
3610
- const rest = value.slice(root.length + 1);
3611
- const segment = rest.includes('.') ? rest.slice(0, rest.indexOf('.')) : rest;
3612
- const member = this.members().find((candidate) => candidate.name === segment);
3613
- if (!member) {
3614
- return 'memberUnknown';
3876
+ // Dentro una forma dichiarata il percorso e' verificabile: dirlo "non verificato" sarebbe falso.
3877
+ if (this.navigableRoot()) {
3878
+ const resolution = this.resolution();
3879
+ switch (resolution?.status) {
3880
+ case 'unknown':
3881
+ return 'memberUnknown';
3882
+ case 'unverifiable':
3883
+ return 'pathUnverified';
3884
+ case 'resolved':
3885
+ return this.writableOnly() && !resolution.last?.isWritable ? 'memberNotWritable' : 'member';
3886
+ default:
3887
+ // Percorso ancora aperto (`Richiesta.`) o risposta non arrivata: non si accusa nessuno.
3888
+ return 'empty';
3615
3889
  }
3616
- if (this.writableOnly() && member.isWritable === false) {
3617
- return 'memberNotWritable';
3618
- }
3619
- return 'member';
3620
3890
  }
3621
3891
  if (all.some((reference) => reference.name === root)) {
3622
3892
  return 'navigated';
@@ -3624,22 +3894,43 @@ class ReferencePickerComponent {
3624
3894
  // Se l'elenco non e' arrivato, non si accusa nessuno.
3625
3895
  return all.length === 0 && this.isLoading() ? 'empty' : 'unknown';
3626
3896
  }, ...(ngDevMode ? [{ debugName: "valueState" }] : []));
3897
+ /** Le parole cambiano con la tappa: un campo di un'entita' non e' un membro di una classe. */
3898
+ tailIsObject = computed(() => {
3899
+ const resolution = this.resolution();
3900
+ return resolution?.levels[resolution.levels.length - 1]?.container.kind === 'object';
3901
+ }, ...(ngDevMode ? [{ debugName: "tailIsObject" }] : []));
3902
+ tailContainer = computed(() => {
3903
+ const resolution = this.resolution();
3904
+ return resolution?.levels[resolution.levels.length - 1]?.container.name ?? '';
3905
+ }, ...(ngDevMode ? [{ debugName: "tailContainer" }] : []));
3627
3906
  hint = computed(() => {
3907
+ const resolution = this.resolution();
3628
3908
  switch (this.valueState()) {
3629
3909
  case 'host':
3630
3910
  return 'Scope risolto dal sistema ospite, che non dichiara i suoi percorsi: il backend non puo’ verificarlo.';
3631
3911
  case 'navigated':
3632
- return 'Percorso su un riferimento noto: la validazione non verifica i campi dopo il punto.';
3633
- case 'member':
3634
- return `Membro di ${this.structureRoot()?.objectType}. Oltre il primo segmento il percorso non e’ verificato.`;
3912
+ return 'Percorso su un output automatico: la validazione non verifica i segmenti dopo il punto.';
3913
+ case 'member': {
3914
+ const what = this.tailIsObject() ? 'Campo' : 'Membro';
3915
+ return `${what} di ${this.tailContainer()}: il percorso e’ verificato fino in fondo.`;
3916
+ }
3635
3917
  case 'memberUnknown': {
3636
- const names = this.members()
3637
- .map((member) => member.name)
3638
- .join(', ');
3639
- return `Questo membro non esiste su ${this.structureRoot()?.objectType} (STRUCTURE_MEMBER_UNKNOWN). Disponibili: ${names}.`;
3918
+ if (!resolution) {
3919
+ return null;
3920
+ }
3921
+ const segment = resolution.stoppedAt?.segment ?? '';
3922
+ const names = pathAvailableNames(resolution).join(', ');
3923
+ const head = this.tailIsObject()
3924
+ ? `«${segment}» non e’ un campo di ${this.tailContainer()} (FIELD_UNKNOWN).`
3925
+ : `«${segment}» non e’ un membro di ${this.tailContainer()} (STRUCTURE_MEMBER_UNKNOWN).`;
3926
+ return names ? `${head} Disponibili: ${names}.` : head;
3640
3927
  }
3641
3928
  case 'memberNotWritable':
3642
- return 'Questo membro e’ calcolato: si legge, ma assegnarlo e’ TARGET_NOT_WRITABLE.';
3929
+ return this.tailIsObject()
3930
+ ? 'Questo campo non si scrive ne’ creando ne’ aggiornando un record: assegnarlo e’ TARGET_NOT_WRITABLE.'
3931
+ : 'Questo membro e’ calcolato: si legge, ma assegnarlo e’ TARGET_NOT_WRITABLE.';
3932
+ case 'pathUnverified':
3933
+ return resolution ? pathNotVerifiableMessage(resolution) : null;
3643
3934
  case 'unknown':
3644
3935
  return 'Questo nome non e’ fra i riferimenti disponibili: la validazione lo segnalerebbe.';
3645
3936
  default:
@@ -3669,9 +3960,25 @@ class ReferencePickerComponent {
3669
3960
  this.valueChange.emit(reference.name);
3670
3961
  this.close();
3671
3962
  }
3672
- /** Scrittura libera: indispensabile per i campi di un record e per gli scope dell'host. */
3963
+ /**
3964
+ * Scende di un livello dentro un percorso proponibile invece di scegliere il segmento: scrive il
3965
+ * punto, e l'elenco diventa quello della tappa successiva. Serve perche' `Richiesta.Cliente` puo'
3966
+ * essere il riferimento che si vuole **oppure** la tappa da cui continuare.
3967
+ */
3968
+ descend(reference) {
3969
+ this.valueChange.emit(`${reference.name}.`);
3970
+ this.query.set('');
3971
+ }
3972
+ /** `true` se da questo riferimento il percorso continua: la sua forma e' dichiarata. */
3973
+ canDescend(reference) {
3974
+ if (reference.isCollection || !reference.objectType || reference.kind === 'ElementOutput') {
3975
+ return false;
3976
+ }
3977
+ return this.dictionaries.isStructure(reference.dataType) || reference.dataType === 'Object';
3978
+ }
3979
+ /** Scrittura libera: indispensabile per gli scope dell'host e per i percorsi non enumerabili. */
3673
3980
  onManualInput(value) {
3674
- this.valueChange.emit(value.trim() ? value.trim() : undefined);
3981
+ this.valueChange.emit(value?.trim() ? value?.trim() : undefined);
3675
3982
  }
3676
3983
  clear() {
3677
3984
  this.valueChange.emit(undefined);
@@ -3690,11 +3997,11 @@ class ReferencePickerComponent {
3690
3997
  return parts.join(' · ');
3691
3998
  }
3692
3999
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ReferencePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3693
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ReferencePickerComponent, isStandalone: true, selector: "fb-reference-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, writableOnly: { classPropertyName: "writableOnly", publicName: "writableOnly", isSignal: true, isRequired: false, transformFunction: null }, elementsOnly: { classPropertyName: "elementsOnly", publicName: "elementsOnly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <!-- Un membro inesistente o non scrivibile e' un **errore**: mostrarlo come avviso direbbe il falso. -->\r\n <p\r\n class=\"fb-ref__hint\"\r\n [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\"\r\n [class.fb-ref__hint--error]=\"isHintError()\"\r\n >\r\n {{ hint() }}\r\n </p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__hint--error{color:var(--fb-error, #c9372c)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4000
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ReferencePickerComponent, isStandalone: true, selector: "fb-reference-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, writableOnly: { classPropertyName: "writableOnly", publicName: "writableOnly", isSignal: true, isRequired: false, transformFunction: null }, elementsOnly: { classPropertyName: "elementsOnly", publicName: "elementsOnly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <!-- Un membro inesistente o non scrivibile e' un **errore**: mostrarlo come avviso direbbe il falso. -->\r\n <p\r\n class=\"fb-ref__hint\"\r\n [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\"\r\n [class.fb-ref__hint--error]=\"isHintError()\"\r\n >\r\n {{ hint() }}\r\n </p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <!-- Due bottoni dove il percorso continua: scegliere il riferimento e scendere di un\r\n livello sono cose diverse, e su una Structure o un record servono entrambe. -->\r\n <div class=\"fb-ref__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n @if (canDescend(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__into\"\r\n [attr.aria-label]=\"'Entra in ' + item.name\"\r\n title=\"Entra e proponi i segmenti\"\r\n (click)=\"descend(item)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__hint--error{color:var(--fb-error, #c9372c)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__row{display:flex;align-items:stretch;gap:2px}.fb-ref__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-ref__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-ref__option{display:flex;flex:1;flex-direction:column;min-width:0;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3694
4001
  }
3695
4002
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ReferencePickerComponent, decorators: [{
3696
4003
  type: Component,
3697
- args: [{ selector: 'fb-reference-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <!-- Un membro inesistente o non scrivibile e' un **errore**: mostrarlo come avviso direbbe il falso. -->\r\n <p\r\n class=\"fb-ref__hint\"\r\n [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\"\r\n [class.fb-ref__hint--error]=\"isHintError()\"\r\n >\r\n {{ hint() }}\r\n </p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__hint--error{color:var(--fb-error, #c9372c)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4004
+ args: [{ selector: 'fb-reference-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <!-- Un membro inesistente o non scrivibile e' un **errore**: mostrarlo come avviso direbbe il falso. -->\r\n <p\r\n class=\"fb-ref__hint\"\r\n [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\"\r\n [class.fb-ref__hint--error]=\"isHintError()\"\r\n >\r\n {{ hint() }}\r\n </p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <!-- Due bottoni dove il percorso continua: scegliere il riferimento e scendere di un\r\n livello sono cose diverse, e su una Structure o un record servono entrambe. -->\r\n <div class=\"fb-ref__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n @if (canDescend(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__into\"\r\n [attr.aria-label]=\"'Entra in ' + item.name\"\r\n title=\"Entra e proponi i segmenti\"\r\n (click)=\"descend(item)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__hint--error{color:var(--fb-error, #c9372c)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__row{display:flex;align-items:stretch;gap:2px}.fb-ref__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-ref__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-ref__option{display:flex;flex:1;flex-direction:column;min-width:0;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
3698
4005
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], writableOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "writableOnly", required: false }] }], elementsOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementsOnly", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3699
4006
 
3700
4007
  /**
@@ -3737,8 +4044,8 @@ class ObjectPickerComponent {
3737
4044
  * resterebbe nella casella pur non essendo piu' il valore del documento.
3738
4045
  */
3739
4046
  effect(() => {
3740
- const value = (this.value() ?? '').trim();
3741
- if (value !== (untracked(this.query) ?? '').trim()) {
4047
+ const value = (this.value() ?? '')?.trim();
4048
+ if (value !== (untracked(this.query) ?? '')?.trim()) {
3742
4049
  this.query.set(null);
3743
4050
  }
3744
4051
  });
@@ -3746,7 +4053,7 @@ class ObjectPickerComponent {
3746
4053
  /** Quel che si vede nella casella: il filtro se si sta digitando, il valore altrimenti. */
3747
4054
  text = computed(() => this.query() ?? this.value() ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
3748
4055
  options = computed(() => {
3749
- const needle = (this.query() ?? '').trim().toLowerCase();
4056
+ const needle = (this.query() ?? '')?.trim().toLowerCase();
3750
4057
  const all = this.objects();
3751
4058
  if (!needle) {
3752
4059
  return all;
@@ -3759,14 +4066,14 @@ class ObjectPickerComponent {
3759
4066
  * "non lo so" non e' "non esiste".
3760
4067
  */
3761
4068
  isUnknown = computed(() => {
3762
- const value = (this.value() ?? '').trim();
4069
+ const value = (this.value() ?? '')?.trim();
3763
4070
  if (!value || !this.hasCatalog()) {
3764
4071
  return false;
3765
4072
  }
3766
4073
  return !this.objects().some((object) => object.name === value);
3767
4074
  }, ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
3768
4075
  labelOfValue = computed(() => {
3769
- const value = (this.value() ?? '').trim();
4076
+ const value = (this.value() ?? '')?.trim();
3770
4077
  const found = this.objects().find((object) => object.name === value);
3771
4078
  return found?.label && found.label !== found.name ? found.label : null;
3772
4079
  }, ...(ngDevMode ? [{ debugName: "labelOfValue" }] : []));
@@ -3788,7 +4095,7 @@ class ObjectPickerComponent {
3788
4095
  onInput(text) {
3789
4096
  this.query.set(text);
3790
4097
  this.open();
3791
- this.valueChange.emit(text.trim() ? text.trim() : undefined);
4098
+ this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
3792
4099
  }
3793
4100
  choose(object) {
3794
4101
  this.valueChange.emit(object.name);
@@ -3809,11 +4116,11 @@ class ObjectPickerComponent {
3809
4116
  return parts.join(' · ');
3810
4117
  }
3811
4118
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ObjectPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3812
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ObjectPickerComponent, isStandalone: true, selector: "fb-object-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra gli oggetti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Questo nome non e\u2019 fra gli oggetti disponibili: la validazione lo segnalerebbe.\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco degli oggetti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasCatalog() ? 'Nessun oggetto corrisponde.' : 'Catalogo degli oggetti non disponibile.' }}\r\n </p>\r\n }\r\n @for (object of options(); track object.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"object.name === value()\"\r\n (click)=\"choose(object)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ object.name }}</span>\r\n @if (describe(object)) {\r\n <span class=\"fb-pick__meta\">{{ describe(object) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4119
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ObjectPickerComponent, isStandalone: true, selector: "fb-object-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra gli oggetti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Questo nome non e\u2019 fra gli oggetti disponibili: la validazione lo segnalerebbe.\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco degli oggetti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasCatalog() ? 'Nessun oggetto corrisponde.' : 'Catalogo degli oggetti non disponibile.' }}\r\n </p>\r\n }\r\n @for (object of options(); track object.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"object.name === value()\"\r\n (click)=\"choose(object)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ object.name }}</span>\r\n @if (describe(object)) {\r\n <span class=\"fb-pick__meta\">{{ describe(object) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3813
4120
  }
3814
4121
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ObjectPickerComponent, decorators: [{
3815
4122
  type: Component,
3816
- args: [{ selector: 'fb-object-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra gli oggetti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Questo nome non e\u2019 fra gli oggetti disponibili: la validazione lo segnalerebbe.\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco degli oggetti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasCatalog() ? 'Nessun oggetto corrisponde.' : 'Catalogo degli oggetti non disponibile.' }}\r\n </p>\r\n }\r\n @for (object of options(); track object.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"object.name === value()\"\r\n (click)=\"choose(object)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ object.name }}</span>\r\n @if (describe(object)) {\r\n <span class=\"fb-pick__meta\">{{ describe(object) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4123
+ args: [{ selector: 'fb-object-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra gli oggetti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Questo nome non e\u2019 fra gli oggetti disponibili: la validazione lo segnalerebbe.\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco degli oggetti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasCatalog() ? 'Nessun oggetto corrisponde.' : 'Catalogo degli oggetti non disponibile.' }}\r\n </p>\r\n }\r\n @for (object of options(); track object.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"object.name === value()\"\r\n (click)=\"choose(object)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ object.name }}</span>\r\n @if (describe(object)) {\r\n <span class=\"fb-pick__meta\">{{ describe(object) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
3817
4124
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3818
4125
 
3819
4126
  /**
@@ -3829,9 +4136,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3829
4136
  * porta il flow che crea il record e non si cambia dopo, un identificativo generato dal database
3830
4137
  * e' l'opposto (§5.7).
3831
4138
  *
3832
- * La scrittura libera resta possibile perche' i percorsi di relazione (`Cliente.Citta`) non
3833
- * sono enumerabili: su un valore col punto non si accusa nessuno, si avvisa che la
3834
- * validazione non lo verifica.
4139
+ * I **percorsi di relazione** (`Cliente.Citta`) non sono piu' un buco nero: la validazione li
4140
+ * segue un segmento alla volta (§4.4), e quindi li segue anche il picker `core/path-navigation`
4141
+ * naviga la catena, l'elenco propone **il livello in cui si sta scrivendo** e l'ultimo segmento si
4142
+ * verifica con l'`usage` richiesto. Dove la catena si interrompe — una relazione che non dichiara
4143
+ * l'oggetto referenziato, un oggetto non descritto, una collection — l'avviso e'
4144
+ * `PATH_NOT_VERIFIABLE`, che vuol dire "non lo so" e non "sbagliato": la scrittura libera resta.
3835
4145
  */
3836
4146
  class FieldPickerComponent {
3837
4147
  catalog = inject(FlowCatalogStore);
@@ -3843,80 +4153,94 @@ class FieldPickerComponent {
3843
4153
  placeholder = input('Scrivi o scegli un campo', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
3844
4154
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3845
4155
  valueChange = output();
3846
- fields = signal([], ...(ngDevMode ? [{ debugName: "fields" }] : []));
3847
- loadFailed = signal(false, ...(ngDevMode ? [{ debugName: "loadFailed" }] : []));
3848
4156
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
3849
4157
  /** Il testo digitato mentre il pannello e' aperto: filtra l'elenco. */
3850
4158
  query = signal(null, ...(ngDevMode ? [{ debugName: "query" }] : []));
4159
+ /**
4160
+ * La navigazione del percorso corrente. Il testo digitato conta quanto il valore: mentre si
4161
+ * scrive `Cliente.Cit` l'elenco deve già essere quello dei campi di `Cliente`.
4162
+ */
4163
+ navigation = navigatePath(this.catalog, () => ({
4164
+ root: this.object() ? { kind: 'object', name: this.object() } : null,
4165
+ path: this.query() ?? this.value() ?? '',
4166
+ usage: this.usage(),
4167
+ }));
4168
+ resolution = this.navigation.resolution;
3851
4169
  constructor() {
3852
- effect(() => {
3853
- const object = this.object();
3854
- const usage = this.usage();
3855
- this.loadFailed.set(false);
3856
- if (!object) {
3857
- this.fields.set([]);
3858
- return;
3859
- }
3860
- void this.catalog
3861
- .listFields(object, usage)
3862
- .then((list) => this.fields.set(list ?? []))
3863
- // Catalogo non disponibile: il campo resta scrivibile a mano, non bloccato.
3864
- .catch(() => {
3865
- this.fields.set([]);
3866
- this.loadFailed.set(true);
3867
- });
3868
- });
3869
4170
  // Vedi ObjectPickerComponent: il filtro sopravvive alla propria scrittura, non a un
3870
4171
  // valore che arriva da fuori (un'altra riga della lista, un altro elemento).
3871
4172
  effect(() => {
3872
- const value = (this.value() ?? '').trim();
3873
- if (value !== (untracked(this.query) ?? '').trim()) {
4173
+ const value = (this.value() ?? '')?.trim();
4174
+ if (value !== (untracked(this.query) ?? '')?.trim()) {
3874
4175
  this.query.set(null);
3875
4176
  }
3876
4177
  });
3877
4178
  }
3878
4179
  text = computed(() => this.query() ?? this.value() ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
4180
+ /** La tappa in cui si sta scrivendo: da qui vengono le proposte, non dalla radice. */
4181
+ tail = computed(() => this.resolution()?.tail, ...(ngDevMode ? [{ debugName: "tail" }] : []));
4182
+ /** Cio' che si premette al nome scelto: `''` sul primo segmento, `Cliente.` sul secondo. */
4183
+ prefix = computed(() => {
4184
+ const prefix = this.tail()?.prefix ?? '';
4185
+ return prefix ? `${prefix}.` : '';
4186
+ }, ...(ngDevMode ? [{ debugName: "prefix" }] : []));
3879
4187
  options = computed(() => {
3880
- const needle = (this.query() ?? '').trim().toLowerCase();
3881
- const all = this.fields();
4188
+ const tail = this.tail();
4189
+ if (!tail) {
4190
+ return [];
4191
+ }
4192
+ const needle = tail.segment?.trim().toLowerCase();
3882
4193
  if (!needle) {
3883
- return all;
4194
+ return tail.level.entries;
3884
4195
  }
3885
- return all.filter((field) => `${field.name} ${field.label ?? ''}`.toLowerCase().includes(needle));
4196
+ return tail.level.entries.filter((entry) => `${entry.name} ${entry.label ?? ''}`.toLowerCase().includes(needle));
3886
4197
  }, ...(ngDevMode ? [{ debugName: "options" }] : []));
3887
- hasCatalog = computed(() => this.fields().length > 0, ...(ngDevMode ? [{ debugName: "hasCatalog" }] : []));
3888
- /** `true` se il valore e' un percorso di relazione: non enumerabile, non verificato (§4.4). */
3889
- isRelationPath = computed(() => (this.value() ?? '').includes('.'), ...(ngDevMode ? [{ debugName: "isRelationPath" }] : []));
3890
- isUnknown = computed(() => {
3891
- const value = (this.value() ?? '').trim();
3892
- if (!value || !this.hasCatalog() || this.isRelationPath()) {
3893
- return false;
3894
- }
3895
- return !this.fields().some((field) => field.name === value);
3896
- }, ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
3897
- /** L'avviso dipende dall'uso: un campo puo' esistere e non essere filtrabile. */
4198
+ /** `true` se la tappa in cui si scrive ha un elenco **autorevole** (§4.7.1). */
4199
+ hasCatalog = computed(() => this.tail()?.level.status === 'declared', ...(ngDevMode ? [{ debugName: "hasCatalog" }] : []));
4200
+ /** L'etichetta della tappa: serve nell'intestazione dell'elenco quando non e' la radice. */
4201
+ levelLabel = computed(() => this.tail()?.level.container.name ?? '', ...(ngDevMode ? [{ debugName: "levelLabel" }] : []));
4202
+ /** `true` se il valore e' un percorso navigato: cambia il testo dei messaggi, non la severita'. */
4203
+ isPath = computed(() => (this.resolution()?.segments.length ?? 1) > 1, ...(ngDevMode ? [{ debugName: "isPath" }] : []));
4204
+ isUnknown = computed(() => this.resolution()?.status === 'unknown', ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
4205
+ /** §4.4 — la catena si e' interrotta: avviso, non errore. Il percorso puo' essere giusto. */
4206
+ isNotVerifiable = computed(() => this.resolution()?.status === 'unverifiable', ...(ngDevMode ? [{ debugName: "isNotVerifiable" }] : []));
4207
+ /**
4208
+ * L'avviso dipende dall'uso un campo puo' esistere e non essere filtrabile — e dalla **tappa**:
4209
+ * su `Cliente.Citta` il campo che non esiste e' di `Cliente`, non dell'oggetto di partenza.
4210
+ */
3898
4211
  unknownMessage = computed(() => {
4212
+ const resolution = this.resolution();
4213
+ const segment = resolution?.stoppedAt?.segment ?? '';
4214
+ const container = this.levelLabel();
4215
+ const names = resolution ? pathAvailableNames(resolution).join(', ') : '';
4216
+ const what = `«${segment}»`;
4217
+ const where = this.isPath() ? ` di ${container}` : '';
4218
+ const suffix = names ? ` Disponibili: ${names}.` : '';
3899
4219
  switch (this.usage()) {
3900
4220
  case 'filterable':
3901
- return 'Questo campo non e’ fra quelli filtrabili dell’oggetto.';
4221
+ return `${what} non e’ fra i campi filtrabili${where}.${suffix}`;
3902
4222
  case 'sortable':
3903
- return 'Questo campo non e’ fra quelli su cui si puo’ ordinare.';
4223
+ return `${what} non e’ fra i campi su cui si puo’ ordinare${where}.${suffix}`;
3904
4224
  case 'updateable':
3905
- return 'Questo campo non e’ fra quelli aggiornabili dell’oggetto.';
4225
+ return `${what} non e’ fra i campi aggiornabili${where}.${suffix}`;
3906
4226
  case 'createable':
3907
4227
  // Non e' lo stesso insieme di `updateable`: la colonna di una chiave naturale si scrive
3908
4228
  // creando il record e non si cambia dopo (§5.7).
3909
- return 'Questo campo non e’ fra quelli valorizzabili alla creazione.';
4229
+ return `${what} non e’ fra i campi valorizzabili alla creazione${where}.${suffix}`;
3910
4230
  default:
3911
- return 'Questo campo non e’ fra quelli dell’oggetto.';
4231
+ return `${what} non e’ un campo${where || ' dell’oggetto'}.${suffix}`;
3912
4232
  }
3913
4233
  }, ...(ngDevMode ? [{ debugName: "unknownMessage" }] : []));
4234
+ notVerifiableMessage = computed(() => {
4235
+ const resolution = this.resolution();
4236
+ return resolution ? pathNotVerifiableMessage(resolution) : '';
4237
+ }, ...(ngDevMode ? [{ debugName: "notVerifiableMessage" }] : []));
3914
4238
  describeValue = computed(() => {
3915
- const value = (this.value() ?? '').trim();
3916
- const found = this.fields().find((field) => field.name === value);
3917
- return found ? this.describe(found) || null : null;
4239
+ const last = this.resolution()?.last;
4240
+ return last ? describePathEntry(last) || null : null;
3918
4241
  }, ...(ngDevMode ? [{ debugName: "describeValue" }] : []));
3919
- loadErrored = computed(() => this.loadFailed(), ...(ngDevMode ? [{ debugName: "loadErrored" }] : []));
4242
+ /** Catalogo non raggiungibile sulla **radice**: il campo resta scrivibile a mano, non bloccato. */
4243
+ loadErrored = computed(() => this.resolution()?.levels[0]?.status === 'unknown-catalog', ...(ngDevMode ? [{ debugName: "loadErrored" }] : []));
3920
4244
  open() {
3921
4245
  if (this.disabled()) {
3922
4246
  return;
@@ -3933,35 +4257,40 @@ class FieldPickerComponent {
3933
4257
  onInput(text) {
3934
4258
  this.query.set(text);
3935
4259
  this.open();
3936
- this.valueChange.emit(text.trim() ? text.trim() : undefined);
4260
+ this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
3937
4261
  }
3938
- choose(field) {
3939
- this.valueChange.emit(field.name);
4262
+ choose(entry) {
4263
+ this.valueChange.emit(`${this.prefix()}${entry.name}`);
3940
4264
  this.close();
3941
4265
  }
4266
+ /**
4267
+ * Scende dentro una relazione invece di scegliere il campo: il pannello resta aperto e l'elenco
4268
+ * diventa quello dell'oggetto referenziato. È l'equivalente di scrivere il punto, e infatti
4269
+ * scrive il punto: il valore intermedio e' quello che si vedrebbe digitando.
4270
+ */
4271
+ descend(entry) {
4272
+ const path = `${this.prefix()}${entry.name}.`;
4273
+ this.query.set(path);
4274
+ this.valueChange.emit(path);
4275
+ this.open();
4276
+ }
4277
+ /** `true` se da questo campo il percorso continua: e' una relazione con l'oggetto dichiarato. */
4278
+ canDescend(entry) {
4279
+ return !!entry.next;
4280
+ }
3942
4281
  clear() {
3943
4282
  this.valueChange.emit(undefined);
3944
4283
  this.close();
3945
4284
  }
3946
- describe(field) {
3947
- const parts = [];
3948
- if (field.label && field.label !== field.name) {
3949
- parts.push(field.label);
3950
- }
3951
- if (field.dataType) {
3952
- parts.push(field.objectType ? `${field.dataType} · ${field.objectType}` : field.dataType);
3953
- }
3954
- if (field.isRequired) {
3955
- parts.push('obbligatorio');
3956
- }
3957
- return parts.join(' · ');
4285
+ describe(entry) {
4286
+ return describePathEntry(entry);
3958
4287
  }
3959
4288
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FieldPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3960
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FieldPickerComponent, isStandalone: true, selector: "fb-field-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i campi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isRelationPath()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Percorso di relazione: la validazione non lo verifica, un errore si scopre solo eseguendo.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ unknownMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei campi non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!object()) {\r\n Scegli prima un oggetto.\r\n } @else {\r\n {{ hasCatalog() ? 'Nessun campo corrisponde.' : 'Catalogo dei campi non disponibile.' }}\r\n }\r\n </p>\r\n }\r\n @for (field of options(); track field.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"field.name === value()\"\r\n (click)=\"choose(field)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ field.name }}</span>\r\n @if (describe(field)) {\r\n <span class=\"fb-pick__meta\">{{ describe(field) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4289
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FieldPickerComponent, isStandalone: true, selector: "fb-field-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i campi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ unknownMessage() }}</p>\r\n } @else if (isNotVerifiable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ notVerifiableMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei campi non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (prefix()) {\r\n <p class=\"fb-pick__group\">Campi di {{ levelLabel() }}</p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!object()) {\r\n Scegli prima un oggetto.\r\n } @else {\r\n {{ hasCatalog() ? 'Nessun campo corrisponde.' : 'Catalogo dei campi non disponibile.' }}\r\n }\r\n </p>\r\n }\r\n @for (entry of options(); track entry.name) {\r\n <div class=\"fb-pick__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"prefix() + entry.name === value()\"\r\n (click)=\"choose(entry)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ entry.name }}</span>\r\n @if (describe(entry)) {\r\n <span class=\"fb-pick__meta\">{{ describe(entry) }}</span>\r\n }\r\n </button>\r\n @if (canDescend(entry)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__into\"\r\n [attr.aria-label]=\"'Entra in ' + entry.name\"\r\n title=\"Entra nella relazione\"\r\n (click)=\"descend(entry)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3961
4290
  }
3962
4291
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FieldPickerComponent, decorators: [{
3963
4292
  type: Component,
3964
- args: [{ selector: 'fb-field-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i campi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isRelationPath()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Percorso di relazione: la validazione non lo verifica, un errore si scopre solo eseguendo.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ unknownMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei campi non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!object()) {\r\n Scegli prima un oggetto.\r\n } @else {\r\n {{ hasCatalog() ? 'Nessun campo corrisponde.' : 'Catalogo dei campi non disponibile.' }}\r\n }\r\n </p>\r\n }\r\n @for (field of options(); track field.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"field.name === value()\"\r\n (click)=\"choose(field)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ field.name }}</span>\r\n @if (describe(field)) {\r\n <span class=\"fb-pick__meta\">{{ describe(field) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4293
+ args: [{ selector: 'fb-field-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i campi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ unknownMessage() }}</p>\r\n } @else if (isNotVerifiable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ notVerifiableMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei campi non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (prefix()) {\r\n <p class=\"fb-pick__group\">Campi di {{ levelLabel() }}</p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!object()) {\r\n Scegli prima un oggetto.\r\n } @else {\r\n {{ hasCatalog() ? 'Nessun campo corrisponde.' : 'Catalogo dei campi non disponibile.' }}\r\n }\r\n </p>\r\n }\r\n @for (entry of options(); track entry.name) {\r\n <div class=\"fb-pick__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"prefix() + entry.name === value()\"\r\n (click)=\"choose(entry)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ entry.name }}</span>\r\n @if (describe(entry)) {\r\n <span class=\"fb-pick__meta\">{{ describe(entry) }}</span>\r\n }\r\n </button>\r\n @if (canDescend(entry)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__into\"\r\n [attr.aria-label]=\"'Entra in ' + entry.name\"\r\n title=\"Entra nella relazione\"\r\n (click)=\"descend(entry)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
3965
4294
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], object: [{ type: i0.Input, args: [{ isSignal: true, alias: "object", required: false }] }], usage: [{ type: i0.Input, args: [{ isSignal: true, alias: "usage", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3966
4295
 
3967
4296
  /**
@@ -4002,15 +4331,15 @@ class NamePickerComponent {
4002
4331
  constructor() {
4003
4332
  // Vedi ObjectPickerComponent: il filtro non sopravvive a un valore che arriva da fuori.
4004
4333
  effect(() => {
4005
- const value = (this.value() ?? '').trim();
4006
- if (value !== (untracked(this.query) ?? '').trim()) {
4334
+ const value = (this.value() ?? '')?.trim();
4335
+ if (value !== (untracked(this.query) ?? '')?.trim()) {
4007
4336
  this.query.set(null);
4008
4337
  }
4009
4338
  });
4010
4339
  }
4011
4340
  text = computed(() => this.query() ?? this.value() ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
4012
4341
  visibleOptions = computed(() => {
4013
- const needle = (this.query() ?? '').trim().toLowerCase();
4342
+ const needle = (this.query() ?? '')?.trim().toLowerCase();
4014
4343
  const all = this.options();
4015
4344
  if (!needle) {
4016
4345
  return all;
@@ -4019,14 +4348,14 @@ class NamePickerComponent {
4019
4348
  }, ...(ngDevMode ? [{ debugName: "visibleOptions" }] : []));
4020
4349
  hasOptions = computed(() => this.options().length > 0, ...(ngDevMode ? [{ debugName: "hasOptions" }] : []));
4021
4350
  isUnknown = computed(() => {
4022
- const value = (this.value() ?? '').trim();
4351
+ const value = (this.value() ?? '')?.trim();
4023
4352
  if (!value || !this.hasOptions()) {
4024
4353
  return false;
4025
4354
  }
4026
4355
  return !this.options().some((option) => option.name === value);
4027
4356
  }, ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
4028
4357
  labelOfValue = computed(() => {
4029
- const value = (this.value() ?? '').trim();
4358
+ const value = (this.value() ?? '')?.trim();
4030
4359
  const found = this.options().find((option) => option.name === value);
4031
4360
  return found ? this.describe(found) || null : null;
4032
4361
  }, ...(ngDevMode ? [{ debugName: "labelOfValue" }] : []));
@@ -4046,7 +4375,7 @@ class NamePickerComponent {
4046
4375
  onInput(text) {
4047
4376
  this.query.set(text);
4048
4377
  this.open();
4049
- this.valueChange.emit(text.trim() ? text.trim() : undefined);
4378
+ this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
4050
4379
  }
4051
4380
  choose(option) {
4052
4381
  this.valueChange.emit(option.name);
@@ -4067,11 +4396,11 @@ class NamePickerComponent {
4067
4396
  return parts.join(' · ');
4068
4397
  }
4069
4398
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: NamePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4070
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: NamePickerComponent, isStandalone: true, selector: "fb-name-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, unknownMessage: { classPropertyName: "unknownMessage", publicName: "unknownMessage", isSignal: true, isRequired: false, transformFunction: null }, unknownSeverity: { classPropertyName: "unknownSeverity", publicName: "unknownSeverity", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, isMono: { classPropertyName: "isMono", publicName: "isMono", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n [class.fb-pick__input--mono]=\"isMono()\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i nomi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p\r\n class=\"fb-pick__hint\"\r\n [class.fb-pick__hint--warn]=\"unknownSeverity() === 'warn'\"\r\n [class.fb-pick__hint--error]=\"unknownSeverity() === 'error'\"\r\n >\r\n {{ unknownMessage() }}\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (!hasOptions()) {\r\n <p class=\"fb-pick__hint\">{{ emptyMessage() }}</p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (visibleOptions().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasOptions() ? 'Nessun nome corrisponde.' : 'Nessun candidato da proporre.' }}\r\n </p>\r\n }\r\n @for (option of visibleOptions(); track option.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"option.name === value()\"\r\n (click)=\"choose(option)\"\r\n >\r\n <span class=\"fb-pick__name\" [class.fb-pick__name--mono]=\"isMono()\">{{ option.name }}</span>\r\n @if (describe(option)) {\r\n <span class=\"fb-pick__meta\">{{ describe(option) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4399
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: NamePickerComponent, isStandalone: true, selector: "fb-name-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, unknownMessage: { classPropertyName: "unknownMessage", publicName: "unknownMessage", isSignal: true, isRequired: false, transformFunction: null }, unknownSeverity: { classPropertyName: "unknownSeverity", publicName: "unknownSeverity", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, isMono: { classPropertyName: "isMono", publicName: "isMono", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n [class.fb-pick__input--mono]=\"isMono()\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i nomi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p\r\n class=\"fb-pick__hint\"\r\n [class.fb-pick__hint--warn]=\"unknownSeverity() === 'warn'\"\r\n [class.fb-pick__hint--error]=\"unknownSeverity() === 'error'\"\r\n >\r\n {{ unknownMessage() }}\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (!hasOptions()) {\r\n <p class=\"fb-pick__hint\">{{ emptyMessage() }}</p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (visibleOptions().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasOptions() ? 'Nessun nome corrisponde.' : 'Nessun candidato da proporre.' }}\r\n </p>\r\n }\r\n @for (option of visibleOptions(); track option.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"option.name === value()\"\r\n (click)=\"choose(option)\"\r\n >\r\n <span class=\"fb-pick__name\" [class.fb-pick__name--mono]=\"isMono()\">{{ option.name }}</span>\r\n @if (describe(option)) {\r\n <span class=\"fb-pick__meta\">{{ describe(option) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4071
4400
  }
4072
4401
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: NamePickerComponent, decorators: [{
4073
4402
  type: Component,
4074
- args: [{ selector: 'fb-name-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n [class.fb-pick__input--mono]=\"isMono()\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i nomi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p\r\n class=\"fb-pick__hint\"\r\n [class.fb-pick__hint--warn]=\"unknownSeverity() === 'warn'\"\r\n [class.fb-pick__hint--error]=\"unknownSeverity() === 'error'\"\r\n >\r\n {{ unknownMessage() }}\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (!hasOptions()) {\r\n <p class=\"fb-pick__hint\">{{ emptyMessage() }}</p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (visibleOptions().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasOptions() ? 'Nessun nome corrisponde.' : 'Nessun candidato da proporre.' }}\r\n </p>\r\n }\r\n @for (option of visibleOptions(); track option.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"option.name === value()\"\r\n (click)=\"choose(option)\"\r\n >\r\n <span class=\"fb-pick__name\" [class.fb-pick__name--mono]=\"isMono()\">{{ option.name }}</span>\r\n @if (describe(option)) {\r\n <span class=\"fb-pick__meta\">{{ describe(option) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4403
+ args: [{ selector: 'fb-name-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n [class.fb-pick__input--mono]=\"isMono()\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i nomi disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isUnknown()) {\r\n <p\r\n class=\"fb-pick__hint\"\r\n [class.fb-pick__hint--warn]=\"unknownSeverity() === 'warn'\"\r\n [class.fb-pick__hint--error]=\"unknownSeverity() === 'error'\"\r\n >\r\n {{ unknownMessage() }}\r\n </p>\r\n } @else if (labelOfValue()) {\r\n <p class=\"fb-pick__hint\">{{ labelOfValue() }}</p>\r\n } @else if (!hasOptions()) {\r\n <p class=\"fb-pick__hint\">{{ emptyMessage() }}</p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (visibleOptions().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n {{ hasOptions() ? 'Nessun nome corrisponde.' : 'Nessun candidato da proporre.' }}\r\n </p>\r\n }\r\n @for (option of visibleOptions(); track option.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"option.name === value()\"\r\n (click)=\"choose(option)\"\r\n >\r\n <span class=\"fb-pick__name\" [class.fb-pick__name--mono]=\"isMono()\">{{ option.name }}</span>\r\n @if (describe(option)) {\r\n <span class=\"fb-pick__meta\">{{ describe(option) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4075
4404
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], unknownMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "unknownMessage", required: false }] }], unknownSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "unknownSeverity", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], isMono: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMono", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4076
4405
 
4077
4406
  /**
@@ -4147,7 +4476,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
4147
4476
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4148
4477
 
4149
4478
  /**
4150
- * Selettore di **membro** di una classe — FRONTEND.md §4.7.
4479
+ * Selettore di **membro** di una classe — FRONTEND.md §4.7, §4.7.1.
4151
4480
  *
4152
4481
  * È l'analogo di {@link FieldPickerComponent} per le `Structure`: i membri arrivano dalla
4153
4482
  * descrizione della classe (`GET /catalog/structures/{className}/members`, che restituisce
@@ -4157,10 +4486,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
4157
4486
  * quindi con `usage="writable"` non compare nemmeno nell'elenco.
4158
4487
  *
4159
4488
  * Un membro che non esiste e' `STRUCTURE_MEMBER_UNKNOWN`, e il messaggio elenca i disponibili
4160
- * invece di dire soltanto che il nome e' sbagliato. Dei percorsi annidati
4161
- * (`Richiesta.Cliente.Nome`) viene verificato il **primo** segmento, come per i percorsi di
4162
- * relazione: oltre quello nessuno lo sa, ne' l'editor ne' il backend — la risposta e' piatta e
4163
- * i membri della classe annidata sono una seconda chiamata (§4.7.1).
4489
+ * invece di dire soltanto che il nome e' sbagliato.
4490
+ *
4491
+ * I percorsi **annidati** (`Richiesta.Cliente.Nome`) sono verificati fino in fondo: la risposta
4492
+ * resta piatta, ma la catena si segue una chiamata alla volta — `objectType` di un membro
4493
+ * `Structure` porta la classe annidata, quello di un membro `Object` porta un'**entita' dello
4494
+ * schema**, e da lì si continua con i campi (§4.7.1). Lo fa `core/path-navigation`, lo stesso
4495
+ * codice del field picker, perche' l'unica differenza fra i due mondi e' la primitiva che descrive
4496
+ * la tappa. Dove la catena si interrompe l'avviso e' `PATH_NOT_VERIFIABLE`: un "non lo so", che non
4497
+ * impedisce di scrivere.
4164
4498
  *
4165
4499
  * La risposta ha **tre** esiti e qui si vedono tutti e tre: elenco autorevole, classe valida coi
4166
4500
  * membri non dichiarati (si digita, senza accuse) e classe non registrata (404). Confonderli
@@ -4177,105 +4511,120 @@ class StructureMemberPickerComponent {
4177
4511
  placeholder = input('Scrivi o scegli un membro', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
4178
4512
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
4179
4513
  valueChange = output();
4180
- /**
4181
- * §4.7.1 — non basta l'elenco: serve l'**esito**. Elenco vuoto perche' i membri non sono
4182
- * dichiarati (si digita a mano) e classe non registrata (non c'e' niente da digitare) hanno
4183
- * la stessa lista e messaggi opposti.
4184
- */
4185
- result = signal({ status: 'unknown-catalog', members: [] }, ...(ngDevMode ? [{ debugName: "result" }] : []));
4186
- members = computed(() => this.result().members, ...(ngDevMode ? [{ debugName: "members" }] : []));
4187
4514
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
4188
4515
  /** Il testo digitato mentre il pannello e' aperto: filtra l'elenco. */
4189
4516
  query = signal(null, ...(ngDevMode ? [{ debugName: "query" }] : []));
4517
+ /**
4518
+ * La navigazione del percorso corrente, dalla classe in giu'. Il testo digitato conta quanto il
4519
+ * valore: mentre si scrive `Cliente.No` l'elenco deve già essere quello dei campi di `Cliente`.
4520
+ */
4521
+ navigation = navigatePath(this.catalog, () => ({
4522
+ root: this.className() ? { kind: 'structure', name: this.className() } : null,
4523
+ path: this.query() ?? this.value() ?? '',
4524
+ }));
4525
+ resolution = this.navigation.resolution;
4190
4526
  constructor() {
4191
- effect(() => {
4192
- const className = this.className();
4193
- if (!className) {
4194
- this.result.set({ status: 'unknown-catalog', members: [] });
4195
- return;
4196
- }
4197
- // `describeStructure` non rifiuta: gli esiti sono dati, non errori (§4.7.1).
4198
- void this.catalog.describeStructure(className).then((result) => this.result.set(result));
4199
- });
4200
4527
  // Vedi ObjectPickerComponent: il filtro sopravvive alla propria scrittura, non a un valore
4201
4528
  // che arriva da fuori (un'altra riga della lista, un altro elemento).
4202
4529
  effect(() => {
4203
- const value = (this.value() ?? '').trim();
4204
- if (value !== (untracked(this.query) ?? '').trim()) {
4530
+ const value = (this.value() ?? '')?.trim();
4531
+ if (value !== (untracked(this.query) ?? '')?.trim()) {
4205
4532
  this.query.set(null);
4206
4533
  }
4207
4534
  });
4208
4535
  }
4209
4536
  text = computed(() => this.query() ?? this.value() ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
4210
- /** Con `usage="writable"` i membri di sola lettura non si propongono affatto. */
4537
+ /** La tappa in cui si sta scrivendo: la classe di partenza, o quella in cui si e' entrati. */
4538
+ tail = computed(() => this.resolution()?.tail, ...(ngDevMode ? [{ debugName: "tail" }] : []));
4539
+ /** Cio' che si premette al nome scelto: `''` sul primo segmento, `Cliente.` sul secondo. */
4540
+ prefix = computed(() => {
4541
+ const prefix = this.tail()?.prefix ?? '';
4542
+ return prefix ? `${prefix}.` : '';
4543
+ }, ...(ngDevMode ? [{ debugName: "prefix" }] : []));
4544
+ /**
4545
+ * Con `usage="writable"` i segmenti di sola lettura non si propongono affatto — con
4546
+ * un'eccezione: quelli **navigabili** restano, perche' un membro record che non si sostituisce
4547
+ * puo' avere campi aggiornabili, e nasconderlo chiuderebbe la strada a `Richiesta.Cliente.Email`.
4548
+ */
4211
4549
  available = computed(() => {
4212
- const all = this.members();
4550
+ const entries = this.tail()?.level.entries ?? [];
4213
4551
  if (this.usage() !== 'writable') {
4214
- return all;
4552
+ return entries;
4215
4553
  }
4216
- return all.filter((member) => member.isWritable !== false);
4554
+ return entries.filter((entry) => entry.isWritable || !!entry.next);
4217
4555
  }, ...(ngDevMode ? [{ debugName: "available" }] : []));
4218
4556
  options = computed(() => {
4219
- const needle = (this.query() ?? '').trim().toLowerCase();
4557
+ const needle = (this.tail()?.segment ?? '')?.trim().toLowerCase();
4220
4558
  const all = this.available();
4221
4559
  if (!needle) {
4222
4560
  return all;
4223
4561
  }
4224
- return all.filter((member) => `${member.name} ${member.label ?? ''}`.toLowerCase().includes(needle));
4562
+ return all.filter((entry) => `${entry.name} ${entry.label ?? ''}`.toLowerCase().includes(needle));
4225
4563
  }, ...(ngDevMode ? [{ debugName: "options" }] : []));
4226
4564
  /**
4227
- * Come chiamare la classe: la `label` la porta l'involucro della descrizione, ed e' il motivo
4228
- * per cui l'involucro esiste (§4.7.1). Assente, si mostra il nome della classe.
4565
+ * Come chiamare la tappa: la `label` la porta l'involucro della descrizione, ed e' il motivo per
4566
+ * cui l'involucro esiste (§4.7.1). Assente, si mostra il nome della classe o dell'entita'.
4229
4567
  */
4230
- classLabel = computed(() => this.result().label || this.className() || '', ...(ngDevMode ? [{ debugName: "classLabel" }] : []));
4231
- /** Elenco autorevole: solo qui un membro fuori elenco e' un errore (§4.7.1). */
4232
- hasCatalog = computed(() => this.result().status === 'declared', ...(ngDevMode ? [{ debugName: "hasCatalog" }] : []));
4233
- /** 404: la classe non e' registrata. Il nome del membro non c'e' modo di indovinarlo. */
4234
- isClassUnknown = computed(() => this.result().status === 'unknown-class', ...(ngDevMode ? [{ debugName: "isClassUnknown" }] : []));
4568
+ classLabel = computed(() => pathContainerLabel(this.tail()?.level) || this.className() || '', ...(ngDevMode ? [{ debugName: "classLabel" }] : []));
4569
+ /** `true` se la tappa in cui si scrive ha un elenco **autorevole** (§4.7.1). */
4570
+ hasCatalog = computed(() => this.tail()?.level.status === 'declared', ...(ngDevMode ? [{ debugName: "hasCatalog" }] : []));
4571
+ /** Nell'elenco dei campi di un'entita' il titolo lo dice: non sono membri di una classe. */
4572
+ isObjectLevel = computed(() => this.tail()?.level.container.kind === 'object', ...(ngDevMode ? [{ debugName: "isObjectLevel" }] : []));
4573
+ rootStatus = computed(() => this.resolution()?.levels[0]?.status, ...(ngDevMode ? [{ debugName: "rootStatus" }] : []));
4574
+ /** 404 sulla classe di partenza: il nome del membro non c'e' modo di indovinarlo. */
4575
+ isClassUnknown = computed(() => this.rootStatus() === 'unknown-container', ...(ngDevMode ? [{ debugName: "isClassUnknown" }] : []));
4235
4576
  /** Classe valida, membri non dichiarati: si digita, e non si segnala niente. */
4236
- isUndeclared = computed(() => this.result().status === 'undeclared', ...(ngDevMode ? [{ debugName: "isUndeclared" }] : []));
4237
- /** Il primo segmento: e' l'unico che si puo' verificare (§4.7). */
4238
- firstSegment = computed(() => {
4239
- const value = (this.value() ?? '').trim();
4240
- const dot = value.indexOf('.');
4241
- return dot < 0 ? value : value.slice(0, dot);
4242
- }, ...(ngDevMode ? [{ debugName: "firstSegment" }] : []));
4243
- /** `true` se il valore naviga dentro un membro: oltre il primo segmento non si verifica. */
4244
- isNestedPath = computed(() => (this.value() ?? '').includes('.'), ...(ngDevMode ? [{ debugName: "isNestedPath" }] : []));
4245
- declared = computed(() => {
4246
- const segment = this.firstSegment();
4247
- return segment ? this.members().find((member) => member.name === segment) : undefined;
4248
- }, ...(ngDevMode ? [{ debugName: "declared" }] : []));
4249
- isUnknown = computed(() => {
4250
- if (!this.firstSegment() || !this.hasCatalog()) {
4251
- return false;
4252
- }
4253
- return !this.declared();
4254
- }, ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
4255
- /** Il messaggio elenca i disponibili: dire solo "non esiste" non aiuta a correggere. */
4577
+ isUndeclared = computed(() => this.rootStatus() === 'undeclared', ...(ngDevMode ? [{ debugName: "isUndeclared" }] : []));
4578
+ /** Catalogo assente per un motivo che non riguarda la classe: "non lo so". */
4579
+ loadErrored = computed(() => this.rootStatus() === 'unknown-catalog' && !!this.className(), ...(ngDevMode ? [{ debugName: "loadErrored" }] : []));
4580
+ isUnknown = computed(() => this.resolution()?.status === 'unknown', ...(ngDevMode ? [{ debugName: "isUnknown" }] : []));
4581
+ /** §4.4, §4.7.1 — la catena si e' interrotta: avviso, non errore. */
4582
+ isNotVerifiable = computed(() => this.resolution()?.status === 'unverifiable' && this.rootStatus() === 'declared', ...(ngDevMode ? [{ debugName: "isNotVerifiable" }] : []));
4583
+ /**
4584
+ * Il messaggio elenca i disponibili: dire solo "non esiste" non aiuta a correggere. Su un
4585
+ * percorso annidato il segmento che manca e' di **quella** tappa, e le parole cambiano con essa
4586
+ * un campo di un'entita' non e' un membro di una classe.
4587
+ */
4256
4588
  unknownMessage = computed(() => {
4257
- const names = this.available()
4258
- .map((member) => member.name)
4259
- .join(', ');
4260
- const prefix = `«${this.firstSegment()}» non e’ un membro di ${this.className()} (STRUCTURE_MEMBER_UNKNOWN).`;
4261
- return names ? `${prefix} Disponibili: ${names}.` : prefix;
4589
+ const resolution = this.resolution();
4590
+ if (!resolution) {
4591
+ return '';
4592
+ }
4593
+ const segment = resolution.stoppedAt?.segment ?? '';
4594
+ const names = pathAvailableNames(resolution).join(', ');
4595
+ const level = resolution.levels[resolution.levels.length - 1];
4596
+ const what = level?.container.kind === 'object'
4597
+ ? `«${segment}» non e’ un campo di ${level.container.name} (FIELD_UNKNOWN).`
4598
+ : `«${segment}» non e’ un membro di ${level?.container.name} (STRUCTURE_MEMBER_UNKNOWN).`;
4599
+ return names ? `${what} Disponibili: ${names}.` : what;
4262
4600
  }, ...(ngDevMode ? [{ debugName: "unknownMessage" }] : []));
4601
+ notVerifiableMessage = computed(() => {
4602
+ const resolution = this.resolution();
4603
+ return resolution ? pathNotVerifiableMessage(resolution) : '';
4604
+ }, ...(ngDevMode ? [{ debugName: "notVerifiableMessage" }] : []));
4263
4605
  /**
4264
- * Un membro che esiste ma non e' scrivibile, scelto dove serve una destinazione: capita su un
4265
- * documento scritto altrove, e va detto qui invece di lasciarlo scoprire dall'attivazione.
4606
+ * Un segmento che esiste ma non e' scrivibile, scelto dove serve una destinazione: capita su un
4607
+ * documento scritto altrove, e va detto qui invece di lasciarlo scoprire dall'attivazione. Su un
4608
+ * campo di record "scrivibile" vuol dire `createable` **oppure** `updateable` (§5.7).
4266
4609
  */
4267
4610
  isNotWritable = computed(() => {
4268
4611
  if (this.usage() !== 'writable') {
4269
4612
  return false;
4270
4613
  }
4271
- return this.declared()?.isWritable === false;
4614
+ const last = this.resolution()?.last;
4615
+ return !!last && !last.isWritable;
4272
4616
  }, ...(ngDevMode ? [{ debugName: "isNotWritable" }] : []));
4617
+ notWritableMessage = computed(() => {
4618
+ const resolution = this.resolution();
4619
+ const level = resolution?.levels[resolution.levels.length - 1];
4620
+ return level?.container.kind === 'object'
4621
+ ? 'Questo campo non si scrive ne’ creando ne’ aggiornando un record: assegnarlo e’ TARGET_NOT_WRITABLE.'
4622
+ : 'Questo membro e’ calcolato: si legge, ma assegnarlo e’ TARGET_NOT_WRITABLE.';
4623
+ }, ...(ngDevMode ? [{ debugName: "notWritableMessage" }] : []));
4273
4624
  describeValue = computed(() => {
4274
- const member = this.declared();
4275
- return member && !this.isNestedPath() ? this.describe(member) || null : null;
4625
+ const last = this.resolution()?.last;
4626
+ return last ? describePathEntry(last, { showWritability: this.usage() === 'writable' }) || null : null;
4276
4627
  }, ...(ngDevMode ? [{ debugName: "describeValue" }] : []));
4277
- /** Catalogo assente per un motivo che non riguarda la classe: "non lo so". */
4278
- loadErrored = computed(() => this.result().status === 'unknown-catalog' && !!this.className(), ...(ngDevMode ? [{ debugName: "loadErrored" }] : []));
4279
4628
  open() {
4280
4629
  if (this.disabled()) {
4281
4630
  return;
@@ -4292,36 +4641,39 @@ class StructureMemberPickerComponent {
4292
4641
  onInput(text) {
4293
4642
  this.query.set(text);
4294
4643
  this.open();
4295
- this.valueChange.emit(text.trim() ? text.trim() : undefined);
4644
+ this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
4296
4645
  }
4297
- choose(member) {
4298
- this.valueChange.emit(member.name);
4646
+ choose(entry) {
4647
+ this.valueChange.emit(`${this.prefix()}${entry.name}`);
4299
4648
  this.close();
4300
4649
  }
4650
+ /**
4651
+ * Scende dentro un membro composto invece di sceglierlo: e' l'equivalente di scrivere il punto, e
4652
+ * infatti scrive il punto. Serve perche' `Richiesta.Cliente` puo' essere la destinazione che si
4653
+ * vuole **oppure** la tappa da cui continuare.
4654
+ */
4655
+ descend(entry) {
4656
+ const path = `${this.prefix()}${entry.name}.`;
4657
+ this.query.set(path);
4658
+ this.valueChange.emit(path);
4659
+ this.open();
4660
+ }
4661
+ canDescend(entry) {
4662
+ return !!entry.next;
4663
+ }
4301
4664
  clear() {
4302
4665
  this.valueChange.emit(undefined);
4303
4666
  this.close();
4304
4667
  }
4305
- describe(member) {
4306
- const parts = [];
4307
- if (member.label && member.label !== member.name) {
4308
- parts.push(member.label);
4309
- }
4310
- if (member.dataType) {
4311
- const type = member.isCollection ? `${member.dataType}[]` : member.dataType;
4312
- parts.push(member.objectType ? `${type} · ${member.objectType}` : type);
4313
- }
4314
- if (member.isWritable === false) {
4315
- parts.push('sola lettura');
4316
- }
4317
- return parts.join(' · ');
4668
+ describe(entry) {
4669
+ return describePathEntry(entry, { showWritability: this.usage() === 'writable' });
4318
4670
  }
4319
4671
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: StructureMemberPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4320
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: StructureMemberPickerComponent, isStandalone: true, selector: "fb-structure-member-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i membri disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isClassUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n La classe {{ className() }} non e\u2019 registrata (STRUCTURE_TYPE_UNKNOWN): correggi la classe,\r\n non il membro.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ unknownMessage() }}</p>\r\n } @else if (isNotWritable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n Questo membro e\u2019 calcolato: si legge, ma assegnarlo e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n } @else if (isNestedPath()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Percorso annidato: viene verificato solo il primo segmento, il resto si scopre eseguendo.\r\n </p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (isUndeclared()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n {{ classLabel() }} non dichiara i suoi membri: scrivi il nome a mano, non e\u2019 verificabile.\r\n </p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei membri non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (className()) {\r\n <p class=\"fb-pick__group\">Membri di {{ classLabel() }}</p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!className()) {\r\n Scegli prima una classe.\r\n } @else if (hasCatalog()) {\r\n Nessun membro corrisponde.\r\n } @else if (isClassUnknown()) {\r\n Classe non registrata: nessun membro da proporre.\r\n } @else {\r\n Membri non dichiarati: scrivi il nome del membro.\r\n }\r\n </p>\r\n }\r\n @for (member of options(); track member.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"member.name === value()\"\r\n (click)=\"choose(member)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ member.name }}</span>\r\n @if (describe(member)) {\r\n <span class=\"fb-pick__meta\">{{ describe(member) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4672
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: StructureMemberPickerComponent, isStandalone: true, selector: "fb-structure-member-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i membri disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isClassUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n La classe {{ className() }} non e\u2019 registrata (STRUCTURE_TYPE_UNKNOWN): correggi la classe,\r\n non il membro.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ unknownMessage() }}</p>\r\n } @else if (isNotWritable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ notWritableMessage() }}</p>\r\n } @else if (isUndeclared()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n {{ classLabel() }} non dichiara i suoi membri: scrivi il nome a mano, non e\u2019 verificabile.\r\n </p>\r\n } @else if (isNotVerifiable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ notVerifiableMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei membri non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (className()) {\r\n <p class=\"fb-pick__group\">\r\n {{ isObjectLevel() ? 'Campi di ' : 'Membri di ' }}{{ classLabel() }}\r\n </p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!className()) {\r\n Scegli prima una classe.\r\n } @else if (hasCatalog()) {\r\n Nessun membro corrisponde.\r\n } @else if (isClassUnknown()) {\r\n Classe non registrata: nessun membro da proporre.\r\n } @else {\r\n Membri non dichiarati: scrivi il nome del membro.\r\n }\r\n </p>\r\n }\r\n @for (entry of options(); track entry.name) {\r\n <div class=\"fb-pick__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"prefix() + entry.name === value()\"\r\n (click)=\"choose(entry)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ entry.name }}</span>\r\n @if (describe(entry)) {\r\n <span class=\"fb-pick__meta\">{{ describe(entry) }}</span>\r\n }\r\n </button>\r\n @if (canDescend(entry)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__into\"\r\n [attr.aria-label]=\"'Entra in ' + entry.name\"\r\n title=\"Entra nel membro composto\"\r\n (click)=\"descend(entry)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4321
4673
  }
4322
4674
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: StructureMemberPickerComponent, decorators: [{
4323
4675
  type: Component,
4324
- args: [{ selector: 'fb-structure-member-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i membri disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isClassUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n La classe {{ className() }} non e\u2019 registrata (STRUCTURE_TYPE_UNKNOWN): correggi la classe,\r\n non il membro.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ unknownMessage() }}</p>\r\n } @else if (isNotWritable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n Questo membro e\u2019 calcolato: si legge, ma assegnarlo e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n } @else if (isNestedPath()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Percorso annidato: viene verificato solo il primo segmento, il resto si scopre eseguendo.\r\n </p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (isUndeclared()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n {{ classLabel() }} non dichiara i suoi membri: scrivi il nome a mano, non e\u2019 verificabile.\r\n </p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei membri non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (className()) {\r\n <p class=\"fb-pick__group\">Membri di {{ classLabel() }}</p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!className()) {\r\n Scegli prima una classe.\r\n } @else if (hasCatalog()) {\r\n Nessun membro corrisponde.\r\n } @else if (isClassUnknown()) {\r\n Classe non registrata: nessun membro da proporre.\r\n } @else {\r\n Membri non dichiarati: scrivi il nome del membro.\r\n }\r\n </p>\r\n }\r\n @for (member of options(); track member.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"member.name === value()\"\r\n (click)=\"choose(member)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ member.name }}</span>\r\n @if (describe(member)) {\r\n <span class=\"fb-pick__meta\">{{ describe(member) }}</span>\r\n }\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4676
+ args: [{ selector: 'fb-structure-member-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-pick\" [class.fb-pick--open]=\"isOpen()\">\r\n <div class=\"fb-pick__control\">\r\n <input\r\n class=\"fb-pick__input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n [value]=\"text()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n (keydown.escape)=\"close()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i membri disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-pick__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (isClassUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">\r\n La classe {{ className() }} non e\u2019 registrata (STRUCTURE_TYPE_UNKNOWN): correggi la classe,\r\n non il membro.\r\n </p>\r\n } @else if (isUnknown()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ unknownMessage() }}</p>\r\n } @else if (isNotWritable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--error\">{{ notWritableMessage() }}</p>\r\n } @else if (isUndeclared()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n {{ classLabel() }} non dichiara i suoi membri: scrivi il nome a mano, non e\u2019 verificabile.\r\n </p>\r\n } @else if (isNotVerifiable()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">{{ notVerifiableMessage() }}</p>\r\n } @else if (describeValue()) {\r\n <p class=\"fb-pick__hint\">{{ describeValue() }}</p>\r\n } @else if (loadErrored()) {\r\n <p class=\"fb-pick__hint fb-pick__hint--warn\">\r\n Elenco dei membri non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-pick__panel\" role=\"listbox\">\r\n @if (className()) {\r\n <p class=\"fb-pick__group\">\r\n {{ isObjectLevel() ? 'Campi di ' : 'Membri di ' }}{{ classLabel() }}\r\n </p>\r\n }\r\n @if (options().length === 0) {\r\n <p class=\"fb-pick__empty\">\r\n @if (!className()) {\r\n Scegli prima una classe.\r\n } @else if (hasCatalog()) {\r\n Nessun membro corrisponde.\r\n } @else if (isClassUnknown()) {\r\n Classe non registrata: nessun membro da proporre.\r\n } @else {\r\n Membri non dichiarati: scrivi il nome del membro.\r\n }\r\n </p>\r\n }\r\n @for (entry of options(); track entry.name) {\r\n <div class=\"fb-pick__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"prefix() + entry.name === value()\"\r\n (click)=\"choose(entry)\"\r\n >\r\n <span class=\"fb-pick__name\">{{ entry.name }}</span>\r\n @if (describe(entry)) {\r\n <span class=\"fb-pick__meta\">{{ describe(entry) }}</span>\r\n }\r\n </button>\r\n @if (canDescend(entry)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-pick__into\"\r\n [attr.aria-label]=\"'Entra in ' + entry.name\"\r\n title=\"Entra nel membro composto\"\r\n (click)=\"descend(entry)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-pick__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-pick{position:relative}.fb-pick__control{display:flex;align-items:stretch;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-pick--open .fb-pick__control{border-color:var(--fb-accent, #2f6feb)}.fb-pick__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-pick__input:focus-visible{outline:none}.fb-pick__input--mono,.fb-pick__name--mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-pick__toggle,.fb-pick__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-pick__toggle:hover,.fb-pick__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-pick__hint--warn{color:var(--fb-warning, #b7791f)}.fb-pick__hint--error{color:var(--fb-error, #c9372c)}.fb-pick__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-pick__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-pick__option:hover,.fb-pick__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-pick__row{display:flex;align-items:stretch;gap:2px}.fb-pick__row .fb-pick__option{flex:1;min-width:0}.fb-pick__into{flex:0 0 auto;padding:0 7px;border:0;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:13px;cursor:pointer}.fb-pick__into:hover{background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-accent, #2f6feb)}.fb-pick__name{font-size:12px}.fb-pick__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-pick__group{margin:2px 4px 4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #667085)}.fb-pick__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-pick__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
4325
4677
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], usage: [{ type: i0.Input, args: [{ isSignal: true, alias: "usage", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4326
4678
 
4327
4679
  /**
@@ -4796,7 +5148,7 @@ class ConditionEditorComponent {
4796
5148
  // Assente = tutte le condizioni devono essere vere (§4.3).
4797
5149
  return 'and';
4798
5150
  }
4799
- const normalized = logic.trim().toLowerCase();
5151
+ const normalized = logic?.trim().toLowerCase();
4800
5152
  if (normalized === 'and') {
4801
5153
  return 'and';
4802
5154
  }
@@ -5061,7 +5413,7 @@ class RecordFilterEditorComponent {
5061
5413
  if (!logic) {
5062
5414
  return 'and';
5063
5415
  }
5064
- const normalized = logic.trim().toLowerCase();
5416
+ const normalized = logic?.trim().toLowerCase();
5065
5417
  if (normalized === 'and') {
5066
5418
  return 'and';
5067
5419
  }
@@ -9422,5 +9774,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
9422
9774
  * Generated bundle index. Do not edit.
9423
9775
  */
9424
9776
 
9425
- export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_RESOURCE_COLLECTIONS, FieldAssignmentEditorComponent, FieldPickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, HttpFlowBuilderApi, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, areTypesComparable, canvasNodeId, checkConditionLogic, checkFlowName, elementIcon, emptyFlowDefinition, flowNodeWidth, flowNodeWidthClass, isCustomConditionLogic, isGlobalReference, isNumericType, isTypeCheckedOperator, isValidFlowName, moveCondition, outletByKey, outletsOf, parseCanvasNodeId, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, referenceRoot, remapConditionLogic, removeCondition, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, uniqueFlowName, variantFieldOf, variantOf, variantPresetOf };
9777
+ export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_RESOURCE_COLLECTIONS, FieldAssignmentEditorComponent, FieldPickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, HttpFlowBuilderApi, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, areTypesComparable, canvasNodeId, checkConditionLogic, checkFlowName, describePathEntry, elementIcon, emptyFlowDefinition, flowNodeWidth, flowNodeWidthClass, isCustomConditionLogic, isGlobalReference, isNumericType, isTypeCheckedOperator, isValidFlowName, loadPathLevel, moveCondition, navigatePath, outletByKey, outletsOf, parseCanvasNodeId, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathNotVerifiableMessage, referenceRoot, remapConditionLogic, removeCondition, resolvePath, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, uniqueFlowName, variantFieldOf, variantOf, variantPresetOf };
9426
9778
  //# sourceMappingURL=esfaenza-flow-builder.mjs.map