@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.
- package/README.md +29 -11
- package/fesm2022/esfaenza-flow-builder.mjs +621 -269
- package/fesm2022/esfaenza-flow-builder.mjs.map +1 -1
- package/index.d.ts +433 -190
- package/package.json +1 -1
|
@@ -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,
|
|
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)
|
|
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 ?? '')
|
|
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 ?? '')
|
|
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 ?? '')
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()
|
|
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
|
-
*
|
|
3390
|
-
*
|
|
3391
|
-
*
|
|
3392
|
-
*
|
|
3393
|
-
*
|
|
3394
|
-
*
|
|
3395
|
-
*
|
|
3396
|
-
*
|
|
3397
|
-
* `
|
|
3398
|
-
* e
|
|
3399
|
-
*
|
|
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
|
-
*
|
|
3493
|
-
*
|
|
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
|
-
|
|
3496
|
-
const
|
|
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
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
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
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
.filter((
|
|
3514
|
-
.map((
|
|
3515
|
-
name: `${
|
|
3516
|
-
label:
|
|
3517
|
-
kind
|
|
3518
|
-
dataType:
|
|
3519
|
-
isCollection:
|
|
3520
|
-
objectType:
|
|
3521
|
-
isWritable:
|
|
3522
|
-
description:
|
|
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: "
|
|
3791
|
+
}, ...(ngDevMode ? [{ debugName: "pathReferences" }] : []));
|
|
3525
3792
|
options = computed(() => {
|
|
3526
|
-
const needle = this.query()
|
|
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.
|
|
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
|
|
3582
|
-
*
|
|
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`:
|
|
3586
|
-
* - `memberUnknown`:
|
|
3587
|
-
* `
|
|
3588
|
-
* - `memberNotWritable`: il
|
|
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() ?? '')
|
|
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
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
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
|
|
3633
|
-
case 'member':
|
|
3634
|
-
|
|
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
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
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() ?? '')
|
|
3741
|
-
if (value !== (untracked(this.query) ?? '')
|
|
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() ?? '')
|
|
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() ?? '')
|
|
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() ?? '')
|
|
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
|
|
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
|
-
*
|
|
3833
|
-
*
|
|
3834
|
-
*
|
|
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() ?? '')
|
|
3873
|
-
if (value !== (untracked(this.query) ?? '')
|
|
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
|
|
3881
|
-
|
|
4188
|
+
const tail = this.tail();
|
|
4189
|
+
if (!tail) {
|
|
4190
|
+
return [];
|
|
4191
|
+
}
|
|
4192
|
+
const needle = tail.segment?.trim().toLowerCase();
|
|
3882
4193
|
if (!needle) {
|
|
3883
|
-
return
|
|
4194
|
+
return tail.level.entries;
|
|
3884
4195
|
}
|
|
3885
|
-
return
|
|
4196
|
+
return tail.level.entries.filter((entry) => `${entry.name} ${entry.label ?? ''}`.toLowerCase().includes(needle));
|
|
3886
4197
|
}, ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
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
|
|
4221
|
+
return `${what} non e’ fra i campi filtrabili${where}.${suffix}`;
|
|
3902
4222
|
case 'sortable':
|
|
3903
|
-
return
|
|
4223
|
+
return `${what} non e’ fra i campi su cui si puo’ ordinare${where}.${suffix}`;
|
|
3904
4224
|
case 'updateable':
|
|
3905
|
-
return
|
|
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
|
|
4229
|
+
return `${what} non e’ fra i campi valorizzabili alla creazione${where}.${suffix}`;
|
|
3910
4230
|
default:
|
|
3911
|
-
return
|
|
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
|
|
3916
|
-
|
|
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
|
-
|
|
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
|
|
4260
|
+
this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
|
|
3937
4261
|
}
|
|
3938
|
-
choose(
|
|
3939
|
-
this.valueChange.emit(
|
|
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(
|
|
3947
|
-
|
|
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 (
|
|
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 (
|
|
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() ?? '')
|
|
4006
|
-
if (value !== (untracked(this.query) ?? '')
|
|
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() ?? '')
|
|
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() ?? '')
|
|
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() ?? '')
|
|
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
|
|
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.
|
|
4161
|
-
*
|
|
4162
|
-
*
|
|
4163
|
-
*
|
|
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() ?? '')
|
|
4204
|
-
if (value !== (untracked(this.query) ?? '')
|
|
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
|
-
/**
|
|
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
|
|
4550
|
+
const entries = this.tail()?.level.entries ?? [];
|
|
4213
4551
|
if (this.usage() !== 'writable') {
|
|
4214
|
-
return
|
|
4552
|
+
return entries;
|
|
4215
4553
|
}
|
|
4216
|
-
return
|
|
4554
|
+
return entries.filter((entry) => entry.isWritable || !!entry.next);
|
|
4217
4555
|
}, ...(ngDevMode ? [{ debugName: "available" }] : []));
|
|
4218
4556
|
options = computed(() => {
|
|
4219
|
-
const needle = (this.
|
|
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((
|
|
4562
|
+
return all.filter((entry) => `${entry.name} ${entry.label ?? ''}`.toLowerCase().includes(needle));
|
|
4225
4563
|
}, ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
4226
4564
|
/**
|
|
4227
|
-
* Come chiamare la
|
|
4228
|
-
*
|
|
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.
|
|
4231
|
-
/**
|
|
4232
|
-
hasCatalog = computed(() => this.
|
|
4233
|
-
/**
|
|
4234
|
-
|
|
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.
|
|
4237
|
-
/**
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
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
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
4275
|
-
return
|
|
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
|
|
4644
|
+
this.valueChange.emit(text?.trim() ? text?.trim() : undefined);
|
|
4296
4645
|
}
|
|
4297
|
-
choose(
|
|
4298
|
-
this.valueChange.emit(
|
|
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(
|
|
4306
|
-
|
|
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\"
|
|
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\"
|
|
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
|
|
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
|
|
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
|