@esfaenza/flow-builder 20.3.43 → 20.3.45
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 +70 -7
- package/fesm2022/esfaenza-flow-builder.mjs +808 -172
- package/fesm2022/esfaenza-flow-builder.mjs.map +1 -1
- package/index.d.ts +355 -23
- package/package.json +1 -1
|
@@ -1416,6 +1416,12 @@ const ORCHESTRATION_CONDITION_OUTPUT = 'isOrchestrationConditionMet';
|
|
|
1416
1416
|
* del flow** (§5.2). I loro nomi vivono nello stesso spazio dei nomi di node e risorse (§3.3),
|
|
1417
1417
|
* quindi `usedNames` dello store deve comprenderli — un campo omonimo di una variabile e'
|
|
1418
1418
|
* `NAME_DUPLICATED`, non una stranezza tollerata.
|
|
1419
|
+
*
|
|
1420
|
+
* L'unica eccezione a quest'ultima e' la `RepeatingSection`, e le due meta' vanno tenute distinte perche'
|
|
1421
|
+
* e' facile appiattirle: i campi **dentro** una riga non sono risorse (il loro nome non individua alcun
|
|
1422
|
+
* valore, perche' la riga esiste una volta per elemento della collection) ma un nome unico ce l'hanno
|
|
1423
|
+
* comunque. Quindi `screenFieldNames` li comprende — `NAME_DUPLICATED` vale — mentre
|
|
1424
|
+
* {@link resourceFieldsOf} no: proporli fra i riferimenti e' `SCREEN_FIELD_IN_REPEATER`.
|
|
1419
1425
|
*/
|
|
1420
1426
|
/** Uguaglianza fra percorsi: due array diversi con gli stessi indici sono lo stesso campo. */
|
|
1421
1427
|
function samePath(a, b) {
|
|
@@ -1483,7 +1489,9 @@ function allFieldsOf(screen) {
|
|
|
1483
1489
|
*
|
|
1484
1490
|
* Contenitori compresi: un `Region` non e' una risorsa — non raccoglie un valore — ma il suo
|
|
1485
1491
|
* nome resta un nome, e due campi omonimi nella stessa schermata sono comunque un problema.
|
|
1486
|
-
*
|
|
1492
|
+
* Ci sono anche i campi **dentro una riga** di una sezione ripetibile, che risorse non sono ma un nome
|
|
1493
|
+
* ce l'hanno lo stesso: e' l'indirizzo con cui il frontend rende il campo, e due omonimi lo renderebbero
|
|
1494
|
+
* ambiguo (§5.2). Chi vuole i **soli** riferimenti proponibili usa {@link resourceFieldsOf}.
|
|
1487
1495
|
*/
|
|
1488
1496
|
function screenFieldNames(definition) {
|
|
1489
1497
|
const names = [];
|
|
@@ -1526,11 +1534,80 @@ function screenActionNames(definition) {
|
|
|
1526
1534
|
* i suoi output. Il flag `storesValue` arriva dal dizionario, che e' l'unica fonte autoritativa:
|
|
1527
1535
|
* senza la voce del dizionario si risponde `false`, cioe' "non lo so" — proporre un riferimento
|
|
1528
1536
|
* che il backend non conosce e' peggio che non proporlo.
|
|
1537
|
+
*
|
|
1538
|
+
* Guarda il **solo campo**, e non basta: un campo dentro una `RepeatingSection` raccoglie un valore ma
|
|
1539
|
+
* non e' una risorsa, perche' esiste in una riga per ogni elemento della collection. Quella domanda ha
|
|
1540
|
+
* bisogno del contesto, e la risponde {@link resourceFieldsOf}.
|
|
1529
1541
|
*/
|
|
1530
1542
|
function isFieldResource(field, entry) {
|
|
1531
1543
|
return !!field?.name && entry?.storesValue === true;
|
|
1532
1544
|
}
|
|
1533
1545
|
// ---------------------------------------------------------------------------
|
|
1546
|
+
// §5.2 — sezioni ripetibili: cosa cambia dentro una riga
|
|
1547
|
+
// ---------------------------------------------------------------------------
|
|
1548
|
+
/**
|
|
1549
|
+
* §5.2 — i campi dichiarati dentro una `RepeatingSection` sono il **modello di una riga**, e la riga
|
|
1550
|
+
* esiste una volta per elemento della collection: il loro nome non individua alcun valore.
|
|
1551
|
+
*
|
|
1552
|
+
* Da qui discende tutto cio' che l'editor fa di diverso lì dentro. Non sono risorse del flow, non si
|
|
1553
|
+
* propongono fra i riferimenti, non innescano screen action e non sono destinazioni: nominarli e'
|
|
1554
|
+
* `SCREEN_FIELD_IN_REPEATER`. Dentro una riga si nomina il **cursore** (`itemReference`), che e' una
|
|
1555
|
+
* variabile vera — `PuntoCorrente.PodId`, mai `Luce_POD`.
|
|
1556
|
+
*
|
|
1557
|
+
* Cio' che invece **non** cambia: il nome resta unico, perche' e' l'indirizzo con cui il frontend rende
|
|
1558
|
+
* il campo, quindi un omonimo di una variabile e' `NAME_DUPLICATED` come sempre e `screenFieldNames` li
|
|
1559
|
+
* comprende tutti.
|
|
1560
|
+
*/
|
|
1561
|
+
function repeatedFieldNames(definition, typeOf) {
|
|
1562
|
+
const names = new Set();
|
|
1563
|
+
for (const screen of definition?.dynamicScreens ?? []) {
|
|
1564
|
+
walkInside(screen.fields, typeOf, false, (field, inside) => {
|
|
1565
|
+
if (inside && field.name) {
|
|
1566
|
+
names.add(field.name);
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
return names;
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* I campi di una schermata che sono **risorse del flow**: raccolgono un valore e non stanno dentro una
|
|
1574
|
+
* riga. Sono gli unici da proporre come riferimento, come innesco di una screen action e come
|
|
1575
|
+
* destinazione dei suoi output.
|
|
1576
|
+
*/
|
|
1577
|
+
function resourceFieldsOf(screen, typeOf) {
|
|
1578
|
+
const fields = [];
|
|
1579
|
+
walkInside(screen?.fields, typeOf, false, (field, inside) => {
|
|
1580
|
+
if (!inside && isFieldResource(field, typeOf(field))) {
|
|
1581
|
+
fields.push(field);
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
return fields;
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* La sezione ripetibile che **contiene** il campo a un percorso, se c'e'. Un antenato stretto: la
|
|
1588
|
+
* sezione stessa non sta dentro se stessa, ed e' la differenza fra «configuro la sezione» e «configuro
|
|
1589
|
+
* un campo di una riga», che sono due pannelli diversi.
|
|
1590
|
+
*/
|
|
1591
|
+
function repeatingSectionOf(screen, path, typeOf) {
|
|
1592
|
+
if (!screen || !path?.length) {
|
|
1593
|
+
return undefined;
|
|
1594
|
+
}
|
|
1595
|
+
for (let depth = 1; depth < path.length; depth++) {
|
|
1596
|
+
const ancestor = fieldAt(screen.fields, path.slice(0, depth));
|
|
1597
|
+
if (ancestor && typeOf(ancestor)?.isRepeatingSection === true) {
|
|
1598
|
+
return ancestor;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return undefined;
|
|
1602
|
+
}
|
|
1603
|
+
/** Cammina l'albero portandosi dietro «sto dentro una riga»: le sezioni **non si annidano** (§5.2). */
|
|
1604
|
+
function walkInside(fields, typeOf, inside, visit) {
|
|
1605
|
+
for (const field of fields ?? []) {
|
|
1606
|
+
visit(field, inside);
|
|
1607
|
+
walkInside(field.fields, typeOf, inside || typeOf(field)?.isRepeatingSection === true, visit);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
// ---------------------------------------------------------------------------
|
|
1534
1611
|
// Mutazioni (lavorano sulla copia di lavoro dello store)
|
|
1535
1612
|
// ---------------------------------------------------------------------------
|
|
1536
1613
|
/** Inserisce un campo dentro `parentPath` alla posizione indicata (in fondo se assente). */
|
|
@@ -2314,6 +2391,83 @@ function sameMembers(left, right) {
|
|
|
2314
2391
|
return first.length === right.length && first.every((name, index) => name === right[index]);
|
|
2315
2392
|
}
|
|
2316
2393
|
|
|
2394
|
+
/**
|
|
2395
|
+
* I **merge field** `{!...}` — FRONTEND.md §5.12.
|
|
2396
|
+
*
|
|
2397
|
+
* Un testo del flow puo' citare una risorsa dentro un'isola: `«Gentile {!Cliente.Nome},»`. E'
|
|
2398
|
+
* un uso a tutti gli effetti — il runtime lo risolve quando l'elemento scatta — ma non e' un
|
|
2399
|
+
* campo di riferimento, quindi chi cammina il documento non lo trova da se'.
|
|
2400
|
+
*
|
|
2401
|
+
* Questo modulo tiene le tre cose che servono a trovarlo, e sta **a parte** per una ragione
|
|
2402
|
+
* strutturale: le usano in due, il conteggio degli usi (`flow-references.ts`) e la riscrittura
|
|
2403
|
+
* della rinomina (`flow-clipboard.ts`), e quel secondo modulo e' già importato dal primo —
|
|
2404
|
+
* metterle lì dentro avrebbe chiuso un ciclo, metterle nel primo lo avrebbe aperto al contrario.
|
|
2405
|
+
* Un modulo foglia che nessuno dei due deve importare a sua volta e' l'unica forma senza cicli, e
|
|
2406
|
+
* ha comunque un senso proprio: la sintassi dell'isola e' del **contratto**, non del motore delle
|
|
2407
|
+
* formule (§6.3), quindi riconoscerla non e' un'approssimazione come interpretare un'espressione.
|
|
2408
|
+
*/
|
|
2409
|
+
/**
|
|
2410
|
+
* I campi che il contratto dichiara con i merge field.
|
|
2411
|
+
*
|
|
2412
|
+
* Sono quelli e solo quelli: `interviewLabel` (§3.1), il `text` di un text template (§4.6),
|
|
2413
|
+
* `fieldText` e `helpText` di uno screen (§5.2), il `choiceText` di una choice (§5.2) e
|
|
2414
|
+
* l'`errorMessage` di un Custom Error (§5.12).
|
|
2415
|
+
*
|
|
2416
|
+
* E' un **elenco** e non «tutte le stringhe», per la stessa ragione per cui i riferimenti hanno
|
|
2417
|
+
* il loro: `{!Totale}` scritto in un campo che il contratto non dichiara con merge field non
|
|
2418
|
+
* viene espanso da nessuno, e trattarlo come un uso direbbe «usata» di una risorsa che il runtime
|
|
2419
|
+
* non legge — cioe' farebbe tenere una variabile morta credendo di averne una viva. Con lo stesso
|
|
2420
|
+
* patto degli altri elenchi: se il contratto ne aggiunge uno, si aggiunge **qui**, una volta.
|
|
2421
|
+
*/
|
|
2422
|
+
const FLOW_MERGE_TEXT_FIELDS = new Set([
|
|
2423
|
+
'interviewLabel',
|
|
2424
|
+
'text',
|
|
2425
|
+
'fieldText',
|
|
2426
|
+
'helpText',
|
|
2427
|
+
'choiceText',
|
|
2428
|
+
'errorMessage',
|
|
2429
|
+
]);
|
|
2430
|
+
/** `true` se il testo contiene almeno un'isola aperta: e' il filtro piu' economico. */
|
|
2431
|
+
function hasMergeField(text) {
|
|
2432
|
+
return text.includes('{!');
|
|
2433
|
+
}
|
|
2434
|
+
/** Il contenuto delle isole `{!...}`, nell'ordine in cui compaiono. */
|
|
2435
|
+
function mergeFieldIslands(text) {
|
|
2436
|
+
const islands = [];
|
|
2437
|
+
const pattern = /\{!([^}]*)\}/g;
|
|
2438
|
+
let match;
|
|
2439
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
2440
|
+
islands.push(match[1]);
|
|
2441
|
+
}
|
|
2442
|
+
return islands;
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Riscrive un nome dentro le isole di un testo, lasciando intatto il resto.
|
|
2446
|
+
*
|
|
2447
|
+
* Tre cose che la regex deve rispettare, e sono le stesse che rendono corretta la riduzione alla
|
|
2448
|
+
* radice nel conteggio.
|
|
2449
|
+
*
|
|
2450
|
+
* Si sostituisce solo un **token intero**: in `{!Totale_Con_IVA}` il nome `Totale` non compare,
|
|
2451
|
+
* compare un altro identificatore che comincia allo stesso modo, e sostituirlo produrrebbe un
|
|
2452
|
+
* riferimento inventato.
|
|
2453
|
+
*
|
|
2454
|
+
* Il carattere precedente non puo' essere un **punto**: in `{!Ordine.Cliente}` il `Cliente` e' un
|
|
2455
|
+
* membro di `Ordine`, non la risorsa `Cliente`, ed e' l'errore che si fa piu' facilmente perche'
|
|
2456
|
+
* il testo da cercare c'e' davvero. Vale anche per il `$` — `{!$Record.Numero}` non e' una
|
|
2457
|
+
* risorsa del flow.
|
|
2458
|
+
*
|
|
2459
|
+
* E si tocca **solo** cio' che sta dentro `{!...}`: la stessa parola nella frase intorno e' una
|
|
2460
|
+
* parola, non un riferimento, e riscriverla cambierebbe un messaggio per l'utente.
|
|
2461
|
+
*/
|
|
2462
|
+
function rewriteMergeText(text, oldName, newName) {
|
|
2463
|
+
if (!hasMergeField(text)) {
|
|
2464
|
+
return text;
|
|
2465
|
+
}
|
|
2466
|
+
const escaped = oldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2467
|
+
const token = new RegExp(`(^|[^A-Za-z0-9_.$])(${escaped})(?![A-Za-z0-9_$])`, 'g');
|
|
2468
|
+
return text.replace(/\{![^}]*\}/g, (island) => island.replace(token, (_match, before) => `${before}${newName}`));
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2317
2471
|
/**
|
|
2318
2472
|
* Copia e incolla di **elementi** — la logica pura, senza Angular e senza storage.
|
|
2319
2473
|
*
|
|
@@ -2367,13 +2521,29 @@ const FLOW_REFERENCE_FIELDS = new Set([
|
|
|
2367
2521
|
'defaultSelectedChoiceReference',
|
|
2368
2522
|
'screenActionName',
|
|
2369
2523
|
'triggerFieldName',
|
|
2524
|
+
// §5.2 — il **cursore** di una sezione ripetibile: e' una variabile del flow come le altre, ed e'
|
|
2525
|
+
// il nome che i campi della riga citano (`PuntoCorrente.PodId`). Fuori da qui, rinominare quella
|
|
2526
|
+
// variabile lascerebbe la sezione puntata a un nome che non esiste e ogni riferimento dentro la
|
|
2527
|
+
// riga rotto — senza che il conteggio degli usi lo avesse annunciato. La `collectionReference`
|
|
2528
|
+
// sta già nell'elenco, perche' la usano anche Loop e dynamic choice set.
|
|
2529
|
+
'itemReference',
|
|
2370
2530
|
]);
|
|
2371
2531
|
/**
|
|
2372
|
-
* Riscrive ogni occorrenza di un nome nei campi che contengono riferimenti
|
|
2532
|
+
* Riscrive ogni occorrenza di un nome nei campi che contengono riferimenti **e nei merge field**.
|
|
2373
2533
|
*
|
|
2374
2534
|
* Cammina tutto cio' che gli si passa — un documento intero per la rinomina, il solo frammento
|
|
2375
2535
|
* copiato per l'incolla — perche' i riferimenti compaiono in una ventina di posti diversi (§4)
|
|
2376
2536
|
* e l'elenco dei posti sarebbe la cosa che si dimentica di aggiornare.
|
|
2537
|
+
*
|
|
2538
|
+
* I **merge field** ci sono entrati insieme al loro conteggio, e non e' un caso: mostrare «5 usi»
|
|
2539
|
+
* contando i testi e poi riscriverne solo tre significa lasciare due messaggi rotti proprio a chi
|
|
2540
|
+
* ha letto il numero per fidarsi della rinomina. Il testo intorno all'isola non si tocca — lì la
|
|
2541
|
+
* stessa parola e' una parola — e la regola sta in `flow-merge-text.ts`.
|
|
2542
|
+
*
|
|
2543
|
+
* Restano fuori le **formule**: la loro sintassi e' del motore (§6.3), e sostituire un
|
|
2544
|
+
* identificatore in un'espressione che non si interpreta vuol dire indovinare. Chi mostra il
|
|
2545
|
+
* conteggio lo dice, invece di far scoprire dopo che una formula punta a un nome che non c'e'
|
|
2546
|
+
* piu' (`describeNameUses` le tiene contate a parte proprio per questo).
|
|
2377
2547
|
*/
|
|
2378
2548
|
function rewriteReferences(root, oldName, newName) {
|
|
2379
2549
|
if (!oldName || !newName || oldName === newName) {
|
|
@@ -2399,6 +2569,10 @@ function rewriteReferences(root, oldName, newName) {
|
|
|
2399
2569
|
}
|
|
2400
2570
|
const record = value;
|
|
2401
2571
|
for (const [key, child] of Object.entries(record)) {
|
|
2572
|
+
if (FLOW_MERGE_TEXT_FIELDS.has(key) && typeof child === 'string') {
|
|
2573
|
+
record[key] = rewriteMergeText(child, oldName, newName);
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2402
2576
|
if (!FLOW_REFERENCE_FIELDS.has(key)) {
|
|
2403
2577
|
walk(child);
|
|
2404
2578
|
continue;
|
|
@@ -3734,6 +3908,15 @@ class FlowDictionaryStore {
|
|
|
3734
3908
|
screenFieldIsContainer(value) {
|
|
3735
3909
|
return this.screenFieldType(value)?.isContainer ?? false;
|
|
3736
3910
|
}
|
|
3911
|
+
/**
|
|
3912
|
+
* §5.2 — sezione **ripetibile**. Distinto da {@link screenFieldIsContainer} di proposito: contiene
|
|
3913
|
+
* campi, ma quelli che contiene sono il modello di una riga e i loro nomi **non** sono riferimenti
|
|
3914
|
+
* proponibili (`SCREEN_FIELD_IN_REPEATER`). E' il flag su cui si accende tutto cio' che l'editor fa di
|
|
3915
|
+
* diverso dentro una riga.
|
|
3916
|
+
*/
|
|
3917
|
+
screenFieldIsRepeatingSection(value) {
|
|
3918
|
+
return this.screenFieldType(value)?.isRepeatingSection ?? false;
|
|
3919
|
+
}
|
|
3737
3920
|
/** §5.2 — il campo si configura con `choiceReferences`. */
|
|
3738
3921
|
screenFieldAcceptsChoices(value) {
|
|
3739
3922
|
return this.screenFieldType(value)?.acceptsChoices ?? false;
|
|
@@ -6640,16 +6823,25 @@ function pathTypes(catalog, request) {
|
|
|
6640
6823
|
* `position: absolute` dentro il controllo, quindi `contains` basta e non serve registrare
|
|
6641
6824
|
* l'elenco. Un bersaglio che nel frattempo e' stato staccato dal DOM (`isConnected: false`)
|
|
6642
6825
|
* non si giudica: era dentro, e chiudere lì vorrebbe dire chiudere su un proprio gesto.
|
|
6826
|
+
*
|
|
6827
|
+
* L'host va bene finche' il componente **e'** il controllo, che e' il caso dei cinque picker.
|
|
6828
|
+
* Dove invece il controllo e' una parte di un componente piu' grande — la tendina dei codici nel
|
|
6829
|
+
* pannello dei problemi, che e' alto quanto il pannello — l'host e' il confine sbagliato: un clic
|
|
6830
|
+
* su una riga dei rilievi e' «altrove» per l'utente e «dentro» per `contains`, e la tendina
|
|
6831
|
+
* restava aperta. Per questo il confine si puo' passare (`boundary`), e resta l'host quando non
|
|
6832
|
+
* lo si passa.
|
|
6643
6833
|
*/
|
|
6644
|
-
function closeOnOutside(isOpen, close) {
|
|
6834
|
+
function closeOnOutside(isOpen, close, boundary) {
|
|
6645
6835
|
const host = inject(ElementRef).nativeElement;
|
|
6646
6836
|
const doc = inject(DOCUMENT);
|
|
6837
|
+
/** Il confine si rilegge a ogni gesto: un `viewChild` non e' risolto alla registrazione. */
|
|
6838
|
+
const edgeOf = () => boundary?.() ?? host;
|
|
6647
6839
|
const onPointerDown = (event) => {
|
|
6648
6840
|
const target = event.target;
|
|
6649
6841
|
if (!target || (target instanceof Node && !target.isConnected)) {
|
|
6650
6842
|
return;
|
|
6651
6843
|
}
|
|
6652
|
-
if (isOpen() && !
|
|
6844
|
+
if (isOpen() && !edgeOf().contains(target)) {
|
|
6653
6845
|
close();
|
|
6654
6846
|
}
|
|
6655
6847
|
};
|
|
@@ -6663,6 +6855,8 @@ function closeOnOutside(isOpen, close) {
|
|
|
6663
6855
|
* perdere il posto in cui si stava lavorando.
|
|
6664
6856
|
*/
|
|
6665
6857
|
const onKeyDown = (event) => {
|
|
6858
|
+
// `Esc` si accetta da tutto il componente e non dal solo controllo: il focus puo' essere
|
|
6859
|
+
// finito altrove nel pannello, e la via d'uscita da tastiera non deve dipendere da dov'e'.
|
|
6666
6860
|
if (event.key !== 'Escape' || !isOpen() || !host.contains(event.target)) {
|
|
6667
6861
|
return;
|
|
6668
6862
|
}
|
|
@@ -6754,6 +6948,16 @@ class ReferencePickerComponent {
|
|
|
6754
6948
|
* verrebbe accusato di non esistere.
|
|
6755
6949
|
*/
|
|
6756
6950
|
extraReferences = input([], ...(ngDevMode ? [{ debugName: "extraReferences" }] : []));
|
|
6951
|
+
/**
|
|
6952
|
+
* Le radici da proporre **per prime**. Oggi c'e' un chiamante solo: dentro una riga di sezione
|
|
6953
|
+
* ripetibile la strada normale e' il **cursore** (`PuntoCorrente.PodId`), e i campi della riga —
|
|
6954
|
+
* che sono cio' che uno cerca istintivamente — non sono nemmeno riferimenti validi (§5.2).
|
|
6955
|
+
*
|
|
6956
|
+
* Non aggiunge e non toglie niente: riordina. L'elenco resta quello della primitiva, e un
|
|
6957
|
+
* riferimento preferito che non c'e' semplicemente non compare — proporre un nome che il backend
|
|
6958
|
+
* non conosce sarebbe peggio che non metterlo in cima.
|
|
6959
|
+
*/
|
|
6960
|
+
preferredRoots = input([], ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
6757
6961
|
valueChange = output();
|
|
6758
6962
|
/**
|
|
6759
6963
|
* L'elenco come arriva dalla primitiva, **senza** il filtro di tipo.
|
|
@@ -7002,7 +7206,11 @@ class ReferencePickerComponent {
|
|
|
7002
7206
|
* fondo alla tendina i campi finivano sotto la piega — «ho cliccato la freccia e non mi da'
|
|
7003
7207
|
* l'elenco dei campi». L'ordine dei gruppi e' quello di prima apparizione, quindi basta questo.
|
|
7004
7208
|
*/
|
|
7005
|
-
const all =
|
|
7209
|
+
const all = this.preferFirst([
|
|
7210
|
+
...this.pathReferences(),
|
|
7211
|
+
...this.references(),
|
|
7212
|
+
...this.containerOptions(),
|
|
7213
|
+
]);
|
|
7006
7214
|
if (!needle) {
|
|
7007
7215
|
return all;
|
|
7008
7216
|
}
|
|
@@ -7010,6 +7218,22 @@ class ReferencePickerComponent {
|
|
|
7010
7218
|
.toLowerCase()
|
|
7011
7219
|
.includes(needle));
|
|
7012
7220
|
}, ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
7221
|
+
/**
|
|
7222
|
+
* Porta in testa le radici preferite, **senza** cambiare l'ordine relativo del resto. I gruppi si
|
|
7223
|
+
* costruiscono per ordine di prima apparizione, quindi bastano questi due passaggi perche' il
|
|
7224
|
+
* gruppo giusto risulti il primo della tendina.
|
|
7225
|
+
*/
|
|
7226
|
+
preferFirst(references) {
|
|
7227
|
+
const preferred = this.preferredRoots()
|
|
7228
|
+
.map((name) => name.trim().toLowerCase())
|
|
7229
|
+
.filter(Boolean);
|
|
7230
|
+
if (!preferred.length) {
|
|
7231
|
+
return references;
|
|
7232
|
+
}
|
|
7233
|
+
const wanted = (reference) => preferred.includes(reference.name.toLowerCase());
|
|
7234
|
+
const first = references.filter(wanted);
|
|
7235
|
+
return first.length ? [...first, ...references.filter((entry) => !wanted(entry))] : references;
|
|
7236
|
+
}
|
|
7013
7237
|
/** Le opzioni raggruppate per `kind`, così gli output automatici si distinguono. */
|
|
7014
7238
|
groups = computed(() => {
|
|
7015
7239
|
const groups = [];
|
|
@@ -7278,12 +7502,12 @@ class ReferencePickerComponent {
|
|
|
7278
7502
|
return parts.join(' · ');
|
|
7279
7503
|
}
|
|
7280
7504
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ReferencePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7281
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }, extraReferences: { classPropertyName: "extraReferences", publicName: "extraReferences", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], 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]=\"isHintWarning()\"\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 #panel 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 [title]=\"isContainerOnly(item) ? 'Il tipo non e\u2019 quello del campo: entra e scegli dentro' : ''\"\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 });
|
|
7505
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }, extraReferences: { classPropertyName: "extraReferences", publicName: "extraReferences", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], 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]=\"isHintWarning()\"\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 #panel 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 [title]=\"isContainerOnly(item) ? 'Il tipo non e\u2019 quello del campo: entra e scegli dentro' : ''\"\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 });
|
|
7282
7506
|
}
|
|
7283
7507
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ReferencePickerComponent, decorators: [{
|
|
7284
7508
|
type: Component,
|
|
7285
7509
|
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]=\"isHintWarning()\"\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 #panel 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 [title]=\"isContainerOnly(item) ? 'Il tipo non e\u2019 quello del campo: entra e scegli dentro' : ''\"\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"] }]
|
|
7286
|
-
}], 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 }] }], extraReferences: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraReferences", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
|
|
7510
|
+
}], 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 }] }], extraReferences: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraReferences", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
|
|
7287
7511
|
|
|
7288
7512
|
/**
|
|
7289
7513
|
* Selettore di oggetto (entita') — FRONTEND.md §4.2.
|
|
@@ -8286,6 +8510,12 @@ class FormulaEditorComponent {
|
|
|
8286
8510
|
ariaLabel = input('Espressione', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
8287
8511
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
8288
8512
|
rows = input(3, ...(ngDevMode ? [{ debugName: "rows" }] : []));
|
|
8513
|
+
/**
|
|
8514
|
+
* I nomi da suggerire **per primi** (§5.2). Lo passa un chiamante solo: la regola di validazione
|
|
8515
|
+
* di un campo dentro una riga di sezione ripetibile, dove la strada normale e' il cursore. Non
|
|
8516
|
+
* aggiunge nomi all'elenco di `POST /flows/references`: riordina i suggerimenti.
|
|
8517
|
+
*/
|
|
8518
|
+
preferredRoots = input([], ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
8289
8519
|
/**
|
|
8290
8520
|
* Quando propagare al documento. `input` a ogni tasto, `change` all'uscita dal campo: la
|
|
8291
8521
|
* seconda serve dove mutare il documento ridisegna mezza schermata — l'anteprima dello screen
|
|
@@ -8327,7 +8557,12 @@ class FormulaEditorComponent {
|
|
|
8327
8557
|
return [];
|
|
8328
8558
|
}
|
|
8329
8559
|
const needle = token.text.toLowerCase();
|
|
8330
|
-
const
|
|
8560
|
+
const preferred = this.preferredRoots().map((name) => name.trim().toLowerCase());
|
|
8561
|
+
const matches = this.references()
|
|
8562
|
+
.filter((reference) => reference.name.toLowerCase().startsWith(needle))
|
|
8563
|
+
// Stabile: cambia solo chi viene prima, non chi c'e'.
|
|
8564
|
+
.sort((left, right) => Number(preferred.includes(right.name.toLowerCase())) -
|
|
8565
|
+
Number(preferred.includes(left.name.toLowerCase())));
|
|
8331
8566
|
// Un solo suggerimento uguale a cio' che si e' già scritto non aiuta: e' solo un pannello che
|
|
8332
8567
|
// copre il testo.
|
|
8333
8568
|
if (matches.length === 1 && matches[0].name.toLowerCase() === needle) {
|
|
@@ -8648,12 +8883,12 @@ class FormulaEditorComponent {
|
|
|
8648
8883
|
}
|
|
8649
8884
|
}
|
|
8650
8885
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: FormulaEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8651
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: FormulaEditorComponent, isStandalone: true, selector: "fb-formula-editor", inputs: { expression: { classPropertyName: "expression", publicName: "expression", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, expectedDataType: { classPropertyName: "expectedDataType", publicName: "expectedDataType", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, commitOn: { classPropertyName: "commitOn", publicName: "commitOn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expressionChange: "expressionChange" }, viewQueries: [{ propertyName: "area", first: true, predicate: ["area"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (hasPalette()) {\r\n <!--\r\n La palette compare **solo** se il motore dichiara delle funzioni (\u00A76.3): elenco vuoto\r\n significa \u00ABnon le dichiara\u00BB, e un pannello vuoto farebbe credere che non ce ne siano.\r\n -->\r\n <div class=\"fb-formula__bar\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__toggle\"\r\n [class.fb-formula__toggle--on]=\"isPaletteOpen()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isPaletteOpen()\"\r\n title=\"Funzioni disponibili nel motore\"\r\n (click)=\"togglePalette()\"\r\n >\r\n <em>f</em>(x) Funzioni\r\n </button>\r\n </div>\r\n}\r\n\r\n<textarea\r\n #area\r\n class=\"fb-textarea fb-input--mono\"\r\n [class.fb-input--invalid]=\"hasErrors()\"\r\n [value]=\"expression()\"\r\n [rows]=\"rows()\"\r\n [disabled]=\"disabled()\"\r\n [placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"ariaLabel()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (change)=\"onCommit($any($event.target).value)\"\r\n (focus)=\"onFocus()\"\r\n (blur)=\"onBlur()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (keyup)=\"onCaretMove()\"\r\n (click)=\"onCaretMove()\"\r\n></textarea>\r\n\r\n@if (suggestions().length) {\r\n <!--\r\n L'elenco dei riferimenti disponibili come autocompletamento (\u00A76.3): stessa fonte del\r\n reference picker, `POST /flows/references`, output automatici compresi. `mousedown` va\r\n fermato \u2014 il blur della casella arriverebbe prima del click e chiuderebbe l'elenco.\r\n -->\r\n <ul class=\"fb-formula__hints\" role=\"listbox\" aria-label=\"Riferimenti disponibili\">\r\n @for (reference of suggestions(); track reference.name) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__hint\"\r\n [class.fb-formula__hint--on]=\"$index === highlighted()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"$index === highlighted()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insert(reference.name)\"\r\n >\r\n <code>{{ reference.name }}</code>\r\n @if (reference.dataType) {\r\n <span class=\"fb-formula__hint-type\">\r\n {{ reference.dataType }}{{ reference.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n}\r\n\r\n@if (hasPalette() && isPaletteOpen()) {\r\n <div class=\"fb-formula__palette\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-input fb-formula__search\"\r\n placeholder=\"Cerca una funzione\u2026\"\r\n aria-label=\"Cerca una funzione\"\r\n [value]=\"paletteFilter()\"\r\n (input)=\"paletteFilter.set($any($event.target).value)\"\r\n />\r\n @if (paletteGroups().length) {\r\n @for (group of paletteGroups(); track $index) {\r\n <div class=\"fb-formula__group\">\r\n @if (group.category) {\r\n <h5 class=\"fb-formula__group-title\">{{ group.category }}</h5>\r\n }\r\n @for (entry of group.items; track entry.name) {\r\n <!--\r\n `mousedown` fermato come nei suggerimenti: il blur della casella arriverebbe prima\r\n del click. `signature` e `description` si mostrano e non si parsano \u2014 la grammatica\r\n resta del motore (\u00A76.3).\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__fn\"\r\n [disabled]=\"disabled()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insertFunction(entry)\"\r\n >\r\n <code class=\"fb-formula__fn-sig\">{{ signatureOf(entry) }}</code>\r\n @if (entry.resultDataType) {\r\n <span class=\"fb-formula__fn-type\">\r\n {{ entry.resultDataType }}{{ entry.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n @if (entry.description) {\r\n <span class=\"fb-formula__fn-desc\">{{ entry.description }}</span>\r\n }\r\n @if (entry.example) {\r\n <code class=\"fb-formula__fn-example\">{{ entry.example }}</code>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">Nessuna funzione corrisponde alla ricerca.</p>\r\n }\r\n <!--\r\n L'elenco non e\u2019 autoritativo: un motore puo\u2019 supportare piu\u2019 di quanto dichiara, quindi una\r\n funzione che non c\u2019e\u2019 non e\u2019 un errore (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Elenco dichiarato dal motore: puo\u2019 non essere completo. A verificare l\u2019espressione resta il\r\n motore stesso.\r\n </p>\r\n </div>\r\n}\r\n\r\n@if (isChecking()) {\r\n <p class=\"fb-field__hint\">Verifica in corso\u2026</p>\r\n} @else if (issues().length) {\r\n <ul class=\"fb-formula__issues\">\r\n @for (issue of issues(); track $index) {\r\n <li class=\"fb-formula__issue\" [class]=\"'fb-formula__issue--' + severityOf(issue)\">\r\n @if (hasPosition(issue)) {\r\n <!--\r\n Il salto e' il senso di `position`: un indice di carattere che l'utente dovrebbe\r\n contare a mano non porta da nessuna parte.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__jump\"\r\n [disabled]=\"disabled()\"\r\n title=\"Seleziona il tratto segnalato\"\r\n (click)=\"goToIssue(issue)\"\r\n >\r\n @if (excerptOf(issue)) {\r\n <code>{{ excerptOf(issue) }}</code>\r\n } @else {\r\n car. {{ issue.position! + 1 }}\r\n }\r\n </button>\r\n }\r\n <span class=\"fb-formula__message\">{{ issue.message }}</span>\r\n <!-- Codice del motore, non della \u00A77: si mostra e non si interpreta. -->\r\n <code class=\"fb-formula__code\">{{ issue.code }}</code>\r\n </li>\r\n }\r\n </ul>\r\n} @else if (isConfirmed()) {\r\n <p class=\"fb-formula__ok\">\u2713 Espressione verificata dal motore.</p>\r\n} @else if (isUnverified()) {\r\n <!--\r\n Primitiva s\u00EC, motore no: nessun rilievo **e** nessuna conferma. Dirlo e' l'unico modo di\r\n non far leggere il silenzio come un via libera (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Nessun motore di validazione delle formule e\u2019 configurato: l\u2019espressione non e\u2019 stata\r\n controllata.\r\n </p>\r\n}\r\n\r\n@if (typeMismatch(); as mismatch) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n L\u2019espressione produce {{ mismatch.produced }}, ma qui serve {{ mismatch.expected }}.\r\n </p>\r\n}\r\n\r\n<ng-content />\r\n", styles: [":host{display:flex;flex-direction:column;gap:4px}.fb-formula__issues{display:flex;flex-direction:column;gap:3px;margin:0;padding:0;list-style:none}.fb-formula__issue{display:flex;flex-wrap:wrap;gap:5px;align-items:baseline;padding:3px 6px;border-left:3px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:10px;line-height:1.45}.fb-formula__issue--Error{border-left-color:var(--fb-error, #c9372c);color:var(--fb-error, #c9372c)}.fb-formula__issue--Warning{border-left-color:var(--fb-warning, #b7791f);color:var(--fb-warning, #b7791f)}.fb-formula__issue--Info{border-left-color:var(--fb-accent, #2f6feb);color:var(--fb-text-muted, #667085)}.fb-formula__message{flex:1 1 auto;min-width:0}.fb-formula__jump{padding:0 4px;border:1px solid currentcolor;border-radius:3px;background:transparent;color:inherit;font:inherit;font-size:10px;cursor:pointer}.fb-formula__jump:disabled{opacity:.5;cursor:not-allowed}.fb-formula__jump code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__code{color:var(--fb-text-subtle, #98a2b3);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px}.fb-formula__ok{margin:0;font-size:10px;color:var(--fb-success, #22a06b)}.fb-formula__hints{display:flex;flex-direction:column;gap:1px;max-height:160px;margin:0;padding:2px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);list-style:none}.fb-formula__hint{display:flex;gap:6px;align-items:baseline;width:100%;padding:2px 5px;border:0;border-radius:3px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__hint:hover,.fb-formula__hint--on{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__hint code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__hint-type{margin-left:auto;color:var(--fb-text-subtle, #98a2b3)}.fb-formula__bar{display:flex;justify-content:flex-end}.fb-formula__toggle{padding:1px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-formula__toggle em{font-family:ui-serif,Georgia,serif}.fb-formula__toggle--on{border-color:var(--fb-accent, #2f6feb);color:var(--fb-accent, #2f6feb)}.fb-formula__toggle:disabled{opacity:.5;cursor:not-allowed}.fb-formula__palette{display:flex;flex-direction:column;gap:4px;max-height:220px;padding:4px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface-alt, #f8f9fb)}.fb-formula__search{font-size:10px}.fb-formula__group{display:flex;flex-direction:column;gap:2px}.fb-formula__group-title{margin:2px 0 0;color:var(--fb-text-subtle, #98a2b3);font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.fb-formula__fn{display:grid;grid-template-columns:1fr auto;gap:0 6px;padding:3px 5px;border:0;border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__fn:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__fn:disabled{opacity:.5;cursor:not-allowed}.fb-formula__fn-sig{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__fn-type{color:var(--fb-text-subtle, #98a2b3)}.fb-formula__fn-desc,.fb-formula__fn-example{grid-column:1 / -1;color:var(--fb-text-muted, #667085);font-size:9px;line-height:1.4}.fb-formula__fn-example{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8886
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: FormulaEditorComponent, isStandalone: true, selector: "fb-formula-editor", inputs: { expression: { classPropertyName: "expression", publicName: "expression", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, expectedDataType: { classPropertyName: "expectedDataType", publicName: "expectedDataType", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null }, commitOn: { classPropertyName: "commitOn", publicName: "commitOn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expressionChange: "expressionChange" }, viewQueries: [{ propertyName: "area", first: true, predicate: ["area"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (hasPalette()) {\r\n <!--\r\n La palette compare **solo** se il motore dichiara delle funzioni (\u00A76.3): elenco vuoto\r\n significa \u00ABnon le dichiara\u00BB, e un pannello vuoto farebbe credere che non ce ne siano.\r\n -->\r\n <div class=\"fb-formula__bar\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__toggle\"\r\n [class.fb-formula__toggle--on]=\"isPaletteOpen()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isPaletteOpen()\"\r\n title=\"Funzioni disponibili nel motore\"\r\n (click)=\"togglePalette()\"\r\n >\r\n <em>f</em>(x) Funzioni\r\n </button>\r\n </div>\r\n}\r\n\r\n<textarea\r\n #area\r\n class=\"fb-textarea fb-input--mono\"\r\n [class.fb-input--invalid]=\"hasErrors()\"\r\n [value]=\"expression()\"\r\n [rows]=\"rows()\"\r\n [disabled]=\"disabled()\"\r\n [placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"ariaLabel()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (change)=\"onCommit($any($event.target).value)\"\r\n (focus)=\"onFocus()\"\r\n (blur)=\"onBlur()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (keyup)=\"onCaretMove()\"\r\n (click)=\"onCaretMove()\"\r\n></textarea>\r\n\r\n@if (suggestions().length) {\r\n <!--\r\n L'elenco dei riferimenti disponibili come autocompletamento (\u00A76.3): stessa fonte del\r\n reference picker, `POST /flows/references`, output automatici compresi. `mousedown` va\r\n fermato \u2014 il blur della casella arriverebbe prima del click e chiuderebbe l'elenco.\r\n -->\r\n <ul class=\"fb-formula__hints\" role=\"listbox\" aria-label=\"Riferimenti disponibili\">\r\n @for (reference of suggestions(); track reference.name) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__hint\"\r\n [class.fb-formula__hint--on]=\"$index === highlighted()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"$index === highlighted()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insert(reference.name)\"\r\n >\r\n <code>{{ reference.name }}</code>\r\n @if (reference.dataType) {\r\n <span class=\"fb-formula__hint-type\">\r\n {{ reference.dataType }}{{ reference.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n}\r\n\r\n@if (hasPalette() && isPaletteOpen()) {\r\n <div class=\"fb-formula__palette\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-input fb-formula__search\"\r\n placeholder=\"Cerca una funzione\u2026\"\r\n aria-label=\"Cerca una funzione\"\r\n [value]=\"paletteFilter()\"\r\n (input)=\"paletteFilter.set($any($event.target).value)\"\r\n />\r\n @if (paletteGroups().length) {\r\n @for (group of paletteGroups(); track $index) {\r\n <div class=\"fb-formula__group\">\r\n @if (group.category) {\r\n <h5 class=\"fb-formula__group-title\">{{ group.category }}</h5>\r\n }\r\n @for (entry of group.items; track entry.name) {\r\n <!--\r\n `mousedown` fermato come nei suggerimenti: il blur della casella arriverebbe prima\r\n del click. `signature` e `description` si mostrano e non si parsano \u2014 la grammatica\r\n resta del motore (\u00A76.3).\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__fn\"\r\n [disabled]=\"disabled()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insertFunction(entry)\"\r\n >\r\n <code class=\"fb-formula__fn-sig\">{{ signatureOf(entry) }}</code>\r\n @if (entry.resultDataType) {\r\n <span class=\"fb-formula__fn-type\">\r\n {{ entry.resultDataType }}{{ entry.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n @if (entry.description) {\r\n <span class=\"fb-formula__fn-desc\">{{ entry.description }}</span>\r\n }\r\n @if (entry.example) {\r\n <code class=\"fb-formula__fn-example\">{{ entry.example }}</code>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">Nessuna funzione corrisponde alla ricerca.</p>\r\n }\r\n <!--\r\n L'elenco non e\u2019 autoritativo: un motore puo\u2019 supportare piu\u2019 di quanto dichiara, quindi una\r\n funzione che non c\u2019e\u2019 non e\u2019 un errore (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Elenco dichiarato dal motore: puo\u2019 non essere completo. A verificare l\u2019espressione resta il\r\n motore stesso.\r\n </p>\r\n </div>\r\n}\r\n\r\n@if (isChecking()) {\r\n <p class=\"fb-field__hint\">Verifica in corso\u2026</p>\r\n} @else if (issues().length) {\r\n <ul class=\"fb-formula__issues\">\r\n @for (issue of issues(); track $index) {\r\n <li class=\"fb-formula__issue\" [class]=\"'fb-formula__issue--' + severityOf(issue)\">\r\n @if (hasPosition(issue)) {\r\n <!--\r\n Il salto e' il senso di `position`: un indice di carattere che l'utente dovrebbe\r\n contare a mano non porta da nessuna parte.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__jump\"\r\n [disabled]=\"disabled()\"\r\n title=\"Seleziona il tratto segnalato\"\r\n (click)=\"goToIssue(issue)\"\r\n >\r\n @if (excerptOf(issue)) {\r\n <code>{{ excerptOf(issue) }}</code>\r\n } @else {\r\n car. {{ issue.position! + 1 }}\r\n }\r\n </button>\r\n }\r\n <span class=\"fb-formula__message\">{{ issue.message }}</span>\r\n <!-- Codice del motore, non della \u00A77: si mostra e non si interpreta. -->\r\n <code class=\"fb-formula__code\">{{ issue.code }}</code>\r\n </li>\r\n }\r\n </ul>\r\n} @else if (isConfirmed()) {\r\n <p class=\"fb-formula__ok\">\u2713 Espressione verificata dal motore.</p>\r\n} @else if (isUnverified()) {\r\n <!--\r\n Primitiva s\u00EC, motore no: nessun rilievo **e** nessuna conferma. Dirlo e' l'unico modo di\r\n non far leggere il silenzio come un via libera (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Nessun motore di validazione delle formule e\u2019 configurato: l\u2019espressione non e\u2019 stata\r\n controllata.\r\n </p>\r\n}\r\n\r\n@if (typeMismatch(); as mismatch) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n L\u2019espressione produce {{ mismatch.produced }}, ma qui serve {{ mismatch.expected }}.\r\n </p>\r\n}\r\n\r\n<ng-content />\r\n", styles: [":host{display:flex;flex-direction:column;gap:4px}.fb-formula__issues{display:flex;flex-direction:column;gap:3px;margin:0;padding:0;list-style:none}.fb-formula__issue{display:flex;flex-wrap:wrap;gap:5px;align-items:baseline;padding:3px 6px;border-left:3px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:10px;line-height:1.45}.fb-formula__issue--Error{border-left-color:var(--fb-error, #c9372c);color:var(--fb-error, #c9372c)}.fb-formula__issue--Warning{border-left-color:var(--fb-warning, #b7791f);color:var(--fb-warning, #b7791f)}.fb-formula__issue--Info{border-left-color:var(--fb-accent, #2f6feb);color:var(--fb-text-muted, #667085)}.fb-formula__message{flex:1 1 auto;min-width:0}.fb-formula__jump{padding:0 4px;border:1px solid currentcolor;border-radius:3px;background:transparent;color:inherit;font:inherit;font-size:10px;cursor:pointer}.fb-formula__jump:disabled{opacity:.5;cursor:not-allowed}.fb-formula__jump code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__code{color:var(--fb-text-subtle, #98a2b3);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px}.fb-formula__ok{margin:0;font-size:10px;color:var(--fb-success, #22a06b)}.fb-formula__hints{display:flex;flex-direction:column;gap:1px;max-height:160px;margin:0;padding:2px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);list-style:none}.fb-formula__hint{display:flex;gap:6px;align-items:baseline;width:100%;padding:2px 5px;border:0;border-radius:3px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__hint:hover,.fb-formula__hint--on{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__hint code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__hint-type{margin-left:auto;color:var(--fb-text-subtle, #98a2b3)}.fb-formula__bar{display:flex;justify-content:flex-end}.fb-formula__toggle{padding:1px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-formula__toggle em{font-family:ui-serif,Georgia,serif}.fb-formula__toggle--on{border-color:var(--fb-accent, #2f6feb);color:var(--fb-accent, #2f6feb)}.fb-formula__toggle:disabled{opacity:.5;cursor:not-allowed}.fb-formula__palette{display:flex;flex-direction:column;gap:4px;max-height:220px;padding:4px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface-alt, #f8f9fb)}.fb-formula__search{font-size:10px}.fb-formula__group{display:flex;flex-direction:column;gap:2px}.fb-formula__group-title{margin:2px 0 0;color:var(--fb-text-subtle, #98a2b3);font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.fb-formula__fn{display:grid;grid-template-columns:1fr auto;gap:0 6px;padding:3px 5px;border:0;border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__fn:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__fn:disabled{opacity:.5;cursor:not-allowed}.fb-formula__fn-sig{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__fn-type{color:var(--fb-text-subtle, #98a2b3)}.fb-formula__fn-desc,.fb-formula__fn-example{grid-column:1 / -1;color:var(--fb-text-muted, #667085);font-size:9px;line-height:1.4}.fb-formula__fn-example{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8652
8887
|
}
|
|
8653
8888
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: FormulaEditorComponent, decorators: [{
|
|
8654
8889
|
type: Component,
|
|
8655
8890
|
args: [{ selector: 'fb-formula-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (hasPalette()) {\r\n <!--\r\n La palette compare **solo** se il motore dichiara delle funzioni (\u00A76.3): elenco vuoto\r\n significa \u00ABnon le dichiara\u00BB, e un pannello vuoto farebbe credere che non ce ne siano.\r\n -->\r\n <div class=\"fb-formula__bar\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__toggle\"\r\n [class.fb-formula__toggle--on]=\"isPaletteOpen()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isPaletteOpen()\"\r\n title=\"Funzioni disponibili nel motore\"\r\n (click)=\"togglePalette()\"\r\n >\r\n <em>f</em>(x) Funzioni\r\n </button>\r\n </div>\r\n}\r\n\r\n<textarea\r\n #area\r\n class=\"fb-textarea fb-input--mono\"\r\n [class.fb-input--invalid]=\"hasErrors()\"\r\n [value]=\"expression()\"\r\n [rows]=\"rows()\"\r\n [disabled]=\"disabled()\"\r\n [placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"ariaLabel()\"\r\n (input)=\"onInput($any($event.target).value)\"\r\n (change)=\"onCommit($any($event.target).value)\"\r\n (focus)=\"onFocus()\"\r\n (blur)=\"onBlur()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (keyup)=\"onCaretMove()\"\r\n (click)=\"onCaretMove()\"\r\n></textarea>\r\n\r\n@if (suggestions().length) {\r\n <!--\r\n L'elenco dei riferimenti disponibili come autocompletamento (\u00A76.3): stessa fonte del\r\n reference picker, `POST /flows/references`, output automatici compresi. `mousedown` va\r\n fermato \u2014 il blur della casella arriverebbe prima del click e chiuderebbe l'elenco.\r\n -->\r\n <ul class=\"fb-formula__hints\" role=\"listbox\" aria-label=\"Riferimenti disponibili\">\r\n @for (reference of suggestions(); track reference.name) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__hint\"\r\n [class.fb-formula__hint--on]=\"$index === highlighted()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"$index === highlighted()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insert(reference.name)\"\r\n >\r\n <code>{{ reference.name }}</code>\r\n @if (reference.dataType) {\r\n <span class=\"fb-formula__hint-type\">\r\n {{ reference.dataType }}{{ reference.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n}\r\n\r\n@if (hasPalette() && isPaletteOpen()) {\r\n <div class=\"fb-formula__palette\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-input fb-formula__search\"\r\n placeholder=\"Cerca una funzione\u2026\"\r\n aria-label=\"Cerca una funzione\"\r\n [value]=\"paletteFilter()\"\r\n (input)=\"paletteFilter.set($any($event.target).value)\"\r\n />\r\n @if (paletteGroups().length) {\r\n @for (group of paletteGroups(); track $index) {\r\n <div class=\"fb-formula__group\">\r\n @if (group.category) {\r\n <h5 class=\"fb-formula__group-title\">{{ group.category }}</h5>\r\n }\r\n @for (entry of group.items; track entry.name) {\r\n <!--\r\n `mousedown` fermato come nei suggerimenti: il blur della casella arriverebbe prima\r\n del click. `signature` e `description` si mostrano e non si parsano \u2014 la grammatica\r\n resta del motore (\u00A76.3).\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__fn\"\r\n [disabled]=\"disabled()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"insertFunction(entry)\"\r\n >\r\n <code class=\"fb-formula__fn-sig\">{{ signatureOf(entry) }}</code>\r\n @if (entry.resultDataType) {\r\n <span class=\"fb-formula__fn-type\">\r\n {{ entry.resultDataType }}{{ entry.isCollection ? '[]' : '' }}\r\n </span>\r\n }\r\n @if (entry.description) {\r\n <span class=\"fb-formula__fn-desc\">{{ entry.description }}</span>\r\n }\r\n @if (entry.example) {\r\n <code class=\"fb-formula__fn-example\">{{ entry.example }}</code>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">Nessuna funzione corrisponde alla ricerca.</p>\r\n }\r\n <!--\r\n L'elenco non e\u2019 autoritativo: un motore puo\u2019 supportare piu\u2019 di quanto dichiara, quindi una\r\n funzione che non c\u2019e\u2019 non e\u2019 un errore (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Elenco dichiarato dal motore: puo\u2019 non essere completo. A verificare l\u2019espressione resta il\r\n motore stesso.\r\n </p>\r\n </div>\r\n}\r\n\r\n@if (isChecking()) {\r\n <p class=\"fb-field__hint\">Verifica in corso\u2026</p>\r\n} @else if (issues().length) {\r\n <ul class=\"fb-formula__issues\">\r\n @for (issue of issues(); track $index) {\r\n <li class=\"fb-formula__issue\" [class]=\"'fb-formula__issue--' + severityOf(issue)\">\r\n @if (hasPosition(issue)) {\r\n <!--\r\n Il salto e' il senso di `position`: un indice di carattere che l'utente dovrebbe\r\n contare a mano non porta da nessuna parte.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-formula__jump\"\r\n [disabled]=\"disabled()\"\r\n title=\"Seleziona il tratto segnalato\"\r\n (click)=\"goToIssue(issue)\"\r\n >\r\n @if (excerptOf(issue)) {\r\n <code>{{ excerptOf(issue) }}</code>\r\n } @else {\r\n car. {{ issue.position! + 1 }}\r\n }\r\n </button>\r\n }\r\n <span class=\"fb-formula__message\">{{ issue.message }}</span>\r\n <!-- Codice del motore, non della \u00A77: si mostra e non si interpreta. -->\r\n <code class=\"fb-formula__code\">{{ issue.code }}</code>\r\n </li>\r\n }\r\n </ul>\r\n} @else if (isConfirmed()) {\r\n <p class=\"fb-formula__ok\">\u2713 Espressione verificata dal motore.</p>\r\n} @else if (isUnverified()) {\r\n <!--\r\n Primitiva s\u00EC, motore no: nessun rilievo **e** nessuna conferma. Dirlo e' l'unico modo di\r\n non far leggere il silenzio come un via libera (\u00A76.3).\r\n -->\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Nessun motore di validazione delle formule e\u2019 configurato: l\u2019espressione non e\u2019 stata\r\n controllata.\r\n </p>\r\n}\r\n\r\n@if (typeMismatch(); as mismatch) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n L\u2019espressione produce {{ mismatch.produced }}, ma qui serve {{ mismatch.expected }}.\r\n </p>\r\n}\r\n\r\n<ng-content />\r\n", styles: [":host{display:flex;flex-direction:column;gap:4px}.fb-formula__issues{display:flex;flex-direction:column;gap:3px;margin:0;padding:0;list-style:none}.fb-formula__issue{display:flex;flex-wrap:wrap;gap:5px;align-items:baseline;padding:3px 6px;border-left:3px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:10px;line-height:1.45}.fb-formula__issue--Error{border-left-color:var(--fb-error, #c9372c);color:var(--fb-error, #c9372c)}.fb-formula__issue--Warning{border-left-color:var(--fb-warning, #b7791f);color:var(--fb-warning, #b7791f)}.fb-formula__issue--Info{border-left-color:var(--fb-accent, #2f6feb);color:var(--fb-text-muted, #667085)}.fb-formula__message{flex:1 1 auto;min-width:0}.fb-formula__jump{padding:0 4px;border:1px solid currentcolor;border-radius:3px;background:transparent;color:inherit;font:inherit;font-size:10px;cursor:pointer}.fb-formula__jump:disabled{opacity:.5;cursor:not-allowed}.fb-formula__jump code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__code{color:var(--fb-text-subtle, #98a2b3);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px}.fb-formula__ok{margin:0;font-size:10px;color:var(--fb-success, #22a06b)}.fb-formula__hints{display:flex;flex-direction:column;gap:1px;max-height:160px;margin:0;padding:2px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);list-style:none}.fb-formula__hint{display:flex;gap:6px;align-items:baseline;width:100%;padding:2px 5px;border:0;border-radius:3px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__hint:hover,.fb-formula__hint--on{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__hint code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__hint-type{margin-left:auto;color:var(--fb-text-subtle, #98a2b3)}.fb-formula__bar{display:flex;justify-content:flex-end}.fb-formula__toggle{padding:1px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-formula__toggle em{font-family:ui-serif,Georgia,serif}.fb-formula__toggle--on{border-color:var(--fb-accent, #2f6feb);color:var(--fb-accent, #2f6feb)}.fb-formula__toggle:disabled{opacity:.5;cursor:not-allowed}.fb-formula__palette{display:flex;flex-direction:column;gap:4px;max-height:220px;padding:4px;overflow-y:auto;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface-alt, #f8f9fb)}.fb-formula__search{font-size:10px}.fb-formula__group{display:flex;flex-direction:column;gap:2px}.fb-formula__group-title{margin:2px 0 0;color:var(--fb-text-subtle, #98a2b3);font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.fb-formula__fn{display:grid;grid-template-columns:1fr auto;gap:0 6px;padding:3px 5px;border:0;border-radius:3px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:10px;text-align:left;cursor:pointer}.fb-formula__fn:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent)}.fb-formula__fn:disabled{opacity:.5;cursor:not-allowed}.fb-formula__fn-sig{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-formula__fn-type{color:var(--fb-text-subtle, #98a2b3)}.fb-formula__fn-desc,.fb-formula__fn-example{grid-column:1 / -1;color:var(--fb-text-muted, #667085);font-size:9px;line-height:1.4}.fb-formula__fn-example{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--fb-text-subtle, #98a2b3)}\n"] }]
|
|
8656
|
-
}], ctorParameters: () => [], propDecorators: { expression: [{ type: i0.Input, args: [{ isSignal: true, alias: "expression", required: false }] }], usage: [{ type: i0.Input, args: [{ isSignal: true, alias: "usage", required: false }] }], expectedDataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "expectedDataType", required: false }] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], commitOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "commitOn", required: false }] }], expressionChange: [{ type: i0.Output, args: ["expressionChange"] }], area: [{ type: i0.ViewChild, args: ['area', { isSignal: true }] }] } });
|
|
8891
|
+
}], ctorParameters: () => [], propDecorators: { expression: [{ type: i0.Input, args: [{ isSignal: true, alias: "expression", required: false }] }], usage: [{ type: i0.Input, args: [{ isSignal: true, alias: "usage", required: false }] }], expectedDataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "expectedDataType", required: false }] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], commitOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "commitOn", required: false }] }], expressionChange: [{ type: i0.Output, args: ["expressionChange"] }], area: [{ type: i0.ViewChild, args: ['area', { isSignal: true }] }] } });
|
|
8657
8892
|
|
|
8658
8893
|
/**
|
|
8659
8894
|
* `[fbValue]` su un `<select>`.
|
|
@@ -8729,6 +8964,12 @@ class ValueEditorComponent {
|
|
|
8729
8964
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
8730
8965
|
/** Nasconde la modalita' formula dove il modello non la prevede. */
|
|
8731
8966
|
allowFormula = input(true, ...(ngDevMode ? [{ debugName: "allowFormula" }] : []));
|
|
8967
|
+
/**
|
|
8968
|
+
* Le radici da proporre **per prime** nel reference picker (§5.2). Oggi lo passa un chiamante
|
|
8969
|
+
* solo: dentro una riga di sezione ripetibile la strada normale e' il **cursore**, e i campi
|
|
8970
|
+
* della riga non sono nemmeno riferimenti validi. Non aggiunge niente all'elenco: riordina.
|
|
8971
|
+
*/
|
|
8972
|
+
preferredRoots = input([], ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
8732
8973
|
valueChange = output();
|
|
8733
8974
|
/** La modalita' dedotta dal campo effettivamente valorizzato. */
|
|
8734
8975
|
mode = computed(() => {
|
|
@@ -9020,7 +9261,7 @@ class ValueEditorComponent {
|
|
|
9020
9261
|
}
|
|
9021
9262
|
dataTypeOptions = computed(() => this.dictionaries.dataTypes(), ...(ngDevMode ? [{ debugName: "dataTypeOptions" }] : []));
|
|
9022
9263
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ValueEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9023
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ValueEditorComponent, isStandalone: true, selector: "fb-value-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, valueSet: { classPropertyName: "valueSet", publicName: "valueSet", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n @if (isStructureTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Un\u2019istanza di classe si passa per riferimento: non esiste un valore letterale con cui scriverla.\r\n </p>\r\n }\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <!--\r\n Tre stati, non due: **assente**, `false` e `true`. Una casella di spunta ne rappresenta\r\n due, e quella non spuntata finiva per dire \u00ABfalso\u00BB su un valore che nel documento non\r\n c'era \u2014 chi non la toccava leggeva una scelta che nessuno aveva fatto e che al backend non\r\n arrivava. `false` e' un valore e si scrive; \u00ABnon impostato\u00BB e' l'assenza del campo (\u00A72,\r\n \u00A74.2), ed e' l'unica cosa che una spunta non sa dire. Stesso controllo del form di\r\n \u00ABEsegui\u00BB, dove il problema era gi\u00E0 stato risolto cos\u00EC.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"booleanText()\"\r\n (change)=\"onBooleanTextChange($any($event.target).value)\"\r\n >\r\n <option value=\"\">non impostato</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n @if (!booleanText()) {\r\n <p class=\"fb-field__hint\">\r\n Nessun valore nel documento: \u00ABfalso\u00BB e\u2019 una scelta e va detta, perche\u2019 un campo assente\r\n non e\u2019 <code>false</code>.\r\n </p>\r\n }\r\n } @else if (dataType() === 'Enum') {\r\n <!--\r\n Un enum si scrive **per nome** su un insieme chiuso: i nomi ammessi sono quelli del tipo\r\n (\u00A74.2, \u00A74.6), e farli digitare e' il modo piu' facile di scriverne uno che il runtime non\r\n riconosce. Senza `objectType` \u2014 o senza la primitiva \u2014 il picker resta una casella di\r\n testo, che e' l'unica cosa onesta quando i valori non si sanno.\r\n -->\r\n <fb-enum-value-picker\r\n [value]=\"value()?.enumValue\"\r\n [enumType]=\"objectType()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Nome del valore di enum\"\r\n (valueChange)=\"onEnumChange($event)\"\r\n />\r\n } @else if (hasValueSet()) {\r\n <!--\r\n Campo a scelta chiusa (\u00A74.4): i valori ammessi sono della **colonna** e si chiedono con\r\n `listFieldValues`, non si deducono dal tipo. Farli digitare su un insieme di quattro e' il\r\n modo piu' facile di scrivere un valore che nessun record ha \u2014 e il filtro non troverebbe\r\n niente senza nessun errore da nessuna parte.\r\n -->\r\n <fb-field-value-picker\r\n [value]=\"literalText()\"\r\n [valueSet]=\"valueSet()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onClosedValueChange($event)\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <!--\r\n Il tipo atteso e' quello **dichiarato sulla formula** (`formulaDataType`), non quello del\r\n campo: e' cio' che il documento porta, ed e' su quello che il motore va misurato (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"value()?.formulaExpression || ''\"\r\n usage=\"Value\"\r\n [expectedDataType]=\"$any(value()?.formulaDataType) || dataType()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [ariaLabel]=\"label() + ': espressione'\"\r\n (expressionChange)=\"onFormulaChange($event)\"\r\n />\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:6px}\n"], dependencies: [{ kind: "component", type: EnumValuePickerComponent, selector: "fb-enum-value-picker", inputs: ["value", "enumType", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FieldValuePickerComponent, selector: "fb-field-value-picker", inputs: ["value", "valueSet", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9264
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ValueEditorComponent, isStandalone: true, selector: "fb-value-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, valueSet: { classPropertyName: "valueSet", publicName: "valueSet", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n @if (isStructureTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Un\u2019istanza di classe si passa per riferimento: non esiste un valore letterale con cui scriverla.\r\n </p>\r\n }\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <!--\r\n Tre stati, non due: **assente**, `false` e `true`. Una casella di spunta ne rappresenta\r\n due, e quella non spuntata finiva per dire \u00ABfalso\u00BB su un valore che nel documento non\r\n c'era \u2014 chi non la toccava leggeva una scelta che nessuno aveva fatto e che al backend non\r\n arrivava. `false` e' un valore e si scrive; \u00ABnon impostato\u00BB e' l'assenza del campo (\u00A72,\r\n \u00A74.2), ed e' l'unica cosa che una spunta non sa dire. Stesso controllo del form di\r\n \u00ABEsegui\u00BB, dove il problema era gi\u00E0 stato risolto cos\u00EC.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"booleanText()\"\r\n (change)=\"onBooleanTextChange($any($event.target).value)\"\r\n >\r\n <option value=\"\">non impostato</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n @if (!booleanText()) {\r\n <p class=\"fb-field__hint\">\r\n Nessun valore nel documento: \u00ABfalso\u00BB e\u2019 una scelta e va detta, perche\u2019 un campo assente\r\n non e\u2019 <code>false</code>.\r\n </p>\r\n }\r\n } @else if (dataType() === 'Enum') {\r\n <!--\r\n Un enum si scrive **per nome** su un insieme chiuso: i nomi ammessi sono quelli del tipo\r\n (\u00A74.2, \u00A74.6), e farli digitare e' il modo piu' facile di scriverne uno che il runtime non\r\n riconosce. Senza `objectType` \u2014 o senza la primitiva \u2014 il picker resta una casella di\r\n testo, che e' l'unica cosa onesta quando i valori non si sanno.\r\n -->\r\n <fb-enum-value-picker\r\n [value]=\"value()?.enumValue\"\r\n [enumType]=\"objectType()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Nome del valore di enum\"\r\n (valueChange)=\"onEnumChange($event)\"\r\n />\r\n } @else if (hasValueSet()) {\r\n <!--\r\n Campo a scelta chiusa (\u00A74.4): i valori ammessi sono della **colonna** e si chiedono con\r\n `listFieldValues`, non si deducono dal tipo. Farli digitare su un insieme di quattro e' il\r\n modo piu' facile di scrivere un valore che nessun record ha \u2014 e il filtro non troverebbe\r\n niente senza nessun errore da nessuna parte.\r\n -->\r\n <fb-field-value-picker\r\n [value]=\"literalText()\"\r\n [valueSet]=\"valueSet()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onClosedValueChange($event)\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <!--\r\n Il tipo atteso e' quello **dichiarato sulla formula** (`formulaDataType`), non quello del\r\n campo: e' cio' che il documento porta, ed e' su quello che il motore va misurato (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"value()?.formulaExpression || ''\"\r\n usage=\"Value\"\r\n [expectedDataType]=\"$any(value()?.formulaDataType) || dataType()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [ariaLabel]=\"label() + ': espressione'\"\r\n (expressionChange)=\"onFormulaChange($event)\"\r\n />\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:6px}\n"], dependencies: [{ kind: "component", type: EnumValuePickerComponent, selector: "fb-enum-value-picker", inputs: ["value", "enumType", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FieldValuePickerComponent, selector: "fb-field-value-picker", inputs: ["value", "valueSet", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9024
9265
|
}
|
|
9025
9266
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ValueEditorComponent, decorators: [{
|
|
9026
9267
|
type: Component,
|
|
@@ -9030,8 +9271,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
9030
9271
|
FormulaEditorComponent,
|
|
9031
9272
|
ReferencePickerComponent,
|
|
9032
9273
|
SelectValueDirective,
|
|
9033
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n @if (isStructureTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Un\u2019istanza di classe si passa per riferimento: non esiste un valore letterale con cui scriverla.\r\n </p>\r\n }\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <!--\r\n Tre stati, non due: **assente**, `false` e `true`. Una casella di spunta ne rappresenta\r\n due, e quella non spuntata finiva per dire \u00ABfalso\u00BB su un valore che nel documento non\r\n c'era \u2014 chi non la toccava leggeva una scelta che nessuno aveva fatto e che al backend non\r\n arrivava. `false` e' un valore e si scrive; \u00ABnon impostato\u00BB e' l'assenza del campo (\u00A72,\r\n \u00A74.2), ed e' l'unica cosa che una spunta non sa dire. Stesso controllo del form di\r\n \u00ABEsegui\u00BB, dove il problema era gi\u00E0 stato risolto cos\u00EC.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"booleanText()\"\r\n (change)=\"onBooleanTextChange($any($event.target).value)\"\r\n >\r\n <option value=\"\">non impostato</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n @if (!booleanText()) {\r\n <p class=\"fb-field__hint\">\r\n Nessun valore nel documento: \u00ABfalso\u00BB e\u2019 una scelta e va detta, perche\u2019 un campo assente\r\n non e\u2019 <code>false</code>.\r\n </p>\r\n }\r\n } @else if (dataType() === 'Enum') {\r\n <!--\r\n Un enum si scrive **per nome** su un insieme chiuso: i nomi ammessi sono quelli del tipo\r\n (\u00A74.2, \u00A74.6), e farli digitare e' il modo piu' facile di scriverne uno che il runtime non\r\n riconosce. Senza `objectType` \u2014 o senza la primitiva \u2014 il picker resta una casella di\r\n testo, che e' l'unica cosa onesta quando i valori non si sanno.\r\n -->\r\n <fb-enum-value-picker\r\n [value]=\"value()?.enumValue\"\r\n [enumType]=\"objectType()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Nome del valore di enum\"\r\n (valueChange)=\"onEnumChange($event)\"\r\n />\r\n } @else if (hasValueSet()) {\r\n <!--\r\n Campo a scelta chiusa (\u00A74.4): i valori ammessi sono della **colonna** e si chiedono con\r\n `listFieldValues`, non si deducono dal tipo. Farli digitare su un insieme di quattro e' il\r\n modo piu' facile di scrivere un valore che nessun record ha \u2014 e il filtro non troverebbe\r\n niente senza nessun errore da nessuna parte.\r\n -->\r\n <fb-field-value-picker\r\n [value]=\"literalText()\"\r\n [valueSet]=\"valueSet()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onClosedValueChange($event)\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <!--\r\n Il tipo atteso e' quello **dichiarato sulla formula** (`formulaDataType`), non quello del\r\n campo: e' cio' che il documento porta, ed e' su quello che il motore va misurato (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"value()?.formulaExpression || ''\"\r\n usage=\"Value\"\r\n [expectedDataType]=\"$any(value()?.formulaDataType) || dataType()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [ariaLabel]=\"label() + ': espressione'\"\r\n (expressionChange)=\"onFormulaChange($event)\"\r\n />\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:6px}\n"] }]
|
|
9034
|
-
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], valueSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueSet", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
9274
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n @if (isStructureTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Un\u2019istanza di classe si passa per riferimento: non esiste un valore letterale con cui scriverla.\r\n </p>\r\n }\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <!--\r\n Tre stati, non due: **assente**, `false` e `true`. Una casella di spunta ne rappresenta\r\n due, e quella non spuntata finiva per dire \u00ABfalso\u00BB su un valore che nel documento non\r\n c'era \u2014 chi non la toccava leggeva una scelta che nessuno aveva fatto e che al backend non\r\n arrivava. `false` e' un valore e si scrive; \u00ABnon impostato\u00BB e' l'assenza del campo (\u00A72,\r\n \u00A74.2), ed e' l'unica cosa che una spunta non sa dire. Stesso controllo del form di\r\n \u00ABEsegui\u00BB, dove il problema era gi\u00E0 stato risolto cos\u00EC.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"booleanText()\"\r\n (change)=\"onBooleanTextChange($any($event.target).value)\"\r\n >\r\n <option value=\"\">non impostato</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n @if (!booleanText()) {\r\n <p class=\"fb-field__hint\">\r\n Nessun valore nel documento: \u00ABfalso\u00BB e\u2019 una scelta e va detta, perche\u2019 un campo assente\r\n non e\u2019 <code>false</code>.\r\n </p>\r\n }\r\n } @else if (dataType() === 'Enum') {\r\n <!--\r\n Un enum si scrive **per nome** su un insieme chiuso: i nomi ammessi sono quelli del tipo\r\n (\u00A74.2, \u00A74.6), e farli digitare e' il modo piu' facile di scriverne uno che il runtime non\r\n riconosce. Senza `objectType` \u2014 o senza la primitiva \u2014 il picker resta una casella di\r\n testo, che e' l'unica cosa onesta quando i valori non si sanno.\r\n -->\r\n <fb-enum-value-picker\r\n [value]=\"value()?.enumValue\"\r\n [enumType]=\"objectType()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Nome del valore di enum\"\r\n (valueChange)=\"onEnumChange($event)\"\r\n />\r\n } @else if (hasValueSet()) {\r\n <!--\r\n Campo a scelta chiusa (\u00A74.4): i valori ammessi sono della **colonna** e si chiedono con\r\n `listFieldValues`, non si deducono dal tipo. Farli digitare su un insieme di quattro e' il\r\n modo piu' facile di scrivere un valore che nessun record ha \u2014 e il filtro non troverebbe\r\n niente senza nessun errore da nessuna parte.\r\n -->\r\n <fb-field-value-picker\r\n [value]=\"literalText()\"\r\n [valueSet]=\"valueSet()\"\r\n [label]=\"label()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onClosedValueChange($event)\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <!--\r\n Il tipo atteso e' quello **dichiarato sulla formula** (`formulaDataType`), non quello del\r\n campo: e' cio' che il documento porta, ed e' su quello che il motore va misurato (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"value()?.formulaExpression || ''\"\r\n usage=\"Value\"\r\n [expectedDataType]=\"$any(value()?.formulaDataType) || dataType()\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [ariaLabel]=\"label() + ': espressione'\"\r\n (expressionChange)=\"onFormulaChange($event)\"\r\n />\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:6px}\n"] }]
|
|
9275
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], valueSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueSet", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
9035
9276
|
|
|
9036
9277
|
/**
|
|
9037
9278
|
* Editor delle condizioni — FRONTEND.md §4.3 e trappole §13.1, §13.4, §13.13, §13.14.
|
|
@@ -9070,6 +9311,12 @@ class ConditionEditorComponent {
|
|
|
9070
9311
|
title = input('Condizioni', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9071
9312
|
/** `false` dove il modello non prevede la modalita' formula sull'elemento. */
|
|
9072
9313
|
allowFormula = input(true, ...(ngDevMode ? [{ debugName: "allowFormula" }] : []));
|
|
9314
|
+
/**
|
|
9315
|
+
* Le radici da proporre **per prime** nel reference picker (§5.2). Oggi lo passa un chiamante
|
|
9316
|
+
* solo: dentro una riga di sezione ripetibile la strada normale e' il **cursore**, e i campi
|
|
9317
|
+
* della riga non sono nemmeno riferimenti validi. Non aggiunge niente all'elenco: riordina.
|
|
9318
|
+
*/
|
|
9319
|
+
preferredRoots = input([], ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
9073
9320
|
/**
|
|
9074
9321
|
* `false` dove il modello porta **solo** la lista di condizioni, senza un campo per la
|
|
9075
9322
|
* logica: gli `entryConditions` / `exitConditions` di uno step di orchestrazione (§5.14).
|
|
@@ -9355,12 +9602,12 @@ class ConditionEditorComponent {
|
|
|
9355
9602
|
});
|
|
9356
9603
|
}
|
|
9357
9604
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ConditionEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9358
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ConditionEditorComponent, isStandalone: true, selector: "fb-condition-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null }, allowLogic: { classPropertyName: "allowLogic", publicName: "allowLogic", isSignal: true, isRequired: false, transformFunction: null }, issuePath: { classPropertyName: "issuePath", publicName: "issuePath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"], dependencies: [{ kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9605
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ConditionEditorComponent, isStandalone: true, selector: "fb-condition-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null }, allowLogic: { classPropertyName: "allowLogic", publicName: "allowLogic", isSignal: true, isRequired: false, transformFunction: null }, issuePath: { classPropertyName: "issuePath", publicName: "issuePath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"], dependencies: [{ kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9359
9606
|
}
|
|
9360
9607
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ConditionEditorComponent, decorators: [{
|
|
9361
9608
|
type: Component,
|
|
9362
|
-
args: [{ selector: 'fb-condition-editor', standalone: true, imports: [FormulaEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"] }]
|
|
9363
|
-
}], ctorParameters: () => [], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], allowLogic: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowLogic", required: false }] }], issuePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "issuePath", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
9609
|
+
args: [{ selector: 'fb-condition-editor', standalone: true, imports: [FormulaEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"] }]
|
|
9610
|
+
}], ctorParameters: () => [], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], allowLogic: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowLogic", required: false }] }], issuePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "issuePath", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
9364
9611
|
|
|
9365
9612
|
/**
|
|
9366
9613
|
* Editor dei filtri sui record — FRONTEND.md §4.4.
|
|
@@ -9667,7 +9914,7 @@ class RecordFilterEditorComponent {
|
|
|
9667
9914
|
return this.types.valueSetOf(filter.field);
|
|
9668
9915
|
}
|
|
9669
9916
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordFilterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9670
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordFilterEditorComponent, isStandalone: true, selector: "fb-record-filter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, fieldOptions: { classPropertyName: "fieldOptions", publicName: "fieldOptions", isSignal: true, isRequired: false, transformFunction: null }, supportsLogic: { classPropertyName: "supportsLogic", publicName: "supportsLogic", isSignal: true, isRequired: false, transformFunction: null }, supportsFormula: { classPropertyName: "supportsFormula", publicName: "supportsFormula", isSignal: true, isRequired: false, transformFunction: null }, emptyWarning: { classPropertyName: "emptyWarning", publicName: "emptyWarning", isSignal: true, isRequired: false, transformFunction: null }, emptyWarningSeverity: { classPropertyName: "emptyWarningSeverity", publicName: "emptyWarningSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9917
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordFilterEditorComponent, isStandalone: true, selector: "fb-record-filter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, fieldOptions: { classPropertyName: "fieldOptions", publicName: "fieldOptions", isSignal: true, isRequired: false, transformFunction: null }, supportsLogic: { classPropertyName: "supportsLogic", publicName: "supportsLogic", isSignal: true, isRequired: false, transformFunction: null }, supportsFormula: { classPropertyName: "supportsFormula", publicName: "supportsFormula", isSignal: true, isRequired: false, transformFunction: null }, emptyWarning: { classPropertyName: "emptyWarning", publicName: "emptyWarning", isSignal: true, isRequired: false, transformFunction: null }, emptyWarningSeverity: { classPropertyName: "emptyWarningSeverity", publicName: "emptyWarningSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9671
9918
|
}
|
|
9672
9919
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordFilterEditorComponent, decorators: [{
|
|
9673
9920
|
type: Component,
|
|
@@ -9721,6 +9968,12 @@ class ParameterEditorComponent {
|
|
|
9721
9968
|
* quei campi sono una destinazione, e per questo non e' un default ma un parametro del chiamante.
|
|
9722
9969
|
*/
|
|
9723
9970
|
extraTargets = input([], ...(ngDevMode ? [{ debugName: "extraTargets" }] : []));
|
|
9971
|
+
/**
|
|
9972
|
+
* Le radici da proporre **per prime**, in ingresso e in uscita (§5.2). Dentro una riga di sezione
|
|
9973
|
+
* ripetibile e' il cursore: e' lì che vanno letti gli input di un componente e assegnati i suoi
|
|
9974
|
+
* output, perche' una riga ha un cursore e i campi della riga non sono riferimenti.
|
|
9975
|
+
*/
|
|
9976
|
+
preferredRoots = input([], ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
9724
9977
|
changed = output();
|
|
9725
9978
|
inputs = computed(() => this.holder().inputParameters ?? [], ...(ngDevMode ? [{ debugName: "inputs" }] : []));
|
|
9726
9979
|
outputs = computed(() => this.holder().outputParameters ?? [], ...(ngDevMode ? [{ debugName: "outputs" }] : []));
|
|
@@ -9819,12 +10072,12 @@ class ParameterEditorComponent {
|
|
|
9819
10072
|
});
|
|
9820
10073
|
}
|
|
9821
10074
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ParameterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9822
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ParameterEditorComponent, isStandalone: true, selector: "fb-parameter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, catalogParameters: { classPropertyName: "catalogParameters", publicName: "catalogParameters", isSignal: true, isRequired: false, transformFunction: null }, inputTitle: { classPropertyName: "inputTitle", publicName: "inputTitle", isSignal: true, isRequired: false, transformFunction: null }, outputTitle: { classPropertyName: "outputTitle", publicName: "outputTitle", isSignal: true, isRequired: false, transformFunction: null }, showInputs: { classPropertyName: "showInputs", publicName: "showInputs", isSignal: true, isRequired: false, transformFunction: null }, showOutputs: { classPropertyName: "showOutputs", publicName: "showOutputs", isSignal: true, isRequired: false, transformFunction: null }, outputsDisabledReason: { classPropertyName: "outputsDisabledReason", publicName: "outputsDisabledReason", isSignal: true, isRequired: false, transformFunction: null }, extraTargets: { classPropertyName: "extraTargets", publicName: "extraTargets", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Nome e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Nome del parametro\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Nome del parametro di uscita\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro di uscita.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri di uscita dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [extraReferences]=\"extraTargets()\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10075
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ParameterEditorComponent, isStandalone: true, selector: "fb-parameter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, catalogParameters: { classPropertyName: "catalogParameters", publicName: "catalogParameters", isSignal: true, isRequired: false, transformFunction: null }, inputTitle: { classPropertyName: "inputTitle", publicName: "inputTitle", isSignal: true, isRequired: false, transformFunction: null }, outputTitle: { classPropertyName: "outputTitle", publicName: "outputTitle", isSignal: true, isRequired: false, transformFunction: null }, showInputs: { classPropertyName: "showInputs", publicName: "showInputs", isSignal: true, isRequired: false, transformFunction: null }, showOutputs: { classPropertyName: "showOutputs", publicName: "showOutputs", isSignal: true, isRequired: false, transformFunction: null }, outputsDisabledReason: { classPropertyName: "outputsDisabledReason", publicName: "outputsDisabledReason", isSignal: true, isRequired: false, transformFunction: null }, extraTargets: { classPropertyName: "extraTargets", publicName: "extraTargets", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Nome e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Nome del parametro\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Nome del parametro di uscita\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro di uscita.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri di uscita dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [extraReferences]=\"extraTargets()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9823
10076
|
}
|
|
9824
10077
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ParameterEditorComponent, decorators: [{
|
|
9825
10078
|
type: Component,
|
|
9826
|
-
args: [{ selector: 'fb-parameter-editor', standalone: true, imports: [NamePickerComponent, ReferencePickerComponent, ValueEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Nome e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Nome del parametro\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Nome del parametro di uscita\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro di uscita.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri di uscita dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [extraReferences]=\"extraTargets()\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}\n"] }]
|
|
9827
|
-
}], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], catalogParameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalogParameters", required: false }] }], inputTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputTitle", required: false }] }], outputTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputTitle", required: false }] }], showInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInputs", required: false }] }], showOutputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOutputs", required: false }] }], outputsDisabledReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputsDisabledReason", required: false }] }], extraTargets: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraTargets", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
10079
|
+
args: [{ selector: 'fb-parameter-editor', standalone: true, imports: [NamePickerComponent, ReferencePickerComponent, ValueEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Nome e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Nome del parametro\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Parametro</label>\r\n <fb-name-picker\r\n [value]=\"parameter.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Nome del parametro di uscita\"\r\n placeholder=\"Scrivi o scegli un parametro\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Il catalogo non dichiara questo parametro di uscita.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Parametri di uscita dichiarati non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [extraReferences]=\"extraTargets()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}\n"] }]
|
|
10080
|
+
}], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], catalogParameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalogParameters", required: false }] }], inputTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputTitle", required: false }] }], outputTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputTitle", required: false }] }], showInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInputs", required: false }] }], showOutputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOutputs", required: false }] }], outputsDisabledReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputsDisabledReason", required: false }] }], extraTargets: [{ type: i0.Input, args: [{ isSignal: true, alias: "extraTargets", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
9828
10081
|
|
|
9829
10082
|
/**
|
|
9830
10083
|
* Editor di `inputAssignments` — FRONTEND.md §5.8.
|
|
@@ -9908,7 +10161,7 @@ class FieldAssignmentEditorComponent {
|
|
|
9908
10161
|
return this.fields().find((field) => field.name === name);
|
|
9909
10162
|
}
|
|
9910
10163
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: FieldAssignmentEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9911
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: FieldAssignmentEditorComponent, isStandalone: true, selector: "fb-field-assignment-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, disabledReason: { classPropertyName: "disabledReason", publicName: "disabledReason", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (disabledReason()) {\r\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\r\n }\r\n @if (!object()) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\r\n }\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Campi obbligatori non valorizzati:\r\n @for (field of missingRequired(); track field.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\r\n + {{ field.label || field.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of assignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Campo e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n <fb-field-picker\r\n [value]=\"assignment.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"describe(assignment.field)?.dataType\"\r\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\r\n label=\"Valore del campo\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"remove($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\r\n</fieldset>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10164
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: FieldAssignmentEditorComponent, isStandalone: true, selector: "fb-field-assignment-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, disabledReason: { classPropertyName: "disabledReason", publicName: "disabledReason", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (disabledReason()) {\r\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\r\n }\r\n @if (!object()) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\r\n }\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Campi obbligatori non valorizzati:\r\n @for (field of missingRequired(); track field.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\r\n + {{ field.label || field.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of assignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <!-- Campo e valore sono una coppia: in colonna raddoppiavano l\u2019altezza dell\u2019elenco. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n <fb-field-picker\r\n [value]=\"assignment.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"describe(assignment.field)?.dataType\"\r\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\r\n label=\"Valore del campo\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-fields-row__action\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"remove($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\r\n</fieldset>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9912
10165
|
}
|
|
9913
10166
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: FieldAssignmentEditorComponent, decorators: [{
|
|
9914
10167
|
type: Component,
|
|
@@ -10405,6 +10658,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
10405
10658
|
type: Directive
|
|
10406
10659
|
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }] } });
|
|
10407
10660
|
|
|
10661
|
+
/**
|
|
10662
|
+
* Quante volte un nome e' **usato** nel documento.
|
|
10663
|
+
*
|
|
10664
|
+
* Serve dove togliere qualcosa rompe il resto: cancellare una risorsa non e' come cancellare
|
|
10665
|
+
* un elemento — nessun arco sparisce dal canvas, nessun rilievo compare finche' non si valida,
|
|
10666
|
+
* e i riferimenti orfani si scoprono dopo. Il conteggio e' cio' che rende visibile il danno
|
|
10667
|
+
* **prima**.
|
|
10668
|
+
*
|
|
10669
|
+
* Due regole, e sono le stesse della rinomina:
|
|
10670
|
+
*
|
|
10671
|
+
* - i riferimenti stanno nei campi di {@link FLOW_REFERENCE_FIELDS} — l'elenco e' **uno**, e
|
|
10672
|
+
* questo modulo lo importa invece di riscriverlo: due elenchi diventano due elenchi diversi
|
|
10673
|
+
* al primo campo aggiunto (§13.8);
|
|
10674
|
+
* - un riferimento vale anche **navigato**: `Ordine.Numero` e' un uso di `Ordine`.
|
|
10675
|
+
*
|
|
10676
|
+
* Piu' **due** che la rinomina non ha, e sono i due modi in cui una risorsa si cita fuori da un
|
|
10677
|
+
* campo di riferimento.
|
|
10678
|
+
*
|
|
10679
|
+
* Le **formule**: il motore di regole cita le risorse per nome dentro un'espressione
|
|
10680
|
+
* (`GetStreetToponym(Residenza_Street)`), quindi un conteggio che guardasse solo i campi di
|
|
10681
|
+
* riferimento direbbe «non usata» di una variabile che tre formule leggono. L'espressione non la
|
|
10682
|
+
* interpretiamo — la sintassi e' del motore (§6.3) — quindi si spezza in identificatori e si
|
|
10683
|
+
* confronta la radice: e' un'approssimazione, ed e' per questo che il risultato serve ad
|
|
10684
|
+
* **avvertire**, non a decidere al posto dell'utente.
|
|
10685
|
+
*
|
|
10686
|
+
* I **merge field**: `«Gentile {!Cliente.Nome},»` usa `Cliente` esattamente come lo userebbe una
|
|
10687
|
+
* condizione, e senza contarli una variabile citata da tre messaggi risultava «non usata» — che
|
|
10688
|
+
* e' l'informazione su cui si decide di cancellarla. Qui la sintassi dell'isola e' del
|
|
10689
|
+
* **contratto** e non del motore (§5.12), quindi riconoscerla non e' un'approssimazione; cio' che
|
|
10690
|
+
* resta approssimato e' il contenuto, che si tratta come un'espressione perche' puo' esserlo.
|
|
10691
|
+
*
|
|
10692
|
+
* L'elenco dei campi con merge field, la sintassi dell'isola e la sua riscrittura stanno in
|
|
10693
|
+
* `flow-merge-text.ts`, che e' un modulo foglia: le usa anche la rinomina, e quel modulo
|
|
10694
|
+
* (`flow-clipboard.ts`) questo qui lo importa già.
|
|
10695
|
+
*/
|
|
10696
|
+
/**
|
|
10697
|
+
* I campi che contengono un'espressione del motore di regole. `expression` e' quello di una
|
|
10698
|
+
* risorsa `Formula` (§4.6), `formulaExpression` quello di un valore e di una validation rule
|
|
10699
|
+
* (§5.2), `filterFormula` quello di un filtro di record (§5.8).
|
|
10700
|
+
*/
|
|
10701
|
+
const FLOW_FORMULA_FIELDS = new Set([
|
|
10702
|
+
'expression',
|
|
10703
|
+
'formulaExpression',
|
|
10704
|
+
'filterFormula',
|
|
10705
|
+
]);
|
|
10706
|
+
/** `true` se la stringa e' quel nome, o un percorso che parte da quel nome. */
|
|
10707
|
+
function isUseOf(value, name) {
|
|
10708
|
+
return value === name || value.startsWith(`${name}.`);
|
|
10709
|
+
}
|
|
10710
|
+
/**
|
|
10711
|
+
* Gli identificatori citati da un'espressione, ridotti alla loro **radice**: di
|
|
10712
|
+
* `OCRCIE.TaxCode` conta `OCRCIE`, perche' e' quella la risorsa. Senza la riduzione, un
|
|
10713
|
+
* membro chiamato come una variabile la farebbe sembrare usata.
|
|
10714
|
+
*/
|
|
10715
|
+
function rootsIn(expression) {
|
|
10716
|
+
return expression
|
|
10717
|
+
.split(/[^A-Za-z0-9_.$]+/)
|
|
10718
|
+
.filter(Boolean)
|
|
10719
|
+
.map((token) => token.split('.')[0])
|
|
10720
|
+
.filter(Boolean);
|
|
10721
|
+
}
|
|
10722
|
+
/**
|
|
10723
|
+
* Quante volte `name` compare come riferimento nel documento (o in un frammento).
|
|
10724
|
+
*
|
|
10725
|
+
* Non conta la **dichiarazione**: il campo `name` della risorsa o dell'elemento non e' un
|
|
10726
|
+
* campo di riferimento, quindi resta fuori da se'. E' la differenza con il conteggio della
|
|
10727
|
+
* rinomina, che serializza il documento e cerca la stringa: lì un elemento mai referenziato
|
|
10728
|
+
* «compare 1 volta».
|
|
10729
|
+
*/
|
|
10730
|
+
function countNameUses(root, name) {
|
|
10731
|
+
return describeNameUses(root, name).total;
|
|
10732
|
+
}
|
|
10733
|
+
/**
|
|
10734
|
+
* Gli usi di un nome **divisi per come sono scritti**.
|
|
10735
|
+
*
|
|
10736
|
+
* Il totale da solo non basta piu' da quando le categorie sono tre, e non perche' sia bello
|
|
10737
|
+
* dettagliarlo: le tre non si comportano allo stesso modo davanti a una **rinomina**. I
|
|
10738
|
+
* riferimenti e i merge field li riscrive l'editor; le espressioni delle formule no — la loro
|
|
10739
|
+
* sintassi e' del motore, e riscrivere dentro un'espressione che non si interpreta significa
|
|
10740
|
+
* indovinare. Chi mostra il numero puo' quindi dire quante occorrenze restano da correggere a
|
|
10741
|
+
* mano, invece di far scoprire dopo che una formula punta a un nome che non esiste piu'.
|
|
10742
|
+
*/
|
|
10743
|
+
function describeNameUses(root, name) {
|
|
10744
|
+
const uses = { references: 0, formulas: 0, mergeFields: 0, total: 0 };
|
|
10745
|
+
if (!name) {
|
|
10746
|
+
return uses;
|
|
10747
|
+
}
|
|
10748
|
+
const walk = (value) => {
|
|
10749
|
+
if (Array.isArray(value)) {
|
|
10750
|
+
value.forEach(walk);
|
|
10751
|
+
return;
|
|
10752
|
+
}
|
|
10753
|
+
if (!value || typeof value !== 'object') {
|
|
10754
|
+
return;
|
|
10755
|
+
}
|
|
10756
|
+
for (const [key, child] of Object.entries(value)) {
|
|
10757
|
+
if (typeof child === 'string') {
|
|
10758
|
+
if (FLOW_REFERENCE_FIELDS.has(key)) {
|
|
10759
|
+
if (isUseOf(child, name)) {
|
|
10760
|
+
uses.references += 1;
|
|
10761
|
+
}
|
|
10762
|
+
}
|
|
10763
|
+
else if (FLOW_FORMULA_FIELDS.has(key)) {
|
|
10764
|
+
uses.formulas += rootsIn(child).filter((root) => root === name).length;
|
|
10765
|
+
}
|
|
10766
|
+
else if (FLOW_MERGE_TEXT_FIELDS.has(key)) {
|
|
10767
|
+
// Il contenuto dell'isola si tratta come un'espressione: dentro `{!...}` ci puo'
|
|
10768
|
+
// stare un percorso (`{!Cliente.Nome}`) o una chiamata, e la radice e' cio' che
|
|
10769
|
+
// nomina la risorsa. Un testo senza `{!` non produce nessun candidato, quindi la
|
|
10770
|
+
// frase in italiano di una label non fa comparire usi che non esistono.
|
|
10771
|
+
for (const island of mergeFieldIslands(child)) {
|
|
10772
|
+
uses.mergeFields += rootsIn(island).filter((root) => root === name).length;
|
|
10773
|
+
}
|
|
10774
|
+
}
|
|
10775
|
+
continue;
|
|
10776
|
+
}
|
|
10777
|
+
if (FLOW_REFERENCE_FIELDS.has(key) && Array.isArray(child) && child.every((item) => typeof item === 'string')) {
|
|
10778
|
+
// `choiceReferences` e' un array di **nomi**: scendendoci dentro non si troverebbe
|
|
10779
|
+
// nessuna stringa da confrontare, perche' le stringhe sono gli elementi.
|
|
10780
|
+
uses.references += child.filter((item) => isUseOf(item, name)).length;
|
|
10781
|
+
continue;
|
|
10782
|
+
}
|
|
10783
|
+
walk(child);
|
|
10784
|
+
}
|
|
10785
|
+
};
|
|
10786
|
+
walk(root);
|
|
10787
|
+
uses.total = uses.references + uses.formulas + uses.mergeFields;
|
|
10788
|
+
return uses;
|
|
10789
|
+
}
|
|
10790
|
+
|
|
10408
10791
|
/**
|
|
10409
10792
|
* Action — FRONTEND.md §5.9.
|
|
10410
10793
|
*
|
|
@@ -10566,7 +10949,7 @@ class ActionCallInspectorComponent extends NodeInspectorBase {
|
|
|
10566
10949
|
this.patch((node) => mutate(node));
|
|
10567
10950
|
}
|
|
10568
10951
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ActionCallInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10569
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ActionCallInspectorComponent, isStandalone: true, selector: "fb-action-call-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action().actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action().actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: la validazione la segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Esecuzione</legend>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Modello transazionale</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action().flowTransactionModel || ''\"\r\n (change)=\"setTransactionModel($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (model of transactionModels(); track model.value) {\r\n <option [value]=\"model.value\">{{ model.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action().isWaitUntilCompleted === true\"\r\n (change)=\"setWaitUntilCompleted($any($event.target).checked)\"\r\n />\r\n Attendi il completamento\r\n </label>\r\n\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Attesa</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"action().offset ?? ''\"\r\n (input)=\"setOffset($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action().offsetUnit || ''\"\r\n (change)=\"setOffsetUnit($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">Definiscono l\u2019attesa di un\u2019action asincrona.</p>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ramo di timeout</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"timeoutEnabled()\"\r\n (change)=\"setTimeoutEnabled($any($event.target).checked)\"\r\n />\r\n Abilita il ramo di timeout\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Il ramo esiste solo con questo flag: qui il flag e la destinazione sono legati, cos\u00EC non possono\r\n contraddirsi.\r\n </p>\r\n @if (timeoutMismatch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il flag e il ramo di timeout non sono coerenti fra loro.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Output</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Gli output dell\u2019action si referenziano col nome dell\u2019elemento: <code>{{ name() }}.NomeOutput</code>.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"action()\"\r\n [catalogParameters]=\"parameterCatalog()\"\r\n [showOutputs]=\"!storeOutputAutomatically()\"\r\n [outputsDisabledReason]=\"\r\n storeOutputAutomatically() ? 'Con l\u2019output automatico i parametri di uscita non servono.' : null\r\n \"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Le tre uscite\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10952
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ActionCallInspectorComponent, isStandalone: true, selector: "fb-action-call-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action().actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action().actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: la validazione la segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Esecuzione</legend>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Modello transazionale</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action().flowTransactionModel || ''\"\r\n (change)=\"setTransactionModel($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (model of transactionModels(); track model.value) {\r\n <option [value]=\"model.value\">{{ model.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action().isWaitUntilCompleted === true\"\r\n (change)=\"setWaitUntilCompleted($any($event.target).checked)\"\r\n />\r\n Attendi il completamento\r\n </label>\r\n\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Attesa</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"action().offset ?? ''\"\r\n (input)=\"setOffset($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action().offsetUnit || ''\"\r\n (change)=\"setOffsetUnit($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">Definiscono l\u2019attesa di un\u2019action asincrona.</p>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ramo di timeout</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"timeoutEnabled()\"\r\n (change)=\"setTimeoutEnabled($any($event.target).checked)\"\r\n />\r\n Abilita il ramo di timeout\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Il ramo esiste solo con questo flag: qui il flag e la destinazione sono legati, cos\u00EC non possono\r\n contraddirsi.\r\n </p>\r\n @if (timeoutMismatch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il flag e il ramo di timeout non sono coerenti fra loro.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Output</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Gli output dell\u2019action si referenziano col nome dell\u2019elemento: <code>{{ name() }}.NomeOutput</code>.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"action()\"\r\n [catalogParameters]=\"parameterCatalog()\"\r\n [showOutputs]=\"!storeOutputAutomatically()\"\r\n [outputsDisabledReason]=\"\r\n storeOutputAutomatically() ? 'Con l\u2019output automatico i parametri di uscita non servono.' : null\r\n \"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Le tre uscite\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10570
10953
|
}
|
|
10571
10954
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ActionCallInspectorComponent, decorators: [{
|
|
10572
10955
|
type: Component,
|
|
@@ -10716,7 +11099,7 @@ class AssignmentInspectorComponent extends NodeInspectorBase {
|
|
|
10716
11099
|
return !operator.startsWith('Remove') || operator === 'RemovePosition' || operator === 'RemoveUncommon';
|
|
10717
11100
|
}
|
|
10718
11101
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: AssignmentInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10719
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Destinazione, operazione e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Vale anche dentro una classe \u2014 \u00ABRichiesta.Stato\u00BB \u2014 perche'\r\n il tipo si ricava navigando i membri (\u00A74.7.1). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"targetDataType(item)\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11102
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Destinazione, operazione e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Vale anche dentro una classe \u2014 \u00ABRichiesta.Stato\u00BB \u2014 perche'\r\n il tipo si ricava navigando i membri (\u00A74.7.1). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"targetDataType(item)\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10720
11103
|
}
|
|
10721
11104
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: AssignmentInspectorComponent, decorators: [{
|
|
10722
11105
|
type: Component,
|
|
@@ -10837,7 +11220,7 @@ class CollectionProcessorInspectorComponent extends NodeInspectorBase {
|
|
|
10837
11220
|
this.patch((node) => mutate(node));
|
|
10838
11221
|
}
|
|
10839
11222
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: CollectionProcessorInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
10840
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: CollectionProcessorInspectorComponent, isStandalone: true, selector: "fb-collection-processor-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cosa fa</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"processor().collectionProcessorType || ''\"\r\n (change)=\"setKind($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (kind of kinds(); track kind.value) {\r\n <option [value]=\"kind.value\">{{ kind.label }}</option>\r\n }\r\n </select>\r\n @if (!hasKind()) {\r\n <p class=\"fb-field__error\">Senza questo valore l\u2019elemento non e\u2019 eseguibile (PROCESSOR_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection di partenza</label>\r\n <fb-reference-picker\r\n [value]=\"processor().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n</div>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato non si assegna a una variabile: e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento, e si\r\n referenzia col suo nome \u2014 <code>{{ name() }}</code>.\r\n</p>\r\n\r\n@if (isSort()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ordinamento</legend>\r\n <div class=\"fb-list\">\r\n @for (option of sortOptions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveSortOption($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveSortOption($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeSortOption($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"option.sortField || ''\"\r\n placeholder=\"Nome del campo degli elementi\"\r\n (input)=\"setSortField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"option.sortOrder || ''\"\r\n (change)=\"setSortOrder($index, $any($event.target).value)\"\r\n >\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"option.doesPutEmptyStringAndNullFirst === true\"\r\n (change)=\"setEmptyFirst($index, $any($event.target).checked)\"\r\n />\r\n Metti prima i valori vuoti e nulli\r\n </label>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Un Sort senza criteri di ordinamento e\u2019 un errore (SORT_OPTIONS_MISSING).</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addSortOption()\">Aggiungi criterio</button>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di elementi</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"processor().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (isFilter()) {\r\n <div class=\"fb-field\">\r\n <!--\r\n Trappola: questo NON e' dove finisce il risultato. \u00C8 la variabile che espone alle\r\n condizioni l'elemento che si sta esaminando, esattamente come nel Loop.\r\n -->\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento in esame</label>\r\n <fb-reference-picker\r\n [value]=\"processor().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"elementDataType()\"\r\n [objectType]=\"elementObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che espone l\u2019elemento alle condizioni\"\r\n (valueChange)=\"setCurrentElementVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Questa variabile serve <strong>a scrivere le condizioni</strong> qui sotto: non e\u2019 la destinazione del\r\n risultato.\r\n </p>\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"processor()\"\r\n title=\"Tieni gli elementi per cui\"\r\n (changed)=\"onConditionsChanged($event)\"\r\n />\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11223
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: CollectionProcessorInspectorComponent, isStandalone: true, selector: "fb-collection-processor-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cosa fa</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"processor().collectionProcessorType || ''\"\r\n (change)=\"setKind($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (kind of kinds(); track kind.value) {\r\n <option [value]=\"kind.value\">{{ kind.label }}</option>\r\n }\r\n </select>\r\n @if (!hasKind()) {\r\n <p class=\"fb-field__error\">Senza questo valore l\u2019elemento non e\u2019 eseguibile (PROCESSOR_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection di partenza</label>\r\n <fb-reference-picker\r\n [value]=\"processor().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n</div>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato non si assegna a una variabile: e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento, e si\r\n referenzia col suo nome \u2014 <code>{{ name() }}</code>.\r\n</p>\r\n\r\n@if (isSort()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ordinamento</legend>\r\n <div class=\"fb-list\">\r\n @for (option of sortOptions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveSortOption($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveSortOption($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeSortOption($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"option.sortField || ''\"\r\n placeholder=\"Nome del campo degli elementi\"\r\n (input)=\"setSortField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"option.sortOrder || ''\"\r\n (change)=\"setSortOrder($index, $any($event.target).value)\"\r\n >\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"option.doesPutEmptyStringAndNullFirst === true\"\r\n (change)=\"setEmptyFirst($index, $any($event.target).checked)\"\r\n />\r\n Metti prima i valori vuoti e nulli\r\n </label>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Un Sort senza criteri di ordinamento e\u2019 un errore (SORT_OPTIONS_MISSING).</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addSortOption()\">Aggiungi criterio</button>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di elementi</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"processor().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (isFilter()) {\r\n <div class=\"fb-field\">\r\n <!--\r\n Trappola: questo NON e' dove finisce il risultato. \u00C8 la variabile che espone alle\r\n condizioni l'elemento che si sta esaminando, esattamente come nel Loop.\r\n -->\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento in esame</label>\r\n <fb-reference-picker\r\n [value]=\"processor().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"elementDataType()\"\r\n [objectType]=\"elementObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che espone l\u2019elemento alle condizioni\"\r\n (valueChange)=\"setCurrentElementVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Questa variabile serve <strong>a scrivere le condizioni</strong> qui sotto: non e\u2019 la destinazione del\r\n risultato.\r\n </p>\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"processor()\"\r\n title=\"Tieni gli elementi per cui\"\r\n (changed)=\"onConditionsChanged($event)\"\r\n />\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "preferredRoots", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10841
11224
|
}
|
|
10842
11225
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: CollectionProcessorInspectorComponent, decorators: [{
|
|
10843
11226
|
type: Component,
|
|
@@ -11019,7 +11402,7 @@ class DecisionInspectorComponent extends NodeInspectorBase {
|
|
|
11019
11402
|
this.setField('defaultConnectorLabel', label);
|
|
11020
11403
|
}
|
|
11021
11404
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: DecisionInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
11022
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: DecisionInspectorComponent, isStandalone: true, selector: "fb-decision-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\r\n parte del significato del flow: usa le frecce per cambiarlo.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (rule of rules(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Valuta prima\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveRule($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Valuta dopo\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveRule($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"rule.label || ''\"\r\n placeholder=\"Approvato\"\r\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\r\n />\r\n @if (!rule.label) {\r\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!ruleNameError($index)\"\r\n [value]=\"rule.name || ''\"\r\n (input)=\"setRuleName($index, $any($event.target).value)\"\r\n />\r\n @if (ruleNameError($index)) {\r\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\r\n }\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"Quando prendere questo ramo\"\r\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\r\n (changed)=\"onRuleConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\r\n (DECISION_WITHOUT_RULES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"defaultLabel()\"\r\n placeholder=\"Rifiutato\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n @if (!hasDefaultTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11405
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: DecisionInspectorComponent, isStandalone: true, selector: "fb-decision-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\r\n parte del significato del flow: usa le frecce per cambiarlo.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (rule of rules(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Valuta prima\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveRule($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Valuta dopo\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveRule($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"rule.label || ''\"\r\n placeholder=\"Approvato\"\r\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\r\n />\r\n @if (!rule.label) {\r\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!ruleNameError($index)\"\r\n [value]=\"rule.name || ''\"\r\n (input)=\"setRuleName($index, $any($event.target).value)\"\r\n />\r\n @if (ruleNameError($index)) {\r\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\r\n }\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"Quando prendere questo ramo\"\r\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\r\n (changed)=\"onRuleConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\r\n (DECISION_WITHOUT_RULES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"defaultLabel()\"\r\n placeholder=\"Rifiutato\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n @if (!hasDefaultTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "preferredRoots", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11023
11406
|
}
|
|
11024
11407
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: DecisionInspectorComponent, decorators: [{
|
|
11025
11408
|
type: Component,
|
|
@@ -11085,6 +11468,7 @@ const FIELD_ICONS = {
|
|
|
11085
11468
|
ComponentInstance: '⬡',
|
|
11086
11469
|
Region: '▯',
|
|
11087
11470
|
RegionContainer: '▣',
|
|
11471
|
+
RepeatingSection: '⧉',
|
|
11088
11472
|
ObjectProvided: '⌗',
|
|
11089
11473
|
};
|
|
11090
11474
|
class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
@@ -11314,6 +11698,18 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11314
11698
|
isContainer(field) {
|
|
11315
11699
|
return this.dictionary.screenFieldIsContainer(field?.fieldType);
|
|
11316
11700
|
}
|
|
11701
|
+
isRepeatingSection(field) {
|
|
11702
|
+
return this.dictionary.screenFieldIsRepeatingSection(field?.fieldType);
|
|
11703
|
+
}
|
|
11704
|
+
/**
|
|
11705
|
+
* §5.2 — il campo ha dei `fields` dentro, quale che sia il loro significato: e' la domanda che
|
|
11706
|
+
* serve al rilascio e al comando «aggiungi qui». I due flag restano distinti — cio' che sta in una
|
|
11707
|
+
* sezione ripetibile e' il **modello di una riga**, non campi della schermata — ma per decidere
|
|
11708
|
+
* *dove* finisce un campo trascinato la differenza non conta.
|
|
11709
|
+
*/
|
|
11710
|
+
holdsFields(field) {
|
|
11711
|
+
return this.isContainer(field) || this.isRepeatingSection(field);
|
|
11712
|
+
}
|
|
11317
11713
|
/** `true` sulla sezione, che dispone i figli **in riga**: le colonne stanno una accanto all'altra. */
|
|
11318
11714
|
isSection(field) {
|
|
11319
11715
|
return field?.fieldType === 'RegionContainer';
|
|
@@ -11617,7 +12013,7 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11617
12013
|
if (!path) {
|
|
11618
12014
|
return { parent: [], index: this.screen().fields?.length ?? 0 };
|
|
11619
12015
|
}
|
|
11620
|
-
if (this.
|
|
12016
|
+
if (this.holdsFields(this.selectedField())) {
|
|
11621
12017
|
return { parent: path, index: fieldAt(this.screen().fields, path)?.fields?.length ?? 0 };
|
|
11622
12018
|
}
|
|
11623
12019
|
return { parent: path.slice(0, -1), index: path[path.length - 1] + 1 };
|
|
@@ -11625,6 +12021,17 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11625
12021
|
addComponentAtSelection(type) {
|
|
11626
12022
|
this.addComponent(type, this.clickTarget());
|
|
11627
12023
|
}
|
|
12024
|
+
/** Se il tipo puo' finire dove il clic lo metterebbe: e' cio' che disabilita il chip della palette. */
|
|
12025
|
+
canAddAtSelection(type) {
|
|
12026
|
+
return this.canPlaceAt(type, this.clickTarget());
|
|
12027
|
+
}
|
|
12028
|
+
/** §5.2 — l'unico divieto di collocazione: una sezione ripetibile dentro un'altra. */
|
|
12029
|
+
canPlaceAt(type, target) {
|
|
12030
|
+
if (!this.dictionary.screenFieldIsRepeatingSection(type)) {
|
|
12031
|
+
return true;
|
|
12032
|
+
}
|
|
12033
|
+
return (repeatingSectionOf(this.screen(), [...target.parent, target.index], this.typeOfField) === undefined);
|
|
12034
|
+
}
|
|
11628
12035
|
addObjectFieldAtSelection(field) {
|
|
11629
12036
|
this.addObjectField(field, this.clickTarget());
|
|
11630
12037
|
}
|
|
@@ -11634,6 +12041,12 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11634
12041
|
* primo gesto sarebbe sempre lo stesso.
|
|
11635
12042
|
*/
|
|
11636
12043
|
addComponent(type, target) {
|
|
12044
|
+
// §5.2 — le sezioni ripetibili **non si annidano**: il cursore e' una variabile sola per sezione,
|
|
12045
|
+
// e due sezioni annidate ne vorrebbero due vive contemporaneamente. La tendina dei tipi la
|
|
12046
|
+
// esclude già, ma il trascinamento dalla palette da lì non passa.
|
|
12047
|
+
if (!this.canPlaceAt(type, target)) {
|
|
12048
|
+
return;
|
|
12049
|
+
}
|
|
11637
12050
|
const label = this.dictionary.screenFieldType(type)?.label ?? type;
|
|
11638
12051
|
const used = [...this.store.usedNames()];
|
|
11639
12052
|
const field = { name: uniqueFlowName(label, used), fieldType: type };
|
|
@@ -11816,6 +12229,107 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11816
12229
|
return dot < 0 ? '' : reference.slice(dot + 1);
|
|
11817
12230
|
}, ...(ngDevMode ? [{ debugName: "providedField" }] : []));
|
|
11818
12231
|
// -------------------------------------------------------------------------
|
|
12232
|
+
// §5.2 — sezioni ripetibili: cosa cambia quando si sta dentro una riga
|
|
12233
|
+
// -------------------------------------------------------------------------
|
|
12234
|
+
/** Come si legge la voce di dizionario di un campo: il flag e' del dizionario, non del codice. */
|
|
12235
|
+
typeOfField = (field) => this.dictionary.screenFieldType(field.fieldType);
|
|
12236
|
+
selectedIsRepeatingSection = computed(() => this.selectedType()?.isRepeatingSection === true, ...(ngDevMode ? [{ debugName: "selectedIsRepeatingSection" }] : []));
|
|
12237
|
+
/**
|
|
12238
|
+
* La sezione ripetibile che **contiene** il campo selezionato, se c'e'. Antenato stretto: sulla
|
|
12239
|
+
* sezione stessa e' `undefined`, perche' configurare la sezione e configurare un campo di una riga
|
|
12240
|
+
* sono due pannelli diversi.
|
|
12241
|
+
*/
|
|
12242
|
+
selectedRepeater = computed(() => repeatingSectionOf(this.screen(), this.selectedPath(), this.typeOfField), ...(ngDevMode ? [{ debugName: "selectedRepeater" }] : []));
|
|
12243
|
+
isInsideRepeater = computed(() => this.selectedRepeater() !== undefined, ...(ngDevMode ? [{ debugName: "isInsideRepeater" }] : []));
|
|
12244
|
+
/**
|
|
12245
|
+
* Il **cursore** della riga in cui si sta: e' cio' che si nomina lì dentro (§5.2). Scrivere il nome
|
|
12246
|
+
* del campo — `Luce_POD` — e' `SCREEN_FIELD_IN_REPEATER`, perche' quel campo esiste in una riga per
|
|
12247
|
+
* elemento della collection e il riferimento non ne individua nessuna.
|
|
12248
|
+
*/
|
|
12249
|
+
rowCursor = computed(() => this.selectedRepeater()?.itemReference ?? '', ...(ngDevMode ? [{ debugName: "rowCursor" }] : []));
|
|
12250
|
+
/**
|
|
12251
|
+
* Cosa proporre **per primo** nei selettori di riferimento del campo selezionato. Dentro una riga
|
|
12252
|
+
* e' il cursore: e' la strada normale, e i campi della riga — che sono cio' che uno cerca per
|
|
12253
|
+
* istinto — non compaiono affatto, perche' non sono riferimenti validi. Fuori e' vuoto, cioe'
|
|
12254
|
+
* l'ordine di sempre.
|
|
12255
|
+
*/
|
|
12256
|
+
preferredRoots = computed(() => {
|
|
12257
|
+
const cursor = this.rowCursor();
|
|
12258
|
+
return cursor ? [cursor] : [];
|
|
12259
|
+
}, ...(ngDevMode ? [{ debugName: "preferredRoots" }] : []));
|
|
12260
|
+
/**
|
|
12261
|
+
* I tipi proponibili per il campo selezionato. Dentro una riga manca la sezione ripetibile: **non si
|
|
12262
|
+
* annidano** — il cursore e' una variabile sola per sezione, e due sezioni annidate ne vorrebbero due
|
|
12263
|
+
* vive contemporaneamente. La validazione lo rifiuta comunque, ma un editor che lascia comporre e poi
|
|
12264
|
+
* accusa fa perdere tempo due volte.
|
|
12265
|
+
*/
|
|
12266
|
+
fieldTypeOptions = computed(() => {
|
|
12267
|
+
const all = this.dictionary.screenFieldTypes();
|
|
12268
|
+
return this.isInsideRepeater() ? all.filter((entry) => entry.isRepeatingSection !== true) : all;
|
|
12269
|
+
}, ...(ngDevMode ? [{ debugName: "fieldTypeOptions" }] : []));
|
|
12270
|
+
/**
|
|
12271
|
+
* La classe delle righe: l'`objectType` della **variabile** in `collectionReference` (§5.2). Si
|
|
12272
|
+
* legge dal documento e non da `getReferences` perche' i due riferimenti di una sezione sono
|
|
12273
|
+
* variabili per forza — ci scrive il runtime — e la risposta serve sincrona, dentro il template.
|
|
12274
|
+
*/
|
|
12275
|
+
rowClassOf(section) {
|
|
12276
|
+
return this.variableTypeOf(section?.collectionReference)?.objectType;
|
|
12277
|
+
}
|
|
12278
|
+
/** La classe con cui si popolano le tendine dei membri: quella della sezione, dentro o fuori. */
|
|
12279
|
+
rowClass = computed(() => this.rowClassOf(this.selectedIsRepeatingSection() ? this.selectedField() : this.selectedRepeater()), ...(ngDevMode ? [{ debugName: "rowClass" }] : []));
|
|
12280
|
+
/**
|
|
12281
|
+
* §5.2 — le incoerenze dei due riferimenti sono tutte `REPEATING_SECTION_INVALID`, e si dicono qui
|
|
12282
|
+
* invece di aspettare la validazione: riferimento mancante, costante al posto di una variabile,
|
|
12283
|
+
* collection scambiata col cursore, due classi diverse.
|
|
12284
|
+
*/
|
|
12285
|
+
repeaterIssue = computed(() => {
|
|
12286
|
+
const section = this.selectedField();
|
|
12287
|
+
if (!this.selectedIsRepeatingSection() || !section) {
|
|
12288
|
+
return null;
|
|
12289
|
+
}
|
|
12290
|
+
const collection = section.collectionReference;
|
|
12291
|
+
const item = section.itemReference;
|
|
12292
|
+
if (!collection || !item) {
|
|
12293
|
+
return 'Servono entrambi i riferimenti: la collection in cui vivono le righe e il cursore su cui il runtime appoggia la riga corrente.';
|
|
12294
|
+
}
|
|
12295
|
+
const collectionType = this.variableTypeOf(collection);
|
|
12296
|
+
const itemType = this.variableTypeOf(item);
|
|
12297
|
+
if (!collectionType) {
|
|
12298
|
+
return `«${collection}» non e’ una variabile del flow: le righe le scrive il runtime, quindi dev’essere una variabile.`;
|
|
12299
|
+
}
|
|
12300
|
+
if (!itemType) {
|
|
12301
|
+
return `«${item}» non e’ una variabile del flow: il cursore lo scrive il runtime.`;
|
|
12302
|
+
}
|
|
12303
|
+
if (collectionType.dataType !== 'Structure' || itemType.dataType !== 'Structure') {
|
|
12304
|
+
return 'Entrambi i riferimenti devono essere di tipo Structure: la riga e’ un’istanza di classe.';
|
|
12305
|
+
}
|
|
12306
|
+
if (!collectionType.isCollection) {
|
|
12307
|
+
return `«${collection}» non e’ una collection: e’ lì che vivono le righe.`;
|
|
12308
|
+
}
|
|
12309
|
+
if (itemType.isCollection) {
|
|
12310
|
+
return `«${item}» e’ una collection: il cursore tiene **una** riga per volta, come l’assignNextValueToReference di un Loop.`;
|
|
12311
|
+
}
|
|
12312
|
+
if (collectionType.objectType !== itemType.objectType) {
|
|
12313
|
+
return `Le due variabili dichiarano classi diverse (${collectionType.objectType ?? '—'} e ${itemType.objectType ?? '—'}): il cursore deve essere della stessa classe delle righe.`;
|
|
12314
|
+
}
|
|
12315
|
+
return null;
|
|
12316
|
+
}, ...(ngDevMode ? [{ debugName: "repeaterIssue" }] : []));
|
|
12317
|
+
/** §5.2 — il massimo mancante e' un **avviso**: senza, l'utente puo' aggiungere righe all'infinito. */
|
|
12318
|
+
repeaterHasNoLimit = computed(() => this.selectedIsRepeatingSection() && this.selectedField()?.maxInstances === undefined, ...(ngDevMode ? [{ debugName: "repeaterHasNoLimit" }] : []));
|
|
12319
|
+
/**
|
|
12320
|
+
* La variabile del documento con quel nome, se e' una variabile. Non un riferimento qualunque: una
|
|
12321
|
+
* costante o una formula qui e' `REPEATING_SECTION_INVALID`, e distinguerle e' il punto.
|
|
12322
|
+
*/
|
|
12323
|
+
variableTypeOf(name) {
|
|
12324
|
+
if (!name) {
|
|
12325
|
+
return undefined;
|
|
12326
|
+
}
|
|
12327
|
+
const match = this.store
|
|
12328
|
+
.resources()
|
|
12329
|
+
.find((entry) => entry.collection === 'variables' && entry.name === name);
|
|
12330
|
+
return match?.resource;
|
|
12331
|
+
}
|
|
12332
|
+
// -------------------------------------------------------------------------
|
|
11819
12333
|
// Mutazioni sul campo selezionato
|
|
11820
12334
|
// -------------------------------------------------------------------------
|
|
11821
12335
|
/** Scrive una proprieta' del campo selezionato; valore vuoto → chiave omessa (§2). */
|
|
@@ -11878,10 +12392,25 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11878
12392
|
delete field.defaultSelectedChoiceReference;
|
|
11879
12393
|
delete field.autoSelectSingleChoice;
|
|
11880
12394
|
}
|
|
11881
|
-
|
|
12395
|
+
// I `fields` di una sezione ripetibile sono il **modello della riga**: `isContainer` e' falso —
|
|
12396
|
+
// quei campi non sono campi della schermata — ma cancellarli butterebbe via la riga (§5.2).
|
|
12397
|
+
if (!entry?.isContainer && entry?.isRepeatingSection !== true) {
|
|
11882
12398
|
delete field.fields;
|
|
12399
|
+
}
|
|
12400
|
+
if (!entry?.isContainer) {
|
|
11883
12401
|
delete field.regionContainerType;
|
|
11884
12402
|
}
|
|
12403
|
+
if (entry?.isRepeatingSection !== true) {
|
|
12404
|
+
delete field.collectionReference;
|
|
12405
|
+
delete field.itemReference;
|
|
12406
|
+
delete field.minInstances;
|
|
12407
|
+
delete field.maxInstances;
|
|
12408
|
+
delete field.allowAdd;
|
|
12409
|
+
delete field.allowRemove;
|
|
12410
|
+
delete field.addButtonLabel;
|
|
12411
|
+
delete field.removeButtonLabel;
|
|
12412
|
+
delete field.itemLabel;
|
|
12413
|
+
}
|
|
11885
12414
|
if (type !== 'ComponentInstance') {
|
|
11886
12415
|
delete field.extensionName;
|
|
11887
12416
|
delete field.inputParameters;
|
|
@@ -11902,6 +12431,20 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
11902
12431
|
const parsed = Number(raw);
|
|
11903
12432
|
this.setFieldProperty(property, raw === '' || Number.isNaN(parsed) ? undefined : parsed);
|
|
11904
12433
|
}
|
|
12434
|
+
/**
|
|
12435
|
+
* §5.2 — `allowAdd` e `allowRemove` hanno default `true`: si scrive solo il `false`, che e' la
|
|
12436
|
+
* scelta significativa. È la stessa regola dei flag del node (§2), non quella di un `FlowValue`.
|
|
12437
|
+
*/
|
|
12438
|
+
setRepeaterFlag(property, value) {
|
|
12439
|
+
this.patchField((field) => {
|
|
12440
|
+
if (value) {
|
|
12441
|
+
delete field[property];
|
|
12442
|
+
}
|
|
12443
|
+
else {
|
|
12444
|
+
field[property] = false;
|
|
12445
|
+
}
|
|
12446
|
+
});
|
|
12447
|
+
}
|
|
11905
12448
|
/** `isRequired` si scrive solo quando e' vero: il default e' facoltativo. */
|
|
11906
12449
|
setRequired(value) {
|
|
11907
12450
|
this.patchField((field) => {
|
|
@@ -12066,9 +12609,17 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
12066
12609
|
isWritable: true,
|
|
12067
12610
|
description: 'campo di questa schermata',
|
|
12068
12611
|
})), ...(ngDevMode ? [{ debugName: "screenFieldTargets" }] : []));
|
|
12069
|
-
/**
|
|
12612
|
+
/**
|
|
12613
|
+
* I campi che raccolgono un valore: gli unici che un'action possa riempire, e gli unici che
|
|
12614
|
+
* innescano.
|
|
12615
|
+
*
|
|
12616
|
+
* **Senza quelli dentro una riga**, che raccolgono un valore ma non sono risorse: un trigger nomina
|
|
12617
|
+
* un campo, e il campo di una riga non ne ha uno solo — sarebbe da dichiarare *quale* riga osserva.
|
|
12618
|
+
* Un `triggerFieldName` che punta lì e' `SCREEN_FIELD_IN_REPEATER`, e lo stesso vale per
|
|
12619
|
+
* l'`assignToReference` di un output (§5.2).
|
|
12620
|
+
*/
|
|
12070
12621
|
valueFieldsOfScreen() {
|
|
12071
|
-
return
|
|
12622
|
+
return resourceFieldsOf(this.screen(), this.typeOfField);
|
|
12072
12623
|
}
|
|
12073
12624
|
/**
|
|
12074
12625
|
* I campi proponibili come innesco. Sono **i campi di questa schermata**, non i riferimenti in
|
|
@@ -12337,7 +12888,7 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
|
|
|
12337
12888
|
});
|
|
12338
12889
|
}
|
|
12339
12890
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: DynamicScreenInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12340
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: DynamicScreenInspectorComponent, isStandalone: true, selector: "fb-dynamic-screen-inspector", inputs: { initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "composer", first: true, predicate: ["composer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"fb-scr\" #composer>\r\n <!-- ============================================================ sinistra -->\r\n <aside class=\"fb-scr__side\" aria-label=\"Componenti e campi\">\r\n <div class=\"fb-scr__tabs\" role=\"tablist\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'components'\"\r\n [attr.aria-selected]=\"paletteTab() === 'components'\"\r\n (click)=\"setPaletteTab('components')\"\r\n >\r\n Componenti\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'fields'\"\r\n [attr.aria-selected]=\"paletteTab() === 'fields'\"\r\n (click)=\"setPaletteTab('fields')\"\r\n >\r\n Campi\r\n </button>\r\n </div>\r\n\r\n @if (paletteTab() === 'components') {\r\n @if (!dictionary.screenFieldTypes().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il dizionario <code>screenFieldTypes</code> non e\u2019 disponibile: senza i suoi flag l\u2019editor non\r\n sa quali campi ammette ciascun tipo, e non li inventa.\r\n </p>\r\n }\r\n <p class=\"fb-scr__side-hint\">Trascina sulla schermata, oppure clicca per aggiungere.</p>\r\n <ul class=\"fb-scr__palette\">\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"entry.description || entry.label\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onComponentDragEnded($event, entry.value)\"\r\n (click)=\"addComponentAtSelection(entry.value)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">{{ iconOf(entry.value) }}</span>\r\n <span class=\"fb-scr__chip-label\">{{ entry.label }}</span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p class=\"fb-scr__side-hint\">\r\n Un campo dell\u2019entita\u2019 porta con se\u2019 tipo, etichetta, obbligatorieta\u2019 e opzioni: arrivano dallo\r\n schema dati, qui non si ridichiarano.\r\n </p>\r\n <!-- Il catalogo non e' un dizionario chiuso: si sceglie o si scrive, come ovunque si\r\n indichi un'entita' (vedi `object-picker`). -->\r\n <fb-object-picker\r\n [value]=\"fieldsObject()\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Scrivi o scegli un\u2019entita\u2019\"\r\n (valueChange)=\"setFieldsObject($event)\"\r\n />\r\n @if (fieldsObject() && !fieldsOfObject().length) {\r\n <p class=\"fb-scr__side-empty\">\r\n Nessun campo dichiarato per questa entita\u2019: il catalogo non li espone, non significa che non\r\n ci siano.\r\n </p>\r\n }\r\n <ul class=\"fb-scr__palette\">\r\n @for (field of fieldsOfObject(); track $index) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"field.name + (field.dataType ? ' \u00B7 ' + field.dataType : '')\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onObjectFieldDragEnded($event, field)\"\r\n (click)=\"addObjectFieldAtSelection(field)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">\u2317</span>\r\n <span class=\"fb-scr__chip-label\">\r\n {{ field.label || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n <small>{{ field.dataType }}</small>\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </aside>\r\n\r\n <!-- ============================================================== centro -->\r\n <!-- Cliccare fuori da un campo riporta a destra le proprieta' della schermata. -->\r\n <section class=\"fb-scr__canvas\" aria-label=\"Anteprima della schermata\" (click)=\"selectScreen()\">\r\n <div class=\"fb-scr__frame\">\r\n @if (showHeader()) {\r\n <header class=\"fb-scr__frame-head\">\r\n {{ $any(node()).label || name() }}\r\n @if (screen().helpText) {\r\n <span class=\"fb-scr__help\" [title]=\"screen().helpText || ''\">?</span>\r\n }\r\n </header>\r\n }\r\n\r\n <div\r\n class=\"fb-scr__body\"\r\n [attr.data-drop]=\"''\"\r\n data-axis=\"column\"\r\n [class.fb-scr__body--empty]=\"isEmpty()\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: screen().fields || [], parent: [], axis: 'column' }\"\r\n />\r\n </div>\r\n\r\n <!--\r\n La barra di inserimento e' un **velo** sopra l'anteprima, non un figlio del contenitore di\r\n rilascio: dentro la griglia occupava una riga intera e spostava in basso proprio la sezione\r\n che si stava puntando, che allora usciva da sotto il puntatore e il bersaglio oscillava.\r\n -->\r\n @if (dropMarker(); as bar) {\r\n <div\r\n class=\"fb-scr__marker\"\r\n [style.left.px]=\"bar.left\"\r\n [style.top.px]=\"bar.top\"\r\n [style.width.px]=\"bar.width\"\r\n [style.height.px]=\"bar.height\"\r\n ></div>\r\n }\r\n\r\n @if (showFooter()) {\r\n <footer class=\"fb-scr__frame-foot\">\r\n @if (allowPause()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().pauseButtonLabel || 'Pausa' }}</span>\r\n }\r\n <span class=\"fb-scr__spacer\"></span>\r\n @if (allowBack()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().backButtonLabel || 'Indietro' }}</span>\r\n }\r\n <span class=\"fb-scr__btn fb-scr__btn--primary\">\r\n {{ screen().nextOrFinishButtonLabel || (allowFinish() ? 'Fine' : 'Avanti') }}\r\n </span>\r\n </footer>\r\n }\r\n </div>\r\n\r\n @if (isEmpty()) {\r\n <p class=\"fb-callout fb-callout--warn fb-scr__canvas-note\">\r\n La schermata non ha campi: non c\u2019e\u2019 niente da mostrare all\u2019utente (SCREEN_WITHOUT_FIELDS).\r\n </p>\r\n }\r\n </section>\r\n\r\n <!-- ============================================================== destra -->\r\n <aside class=\"fb-scr__props\" aria-label=\"Proprieta\u2019\">\r\n @if (!selectedField()) {\r\n <!-- ------------------------------------------------ proprieta' della schermata -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">Schermata</h3>\r\n </header>\r\n <p class=\"fb-scr__side-hint\">Seleziona un campo nell\u2019anteprima per configurarlo.</p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setScreenText('helpText', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta della\r\n schermata.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setScreenFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setScreenFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setScreenFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showHeader()\" (change)=\"setScreenFlag('showHeader', $any($event.target).checked)\" />\r\n Mostra l\u2019intestazione\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showFooter()\" (change)=\"setScreenFlag('showFooter', $any($event.target).checked)\" />\r\n Mostra il piede\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setScreenText('pausedText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questa schermata non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e\r\n l\u2019utente resterebbe bloccato (SCREEN_DEAD_END).\r\n </p>\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pulsanti</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABindietro\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().backButtonLabel || ''\"\r\n (input)=\"setScreenText('backButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABavanti / fine\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().nextOrFinishButtonLabel || ''\"\r\n (input)=\"setScreenText('nextOrFinishButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABpausa\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pauseButtonLabel || ''\"\r\n (input)=\"setScreenText('pauseButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Stage mostrato nell\u2019avanzamento</label>\r\n <fb-name-picker\r\n [value]=\"screen().stageReference\"\r\n [options]=\"stageOptions()\"\r\n label=\"Stage\"\r\n placeholder=\"Scegli uno stage\"\r\n unknownMessage=\"Questo stage non e\u2019 dichiarato dal flow.\"\r\n emptyMessage=\"Il flow non dichiara stage: creali nel pannello delle risorse.\"\r\n (valueChange)=\"setScreenText('stageReference', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <!--\r\n Screen action (\u00A75.2). Due elenchi e non uno perche' il contratto li separa: piu' trigger\r\n possono invocare la stessa action, quindi \u00ABchi la chiama\u00BB non e' una sua proprieta'. Le\r\n action si aprono una alla volta: i cataloghi dipendono da tipo e nome, e tenerne N in volo\r\n sarebbe N volte la corsa fra risposte.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Azioni della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Un campo che l\u2019utente compila puo\u2019 innescare un\u2019action i cui risultati finiscono negli\r\n <strong>altri campi di questa schermata</strong>: e\u2019 il caso \u00ABscrivi il codice fiscale e nome\r\n e cognome compaiono da soli\u00BB. L\u2019alternativa sarebbe spezzare la schermata in due con un\r\n elemento Action in mezzo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of screenActions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [attr.aria-expanded]=\"selectedActionIndex() === $index\"\r\n (click)=\"selectAction(selectedActionIndex() === $index ? null : $index)\"\r\n >\r\n {{ selectedActionIndex() === $index ? '\u25BE' : '\u25B8' }}\r\n {{ action.name || '(senza nome)' }}\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <span class=\"fb-scr__side-hint\">{{ action.actionName || 'action non scelta' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019action\"\r\n (click)=\"removeScreenAction($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (actionLosesResult(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questa action viene invocata ma il suo risultato si perde: aggiungi un parametro di\r\n uscita, oppure accendi l\u2019output automatico (SCREEN_ACTION_WITHOUT_OUTPUTS).\r\n </p>\r\n }\r\n @if (actionHasNoTrigger(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun trigger la invoca: senza, non girera\u2019 mai.\r\n </p>\r\n }\r\n\r\n @if (selectedActionIndex() === $index) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"action.name || ''\"\r\n (change)=\"setActionName($index, $any($event.target).value)\"\r\n />\r\n @if (actionNameError(action); as error) {\r\n <p class=\"fb-field__error\">{{ error }}</p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n E\u2019 il nome con cui la citano i trigger, e vive nello spazio dei nomi del flow. Con\r\n l\u2019output automatico e\u2019 anche la radice del riferimento:\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: e\u2019 ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionTarget($index, $event ?? '')\"\r\n />\r\n @if (!action.actionName) {\r\n <p class=\"fb-field__error\">Obbligatoria: senza, ACTION_NAME_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action.storeOutputAutomatically === true\"\r\n (change)=\"setActionStoreOutput($index, $any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n L\u2019output automatico rende il risultato leggibile come\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>, ma\r\n <strong>non scrive in nessun campo</strong>: e\u2019 la forma da usare quando il risultato\r\n serve a una condizione, non a precompilare.\r\n </p>\r\n\r\n <!--\r\n L'unico posto in cui un campo di schermata e' una destinazione (\u00A75.2):\r\n `POST /flows/references/writable` continua a escluderlo, e un Assignment che ci\r\n scrive resta TARGET_NOT_WRITABLE.\r\n -->\r\n <fb-parameter-editor\r\n [holder]=\"action\"\r\n [catalogParameters]=\"actionParameterCatalog()\"\r\n [showOutputs]=\"action.storeOutputAutomatically !== true\"\r\n outputTitle=\"Campi da riempire\"\r\n [extraTargets]=\"screenFieldTargets()\"\r\n [outputsDisabledReason]=\"\r\n action.storeOutputAutomatically === true\r\n ? 'Con l\u2019output automatico il risultato non viene scritto nei campi.'\r\n : null\r\n \"\r\n (changed)=\"onActionParametersChanged($index, $event)\"\r\n />\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna action: i campi di questa schermata li compila solo l\u2019utente.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenAction()\">Aggiungi action</button>\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trigger</legend>\r\n <p class=\"fb-section__note\">\r\n Quale campo invoca quale action. Senza condizioni l\u2019action gira a ogni cambio del campo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (trigger of screenTriggers(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il trigger\"\r\n (click)=\"removeScreenTrigger($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action da invocare</label>\r\n <fb-name-picker\r\n [value]=\"trigger.screenActionName\"\r\n [options]=\"screenActionOptions()\"\r\n label=\"Action della schermata\"\r\n placeholder=\"Scegli un\u2019action\"\r\n unknownMessage=\"Questa schermata non dichiara un\u2019action con questo nome: e\u2019 SCREEN_ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Aggiungi prima un\u2019action qui sopra.\"\r\n (valueChange)=\"setTriggerProperty($index, 'screenActionName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo che la innesca</label>\r\n <!-- I campi di **questa** schermata, non i riferimenti in generale (\u00A75.2). -->\r\n <fb-name-picker\r\n [value]=\"trigger.triggerFieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo della schermata\"\r\n placeholder=\"Scegli un campo\"\r\n unknownMessage=\"Questo non e\u2019 un campo di questa schermata: e\u2019 SCREEN_TRIGGER_FIELD_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha ancora campi che raccolgono un valore.\"\r\n (valueChange)=\"setTriggerProperty($index, 'triggerFieldName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">All\u2019arrivo sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"trigger.initBehavior || ''\"\r\n (change)=\"setTriggerProperty($index, 'initBehavior', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 non invocare \u2014</option>\r\n <option value=\"runOnLoad\">Invoca al caricamento (runOnLoad)</option>\r\n </select>\r\n @if (triggerInitIsUnknown(trigger)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ trigger.initBehavior }}\u00BB non e\u2019 ammesso: l\u2019unico valore e\u2019 <code>runOnLoad</code>\r\n (SCREEN_TRIGGER_INIT_BEHAVIOR_UNKNOWN).\r\n </p>\r\n }\r\n @if (triggerFiresOnLoadByDefault(trigger)) {\r\n <p class=\"fb-field__hint\">\r\n Non serve: \u00AB{{ trigger.triggerFieldName }}\u00BB ha un valore di default, e un campo\r\n precompilato dal flow innesca l\u2019action gi\u00E0 all\u2019arrivo sulla schermata.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (triggerHasNoCause(trigger)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Senza un campo che la innesca e senza l\u2019invocazione al caricamento, questo trigger non\r\n scattera\u2019 mai (SCREEN_TRIGGER_WITHOUT_CAUSE).\r\n </p>\r\n }\r\n\r\n <fb-condition-editor\r\n [holder]=\"triggerConditions(trigger)\"\r\n title=\"Invoca l\u2019action quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'triggers[' + $index + ']'\"\r\n (changed)=\"onTriggerConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun trigger: le action dichiarate non verranno invocate.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenTrigger()\">Aggiungi trigger</button>\r\n </fieldset>\r\n } @else {\r\n <!-- ---------------------------------------------------- proprieta' del campo -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">{{ captionOf(selectedField()!) }}</h3>\r\n <div class=\"fb-scr__props-actions\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta su\" (click)=\"moveSelected(-1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta giu\u2019\" (click)=\"moveSelected(1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--sm\"\r\n title=\"Porta fuori dal contenitore\"\r\n [disabled]=\"!canOutdent()\"\r\n (click)=\"outdentSelected()\"\r\n >\r\n \u21E4\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Duplica\" (click)=\"duplicateSelected()\">\u29C9</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--sm\" title=\"Elimina\" (click)=\"removeSelected()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.fieldType || ''\"\r\n (change)=\"setFieldType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.fieldType) {\r\n <p class=\"fb-field__error\">Il tipo e\u2019 obbligatorio: senza, SCREEN_FIELD_TYPE_MISSING.</p>\r\n } @else if (isUnknownType(selectedField())) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo tipo non e\u2019 nel dizionario: l\u2019editor non sa quali campi ammetta e mostra solo i comuni.\r\n </p>\r\n } @else if (selectedType()?.description) {\r\n <p class=\"fb-field__hint\">{{ selectedType()?.description }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome</label>\r\n <!-- Sul `change` e non sull\u2019`input`: la rinomina riscrive i riferimenti nel documento,\r\n e farlo a ogni tasto significherebbe riscriverlo per ogni lettera. -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.name || ''\"\r\n (change)=\"setFieldName($any($event.target).value)\"\r\n />\r\n @if (selectedIsResource()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo e\u2019 una <strong>risorsa</strong>: lo referenzi come\r\n <code>{{ selectedField()!.name || 'Nome' }}</code> in condizioni, formule e parametri. \u00C8 di\r\n sola lettura per il flow \u2014 un Assignment che ci scrive e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (!selectedType()?.isContainer) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ selectedField()!.fieldType === 'DisplayText' ? 'Testo' : 'Etichetta' }}\r\n </label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n ></textarea>\r\n <p class=\"fb-field__hint\">\r\n Supporta i merge field <code>{!Riferimento}</code>.\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 su un componente l\u2019etichetta e\u2019 **ammessa** e viene consegnata al frontend\r\n in `label`, ma la sua assenza non e\u2019 un rilievo: un componente di solito disegna\r\n la propria intestazione. Nasconderla, come faceva prima l\u2019editor, toglieva un\r\n campo che il contratto prevede.\r\n -->\r\n Facoltativa: un componente di solito disegna la propria intestazione.\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.helpText || ''\"\r\n (input)=\"setFieldProperty('helpText', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Larghezza</label>\r\n <div class=\"fb-scr__width\">\r\n <input\r\n type=\"range\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n [value]=\"widthOf(selectedField()!)\"\r\n (input)=\"setNumberProperty('width', $any($event.target).value)\"\r\n />\r\n <span class=\"fb-scr__width-value\">{{ widthOf(selectedField()!) }}/12</span>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Colonne della griglia della schermata. \u00C8 un\u2019indicazione: il frontend puo\u2019 ignorarla.\r\n </p>\r\n </div>\r\n\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 perche\u2019 qui non c\u2019e\u2019 \u00ABobbligatorio\u00BB. La domanda arriva sempre, e la risposta non\r\n si indovina guardando l\u2019interfaccia: un componente non ha un valore proprio, ce l\u2019hanno\r\n i suoi output, che vanno in **variabili** scelte dall\u2019autore. \u00ABObbligatorio\u00BB dovrebbe\r\n dire *quale output* deve essere valorizzato, e il metadata non ha modo di dirlo \u2014\r\n dichiararlo qui sarebbe `SCREEN_FIELD_CONFIGURATION_INVALID` e il runtime lo\r\n ignorerebbe. Le due strade vere stanno nel contratto e sono queste.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Un componente non raccoglie un valore \u2014 lo fanno i suoi output \u2014 quindi obbligatorieta\u2019, valore\r\n predefinito e regola di validazione non si applicano: dichiararli e\u2019\r\n <code>SCREEN_FIELD_CONFIGURATION_INVALID</code> e il runtime li ignora. Per pretendere una\r\n compilazione: la validazione la fa il componente stesso, oppure si controlla la variabile di\r\n destinazione con una Decision dopo la schermata.\r\n </p>\r\n }\r\n\r\n <!-- ------------------------------------------------ campi che raccolgono un valore -->\r\n @if (selectedType()?.storesValue) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valore</legend>\r\n\r\n @if (selectedField()!.fieldType !== 'ObjectProvided') {\r\n <div class=\"fb-field\">\r\n <label\r\n class=\"fb-field__label\"\r\n [class.fb-field__label--required]=\"!!selectedType()?.requiresDataType\"\r\n >\r\n Tipo di dato\r\n </label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.dataType || ''\"\r\n (change)=\"setFieldProperty('dataType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.dataTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (selectedType()?.requiresDataType && !selectedField()!.dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio per questo tipo di campo (DATA_TYPE_MISSING).</p>\r\n }\r\n @if (selectedType()?.isCollection) {\r\n <p class=\"fb-field__hint\">\r\n Il valore raccolto e\u2019 una <strong>collection</strong>, non una stringa con i valori\r\n separati: nelle condizioni si usano gli operatori di collection.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\" [class.fb-field__label--required]=\"objectTypeIsStructure()\">\r\n {{\r\n objectTypeIsStructure()\r\n ? 'Classe'\r\n : selectedField()!.dataType === 'Enum'\r\n ? 'Tipo di enumerazione'\r\n : 'Oggetto'\r\n }}\r\n </label>\r\n @if (selectedField()!.dataType === 'Enum') {\r\n <!-- Dizionario chiuso: si sceglie, non si scrive. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.objectType || ''\"\r\n (change)=\"setFieldProperty('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza il tipo concreto l\u2019editor non puo\u2019 proporre i valori dell\u2019enumerazione.\r\n </p>\r\n }\r\n } @else if (objectTypeIsStructure()) {\r\n <fb-structure-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Classe\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, il runtime non ha nulla da istanziare\r\n (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo dell\u2019entita\u2019</label>\r\n <p class=\"fb-field__hint\">\r\n Tipo, label, obbligatorieta\u2019, scala e \u2014 se il campo e\u2019 una picklist \u2014 le opzioni arrivano\r\n dallo schema dati: qui non si ridichiarano.\r\n </p>\r\n <fb-object-picker\r\n [value]=\"providedObject() || undefined\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setProvidedObject($event)\"\r\n />\r\n <fb-field-picker\r\n [value]=\"providedField() || undefined\"\r\n [object]=\"providedObject() || undefined\"\r\n label=\"Campo\"\r\n (valueChange)=\"setProvidedField($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsRequired()\"\r\n (change)=\"setRequired($any($event.target).checked)\"\r\n />\r\n Obbligatorio\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsEditable()\"\r\n (change)=\"setEditable($any($event.target).checked)\"\r\n />\r\n Modificabile\r\n </label>\r\n @if (!selectedIsEditable()) {\r\n <p class=\"fb-field__hint\">\r\n A <code>false</code> il runtime <strong>ignora</strong> cio\u2019 che il client rimanda indietro:\r\n e\u2019 l\u2019unico modo di rendere un campo davvero di sola lettura.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore di default</label>\r\n <fb-value-editor\r\n [value]=\"selectedField()!.defaultValue\"\r\n [dataType]=\"selectedField()!.dataType\"\r\n [objectType]=\"selectedField()!.objectType\"\r\n [isCollection]=\"selectedType()?.isCollection\"\r\n label=\"Valore di default\"\r\n (valueChange)=\"setDefaultValue($event)\"\r\n />\r\n </div>\r\n\r\n @if (scaleApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.scale ?? ''\"\r\n (change)=\"setNumberProperty('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n } @else if (selectedField()!.scale !== undefined) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>scale</code> su un campo non numerico e\u2019 SCALE_NOT_APPLICABLE.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Lunghezza massima</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxLength ?? ''\"\r\n (change)=\"setNumberProperty('maxLength', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Verificata anche dal runtime (TOO_LONG).</p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tornando sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.inputsOnNextNavToAssocScrn || ''\"\r\n (change)=\"setFieldProperty('inputsOnNextNavToAssocScrn', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito (mantieni i valori)</option>\r\n @for (entry of dictionary.screenFieldInputsRevisited(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Regola di validazione</label>\r\n <!--\r\n `commitOn=\"change\"`: qui mutare il documento ridisegna l'anteprima, e riscriverla a\r\n ogni tasto costa. La verifica parte lo stesso mentre si digita (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"selectedField()!.validationRule?.formulaExpression || ''\"\r\n usage=\"ValidationRule\"\r\n expectedDataType=\"Boolean\"\r\n commitOn=\"change\"\r\n [rows]=\"2\"\r\n placeholder=\"Espressione, es. LEN(Nome) > 3\"\r\n ariaLabel=\"Regola di validazione\"\r\n (expressionChange)=\"setValidationRule('formulaExpression', $event)\"\r\n />\r\n <input\r\n class=\"fb-input\"\r\n placeholder=\"Messaggio mostrato quando l\u2019espressione e\u2019 falsa\"\r\n [value]=\"selectedField()!.validationRule?.errorMessage || ''\"\r\n (change)=\"setValidationRule('errorMessage', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione la valuta il motore di regole: il backend non la interpreta, la fa\r\n verificare al motore.\r\n </p>\r\n </div>\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------------------- choice -->\r\n @if (selectedType()?.acceptsChoices) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Opzioni</legend>\r\n <p class=\"fb-section__note\">\r\n Accetta <strong>Choice</strong> e <strong>Dynamic choice set</strong>, nell\u2019ordine in cui le\r\n opzioni compaiono. Puntare a una variabile e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\r\n </p>\r\n\r\n @if (!selectedField()!.choiceReferences?.length) {\r\n @if (selectedField()!.fieldType === 'ObjectProvided') {\r\n <!-- Le opzioni di una picklist arrivano dallo schema: qui si aggiungono solo se\r\n si vuole sostituirle, e non dichiararne nessuna e' il caso normale. -->\r\n <p class=\"fb-field__hint\">\r\n Se il campo dello schema e\u2019 una picklist, le opzioni arrivano da l\u00EC: dichiararle qui\r\n serve solo a sostituirle.\r\n </p>\r\n } @else {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessuna opzione: il campo non ha niente da mostrare (SCREEN_FIELD_CHOICES_MISSING).\r\n </p>\r\n }\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__title\">{{ choice }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, -1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, 1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--sm\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeChoice($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggiungi un\u2019opzione</label>\r\n <!--\r\n `commitOn=\"enter\"`: qui ogni emissione e\u2019 una riga in piu\u2019, non un campo che cambia\r\n valore \u2014 a tasto, scrivere \u00ABAlta\u00BB aggiungeva \u00ABA\u00BB, \u00ABAl\u00BB, \u00ABAlt\u00BB e \u00ABAlta\u00BB.\r\n -->\r\n <fb-name-picker\r\n [options]=\"choiceOptions()\"\r\n label=\"Choice\"\r\n commitOn=\"enter\"\r\n placeholder=\"Scegli una choice o un choice set\"\r\n unknownMessage=\"Questo nome non e\u2019 una choice ne\u2019 un choice set: e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Il flow non dichiara nessuna choice: creale nel pannello delle risorse.\"\r\n (valueChange)=\"addChoice($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Scegli dall\u2019elenco, oppure scrivi il nome e premi Invio.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Opzione selezionata all\u2019apertura</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.defaultSelectedChoiceReference || ''\"\r\n (change)=\"setFieldProperty('defaultSelectedChoiceReference', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 nessuna \u2014</option>\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <option [value]=\"choice\">{{ choice }}</option>\r\n }\r\n </select>\r\n @if (defaultChoiceIsForeign()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n \u00AB{{ selectedField()!.defaultSelectedChoiceReference }}\u00BB non e\u2019 fra le opzioni elencate qui\r\n sopra.\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedAutoSelectsSingleChoice()\"\r\n (change)=\"setAutoSelectSingleChoice($any($event.target).checked)\"\r\n />\r\n Scegli da sola l\u2019unica opzione\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Quando le opzioni risolte sono <strong>esattamente una</strong>, il campo la prende da se\u2019.\r\n Serve alle tendine che dipendono da un\u2019altra tendina \u2014 i CAP di un comune \u2014 dove chiedere\r\n di scegliere l\u2019unica voce non decide niente.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- contenitori -->\r\n @if (selectedType()?.isContainer) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Contenitore</legend>\r\n @if (selectedField()!.fieldType === 'RegionContainer') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di sezione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.regionContainerType || ''\"\r\n (change)=\"setFieldProperty('regionContainerType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (entry of dictionary.regionContainerTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il contenitore e\u2019 vuoto: non produce niente sulla schermata (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------- ComponentInstance -->\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Componente</legend>\r\n <p class=\"fb-section__note\">\r\n Componenti e form sono la stessa domanda al frontend \u2014 cosa sa rendere, e con quali parametri\r\n \u2014 e passano dallo stesso catalogo. Un <code>ComponentInstance</code> non ha un valore proprio:\r\n lo hanno i suoi output.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Componente</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.extensionName\"\r\n [options]=\"componentOptions()\"\r\n [unusableOptions]=\"unusableComponents()\"\r\n label=\"Componente\"\r\n placeholder=\"Scrivi o scegli un componente\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo componente non esiste nel catalogo: e\u2019 SCREEN_COMPONENT_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 una schermata intera, non un componente montabile qui (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei componenti non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFieldProperty('extensionName', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.extensionName) {\r\n <p class=\"fb-field__error\">Obbligatorio: senza, SCREEN_COMPONENT_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Rendi gli output referenziabili automaticamente\r\n </label>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n Gli output si referenziano come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, senza dichiarare variabili.\r\n </p>\r\n }\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Output automatici <strong>e</strong> parametri di uscita insieme:\r\n OUTPUT_CONFIGURATION_CONFLICT.\r\n </p>\r\n }\r\n\r\n <fb-parameter-editor\r\n [holder]=\"$any(selectedField())\"\r\n [catalogParameters]=\"componentParameterList()\"\r\n inputTitle=\"Valori passati al componente\"\r\n outputTitle=\"Valori raccolti dal componente\"\r\n [showOutputs]=\"!selectedField()!.storeOutputAutomatically\"\r\n outputsDisabledReason=\"Gli output sono automatici: disattivalo per assegnarli a variabili.\"\r\n (changed)=\"onComponentParametersChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- visibilita' -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Visibilita\u2019</legend>\r\n <p class=\"fb-section__note\">\r\n Le regole si rivalutano <strong>sui valori appena inviati</strong>: e\u2019 cos\u00EC che un campo compare\r\n in funzione di un altro campo della stessa schermata. Un campo risultato nascosto viene\r\n <strong>azzerato</strong> e non viene validato.\r\n </p>\r\n <fb-condition-editor\r\n [holder]=\"visibilityRule()\"\r\n title=\"Mostra il campo quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'fields[' + (selectedField()!.name || '') + '].visibilityRule'\"\r\n (changed)=\"onVisibilityChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<!--\r\n ============================================================== l'anteprima\r\n Due template ricorsivi. `listTpl` disegna i figli di un contenitore, e nient'altro: `data-item`\r\n marca cio' che conta come fratello nel calcolo del punto di rilascio, e il segnaposto del\r\n contenitore vuoto ne resta fuori. La barra di inserimento non e' qui \u2014 sta sopra l'anteprima,\r\n vedi il velo nel frame.\r\n-->\r\n<ng-template #listTpl let-fields let-parent=\"parent\" let-axis=\"axis\">\r\n @for (child of fields; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"fieldTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: child, path: childPath(parent, $index), axis: axis }\"\r\n />\r\n }\r\n @if (!fields.length) {\r\n <p class=\"fb-scr__drop-hint\">Trascina qui un componente</p>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #fieldTpl let-field let-path=\"path\" let-axis=\"axis\">\r\n <div\r\n class=\"fb-scr__item\"\r\n data-item=\"\"\r\n cdkDrag\r\n [style.grid-column]=\"'span ' + widthOf(field)\"\r\n [class.fb-scr__item--selected]=\"isSelected(path)\"\r\n [class.fb-scr__item--dragging]=\"isDragging(path)\"\r\n [class.fb-scr__item--container]=\"isContainer(field)\"\r\n (cdkDragStarted)=\"onFieldDragStarted(path)\"\r\n (cdkDragMoved)=\"onFieldDragMoved($event)\"\r\n (cdkDragEnded)=\"onFieldDragEnded($event, path)\"\r\n (click)=\"select(path); $event.stopPropagation()\"\r\n >\r\n <span class=\"fb-scr__grip\" cdkDragHandle title=\"Trascina per spostare\" aria-hidden=\"true\">\u283F</span>\r\n @if (field.visibilityRule?.conditions?.length) {\r\n <span class=\"fb-scr__flag\" title=\"Ha una regola di visibilita\u2019\">\u25D0</span>\r\n }\r\n\r\n @switch (field.fieldType) {\r\n @case ('RegionContainer') {\r\n <div class=\"fb-scr__section\">\r\n @if (field.regionContainerType !== 'SectionWithoutHeader') {\r\n <header class=\"fb-scr__section-head\">{{ field.fieldText || field.name }}</header>\r\n }\r\n <div class=\"fb-scr__cols\" [attr.data-drop]=\"dropId(path)\" data-axis=\"row\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'row' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('Region') {\r\n <div class=\"fb-scr__region\">\r\n <span class=\"fb-scr__region-tag\">{{ field.name }} \u00B7 {{ widthOf(field) }}/12</span>\r\n <div class=\"fb-scr__region-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('DisplayText') {\r\n <p class=\"fb-scr__display\">{{ field.fieldText || '(testo vuoto)' }}</p>\r\n }\r\n @case ('LargeTextArea') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--area\">{{ placeholderOf(field) }}</div>\r\n }\r\n @case ('PasswordField') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022</div>\r\n }\r\n @case ('DropdownBox') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ field.defaultSelectedChoiceReference || choiceLabelsOf(field)[0] || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u25BE</span>\r\n </div>\r\n }\r\n @case ('MultiSelectPicklist') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ choiceLabelsOf(field).join(', ') || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u2261</span>\r\n </div>\r\n }\r\n @case ('RadioButtons') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u25EF {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('MultiSelectCheckboxes') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u2610 {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('ComponentInstance') {\r\n <!--\r\n L\u2019etichetta si disegna se c\u2019e\u2019: il runtime la consegna in `label` come per gli altri\r\n campi (\u00A75.2). Nessun asterisco: un componente non raccoglie un valore, quindi\r\n \u00ABobbligatorio\u00BB non ha niente su cui applicarsi.\r\n -->\r\n @if (field.fieldText) {\r\n <label class=\"fb-scr__label\">{{ field.fieldText }}</label>\r\n }\r\n <div class=\"fb-scr__component\">\r\n <span aria-hidden=\"true\">\u2B21</span>\r\n {{ field.extensionName || 'Componente non indicato' }}\r\n </div>\r\n }\r\n @case ('ObjectProvided') {\r\n <label class=\"fb-scr__label\">\r\n {{ field.fieldText || field.objectFieldReference || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n <span class=\"fb-scr__tag\">{{ field.objectFieldReference || 'campo non indicato' }}</span>\r\n }\r\n @default {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n @if (isUnknownType(field)) {\r\n <span class=\"fb-scr__tag fb-scr__tag--warn\">{{ field.fieldType }}: tipo non nel dizionario</span>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".fb-scr{display:grid;grid-template-columns:220px minmax(0,1fr) 340px;gap:12px;align-items:start;margin-bottom:14px}@media(max-width:1100px){.fb-scr{grid-template-columns:minmax(0,1fr)}}.fb-scr__side,.fb-scr__props{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__tabs{display:flex;gap:4px;margin-bottom:8px}.fb-scr__tab{flex:1;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;font-weight:600;cursor:pointer}.fb-scr__tab--active{border-color:var(--fb-accent, #4f6ef7);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent);color:var(--fb-accent-strong, #3d59e0)}.fb-scr__side-hint,.fb-scr__side-empty{margin:6px 0;font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__palette{display:flex;flex-direction:column;gap:4px;margin:8px 0 0;padding:0;max-height:320px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-scr__chip{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-scr__chip:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-scr__chip-icon{flex:none;width:20px;text-align:center;color:var(--fb-text-muted, #6b7086)}.fb-scr__chip-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-scr__chip-label small{margin-left:4px;color:var(--fb-text-subtle, #98a2b3);font-size:10px}.fb-scr__canvas{min-width:0;padding:14px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-canvas-bg, #f4f5f7);max-height:60vh;overflow-y:auto}.fb-scr__frame{position:relative;display:flex;flex-direction:column;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));overflow:hidden}.fb-scr__frame-head{display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:13px;font-weight:600}.fb-scr__help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px}.fb-scr__body,.fb-scr__region-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:12px;min-height:90px}.fb-scr__region-body{padding:8px;min-height:60px}.fb-scr__cols{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-items:stretch;gap:0;padding:8px;min-height:60px}.fb-scr__cols>.fb-scr__item{display:flex;align-self:stretch}.fb-scr__cols>.fb-scr__item>.fb-scr__region{flex:1;min-width:0}.fb-scr__drop-hint{grid-column:1 / -1;margin:0;padding:10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);color:var(--fb-text-subtle, #98a2b3);font-size:11px;text-align:center}.fb-scr__marker{position:absolute;z-index:2;border-radius:2px;background:var(--fb-accent, #4f6ef7);pointer-events:none}.fb-scr__item{position:relative;box-sizing:border-box;padding:6px 8px;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);cursor:pointer}.fb-scr__item:hover{border-color:var(--fb-border-strong, #cfd4de);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 4%,transparent)}.fb-scr__item--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:inset 0 0 0 1px var(--fb-accent, #4f6ef7)}.fb-scr__item--dragging{opacity:.45;pointer-events:none}.fb-scr__grip{position:absolute;top:2px;left:-2px;padding:0 3px;color:var(--fb-text-subtle, #98a2b3);font-size:11px;line-height:1;opacity:0;cursor:grab}.fb-scr__item:hover>.fb-scr__grip,.fb-scr__item--selected>.fb-scr__grip{opacity:1}.fb-scr__flag{position:absolute;top:2px;right:4px;color:var(--fb-text-muted, #6b7086);font-size:11px}.fb-scr__label{display:block;margin-bottom:3px;font-size:11px;font-weight:600;color:var(--fb-text, #1a1c23)}.fb-scr__req{color:var(--fb-error, #dc2626)}.fb-scr__control{display:flex;align-items:center;justify-content:space-between;gap:6px;min-height:26px;padding:4px 8px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-subtle, #98a2b3);font-size:12px;pointer-events:none}.fb-scr__control--area{min-height:54px;align-items:flex-start}.fb-scr__display{margin:0;font-size:12px;color:var(--fb-text, #1a1c23)}.fb-scr__options{display:flex;flex-direction:column;gap:2px;pointer-events:none}.fb-scr__option{font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__option--missing{color:var(--fb-warning, #b7791f)}.fb-scr__component{display:flex;align-items:center;gap:6px;padding:10px;border:1px dashed var(--fb-accent, #4f6ef7);border-radius:var(--fb-radius-xs, 6px);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 5%,transparent);font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__tag{display:inline-block;margin-top:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__tag--warn{color:var(--fb-warning, #b7791f)}.fb-scr__section{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__section-head{padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__region{border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__region-tag{display:block;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__frame-foot{display:flex;align-items:center;gap:6px;padding:8px 12px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__spacer{flex:1}.fb-scr__btn{padding:3px 10px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__btn--primary{border-color:var(--fb-accent, #4f6ef7);background:var(--fb-accent, #4f6ef7);color:var(--fb-accent-contrast, #fff)}.fb-scr__canvas-note{margin-top:10px}.fb-scr__props{background:var(--fb-surface, #fff)}.fb-scr__props-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.fb-scr__props-title{flex:1;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__props-actions{display:flex;flex-wrap:wrap;gap:3px}.fb-btn--sm{padding:3px 7px;font-size:11px}.fb-scr__width{display:flex;align-items:center;gap:8px}.fb-scr__width input[type=range]{flex:1;min-width:0}.fb-scr__width-value{font-size:11px;color:var(--fb-text-muted, #6b7086)}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12891
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: DynamicScreenInspectorComponent, isStandalone: true, selector: "fb-dynamic-screen-inspector", inputs: { initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "composer", first: true, predicate: ["composer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"fb-scr\" #composer>\r\n <!-- ============================================================ sinistra -->\r\n <aside class=\"fb-scr__side\" aria-label=\"Componenti e campi\">\r\n <div class=\"fb-scr__tabs\" role=\"tablist\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'components'\"\r\n [attr.aria-selected]=\"paletteTab() === 'components'\"\r\n (click)=\"setPaletteTab('components')\"\r\n >\r\n Componenti\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'fields'\"\r\n [attr.aria-selected]=\"paletteTab() === 'fields'\"\r\n (click)=\"setPaletteTab('fields')\"\r\n >\r\n Campi\r\n </button>\r\n </div>\r\n\r\n @if (paletteTab() === 'components') {\r\n @if (!dictionary.screenFieldTypes().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il dizionario <code>screenFieldTypes</code> non e\u2019 disponibile: senza i suoi flag l\u2019editor non\r\n sa quali campi ammette ciascun tipo, e non li inventa.\r\n </p>\r\n }\r\n <p class=\"fb-scr__side-hint\">Trascina sulla schermata, oppure clicca per aggiungere.</p>\r\n <ul class=\"fb-scr__palette\">\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <li>\r\n <!--\r\n \u00A75.2 \u2014 l'unico chip che si spegne: una sezione ripetibile dentro un'altra. Non si\r\n annidano, e il cursore e' una variabile sola per sezione.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [disabled]=\"!canAddAtSelection(entry.value)\"\r\n [title]=\"\r\n canAddAtSelection(entry.value)\r\n ? entry.description || entry.label\r\n : 'Le sezioni ripetibili non si annidano: seleziona un punto fuori dalla riga.'\r\n \"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onComponentDragEnded($event, entry.value)\"\r\n (click)=\"addComponentAtSelection(entry.value)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">{{ iconOf(entry.value) }}</span>\r\n <span class=\"fb-scr__chip-label\">{{ entry.label }}</span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p class=\"fb-scr__side-hint\">\r\n Un campo dell\u2019entita\u2019 porta con se\u2019 tipo, etichetta, obbligatorieta\u2019 e opzioni: arrivano dallo\r\n schema dati, qui non si ridichiarano.\r\n </p>\r\n <!-- Il catalogo non e' un dizionario chiuso: si sceglie o si scrive, come ovunque si\r\n indichi un'entita' (vedi `object-picker`). -->\r\n <fb-object-picker\r\n [value]=\"fieldsObject()\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Scrivi o scegli un\u2019entita\u2019\"\r\n (valueChange)=\"setFieldsObject($event)\"\r\n />\r\n @if (fieldsObject() && !fieldsOfObject().length) {\r\n <p class=\"fb-scr__side-empty\">\r\n Nessun campo dichiarato per questa entita\u2019: il catalogo non li espone, non significa che non\r\n ci siano.\r\n </p>\r\n }\r\n <ul class=\"fb-scr__palette\">\r\n @for (field of fieldsOfObject(); track $index) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"field.name + (field.dataType ? ' \u00B7 ' + field.dataType : '')\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onObjectFieldDragEnded($event, field)\"\r\n (click)=\"addObjectFieldAtSelection(field)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">\u2317</span>\r\n <span class=\"fb-scr__chip-label\">\r\n {{ field.label || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n <small>{{ field.dataType }}</small>\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </aside>\r\n\r\n <!-- ============================================================== centro -->\r\n <!-- Cliccare fuori da un campo riporta a destra le proprieta' della schermata. -->\r\n <section class=\"fb-scr__canvas\" aria-label=\"Anteprima della schermata\" (click)=\"selectScreen()\">\r\n <div class=\"fb-scr__frame\">\r\n @if (showHeader()) {\r\n <header class=\"fb-scr__frame-head\">\r\n {{ $any(node()).label || name() }}\r\n @if (screen().helpText) {\r\n <span class=\"fb-scr__help\" [title]=\"screen().helpText || ''\">?</span>\r\n }\r\n </header>\r\n }\r\n\r\n <div\r\n class=\"fb-scr__body\"\r\n [attr.data-drop]=\"''\"\r\n data-axis=\"column\"\r\n [class.fb-scr__body--empty]=\"isEmpty()\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: screen().fields || [], parent: [], axis: 'column' }\"\r\n />\r\n </div>\r\n\r\n <!--\r\n La barra di inserimento e' un **velo** sopra l'anteprima, non un figlio del contenitore di\r\n rilascio: dentro la griglia occupava una riga intera e spostava in basso proprio la sezione\r\n che si stava puntando, che allora usciva da sotto il puntatore e il bersaglio oscillava.\r\n -->\r\n @if (dropMarker(); as bar) {\r\n <div\r\n class=\"fb-scr__marker\"\r\n [style.left.px]=\"bar.left\"\r\n [style.top.px]=\"bar.top\"\r\n [style.width.px]=\"bar.width\"\r\n [style.height.px]=\"bar.height\"\r\n ></div>\r\n }\r\n\r\n @if (showFooter()) {\r\n <footer class=\"fb-scr__frame-foot\">\r\n @if (allowPause()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().pauseButtonLabel || 'Pausa' }}</span>\r\n }\r\n <span class=\"fb-scr__spacer\"></span>\r\n @if (allowBack()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().backButtonLabel || 'Indietro' }}</span>\r\n }\r\n <span class=\"fb-scr__btn fb-scr__btn--primary\">\r\n {{ screen().nextOrFinishButtonLabel || (allowFinish() ? 'Fine' : 'Avanti') }}\r\n </span>\r\n </footer>\r\n }\r\n </div>\r\n\r\n @if (isEmpty()) {\r\n <p class=\"fb-callout fb-callout--warn fb-scr__canvas-note\">\r\n La schermata non ha campi: non c\u2019e\u2019 niente da mostrare all\u2019utente (SCREEN_WITHOUT_FIELDS).\r\n </p>\r\n }\r\n </section>\r\n\r\n <!-- ============================================================== destra -->\r\n <aside class=\"fb-scr__props\" aria-label=\"Proprieta\u2019\">\r\n @if (!selectedField()) {\r\n <!-- ------------------------------------------------ proprieta' della schermata -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">Schermata</h3>\r\n </header>\r\n <p class=\"fb-scr__side-hint\">Seleziona un campo nell\u2019anteprima per configurarlo.</p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setScreenText('helpText', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta della\r\n schermata.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setScreenFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setScreenFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setScreenFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showHeader()\" (change)=\"setScreenFlag('showHeader', $any($event.target).checked)\" />\r\n Mostra l\u2019intestazione\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showFooter()\" (change)=\"setScreenFlag('showFooter', $any($event.target).checked)\" />\r\n Mostra il piede\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setScreenText('pausedText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questa schermata non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e\r\n l\u2019utente resterebbe bloccato (SCREEN_DEAD_END).\r\n </p>\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pulsanti</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABindietro\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().backButtonLabel || ''\"\r\n (input)=\"setScreenText('backButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABavanti / fine\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().nextOrFinishButtonLabel || ''\"\r\n (input)=\"setScreenText('nextOrFinishButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABpausa\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pauseButtonLabel || ''\"\r\n (input)=\"setScreenText('pauseButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Stage mostrato nell\u2019avanzamento</label>\r\n <fb-name-picker\r\n [value]=\"screen().stageReference\"\r\n [options]=\"stageOptions()\"\r\n label=\"Stage\"\r\n placeholder=\"Scegli uno stage\"\r\n unknownMessage=\"Questo stage non e\u2019 dichiarato dal flow.\"\r\n emptyMessage=\"Il flow non dichiara stage: creali nel pannello delle risorse.\"\r\n (valueChange)=\"setScreenText('stageReference', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <!--\r\n Screen action (\u00A75.2). Due elenchi e non uno perche' il contratto li separa: piu' trigger\r\n possono invocare la stessa action, quindi \u00ABchi la chiama\u00BB non e' una sua proprieta'. Le\r\n action si aprono una alla volta: i cataloghi dipendono da tipo e nome, e tenerne N in volo\r\n sarebbe N volte la corsa fra risposte.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Azioni della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Un campo che l\u2019utente compila puo\u2019 innescare un\u2019action i cui risultati finiscono negli\r\n <strong>altri campi di questa schermata</strong>: e\u2019 il caso \u00ABscrivi il codice fiscale e nome\r\n e cognome compaiono da soli\u00BB. L\u2019alternativa sarebbe spezzare la schermata in due con un\r\n elemento Action in mezzo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of screenActions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [attr.aria-expanded]=\"selectedActionIndex() === $index\"\r\n (click)=\"selectAction(selectedActionIndex() === $index ? null : $index)\"\r\n >\r\n {{ selectedActionIndex() === $index ? '\u25BE' : '\u25B8' }}\r\n {{ action.name || '(senza nome)' }}\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <span class=\"fb-scr__side-hint\">{{ action.actionName || 'action non scelta' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019action\"\r\n (click)=\"removeScreenAction($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (actionLosesResult(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questa action viene invocata ma il suo risultato si perde: aggiungi un parametro di\r\n uscita, oppure accendi l\u2019output automatico (SCREEN_ACTION_WITHOUT_OUTPUTS).\r\n </p>\r\n }\r\n @if (actionHasNoTrigger(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun trigger la invoca: senza, non girera\u2019 mai.\r\n </p>\r\n }\r\n\r\n @if (selectedActionIndex() === $index) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"action.name || ''\"\r\n (change)=\"setActionName($index, $any($event.target).value)\"\r\n />\r\n @if (actionNameError(action); as error) {\r\n <p class=\"fb-field__error\">{{ error }}</p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n E\u2019 il nome con cui la citano i trigger, e vive nello spazio dei nomi del flow. Con\r\n l\u2019output automatico e\u2019 anche la radice del riferimento:\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: e\u2019 ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionTarget($index, $event ?? '')\"\r\n />\r\n @if (!action.actionName) {\r\n <p class=\"fb-field__error\">Obbligatoria: senza, ACTION_NAME_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action.storeOutputAutomatically === true\"\r\n (change)=\"setActionStoreOutput($index, $any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n L\u2019output automatico rende il risultato leggibile come\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>, ma\r\n <strong>non scrive in nessun campo</strong>: e\u2019 la forma da usare quando il risultato\r\n serve a una condizione, non a precompilare.\r\n </p>\r\n\r\n <!--\r\n L'unico posto in cui un campo di schermata e' una destinazione (\u00A75.2):\r\n `POST /flows/references/writable` continua a escluderlo, e un Assignment che ci\r\n scrive resta TARGET_NOT_WRITABLE.\r\n -->\r\n <fb-parameter-editor\r\n [holder]=\"action\"\r\n [catalogParameters]=\"actionParameterCatalog()\"\r\n [showOutputs]=\"action.storeOutputAutomatically !== true\"\r\n outputTitle=\"Campi da riempire\"\r\n [extraTargets]=\"screenFieldTargets()\"\r\n [outputsDisabledReason]=\"\r\n action.storeOutputAutomatically === true\r\n ? 'Con l\u2019output automatico il risultato non viene scritto nei campi.'\r\n : null\r\n \"\r\n (changed)=\"onActionParametersChanged($index, $event)\"\r\n />\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna action: i campi di questa schermata li compila solo l\u2019utente.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenAction()\">Aggiungi action</button>\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trigger</legend>\r\n <p class=\"fb-section__note\">\r\n Quale campo invoca quale action. Senza condizioni l\u2019action gira a ogni cambio del campo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (trigger of screenTriggers(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il trigger\"\r\n (click)=\"removeScreenTrigger($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action da invocare</label>\r\n <fb-name-picker\r\n [value]=\"trigger.screenActionName\"\r\n [options]=\"screenActionOptions()\"\r\n label=\"Action della schermata\"\r\n placeholder=\"Scegli un\u2019action\"\r\n unknownMessage=\"Questa schermata non dichiara un\u2019action con questo nome: e\u2019 SCREEN_ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Aggiungi prima un\u2019action qui sopra.\"\r\n (valueChange)=\"setTriggerProperty($index, 'screenActionName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo che la innesca</label>\r\n <!-- I campi di **questa** schermata, non i riferimenti in generale (\u00A75.2). -->\r\n <fb-name-picker\r\n [value]=\"trigger.triggerFieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo della schermata\"\r\n placeholder=\"Scegli un campo\"\r\n unknownMessage=\"Questo non e\u2019 un campo di questa schermata: e\u2019 SCREEN_TRIGGER_FIELD_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha ancora campi che raccolgono un valore.\"\r\n (valueChange)=\"setTriggerProperty($index, 'triggerFieldName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">All\u2019arrivo sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"trigger.initBehavior || ''\"\r\n (change)=\"setTriggerProperty($index, 'initBehavior', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 non invocare \u2014</option>\r\n <option value=\"runOnLoad\">Invoca al caricamento (runOnLoad)</option>\r\n </select>\r\n @if (triggerInitIsUnknown(trigger)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ trigger.initBehavior }}\u00BB non e\u2019 ammesso: l\u2019unico valore e\u2019 <code>runOnLoad</code>\r\n (SCREEN_TRIGGER_INIT_BEHAVIOR_UNKNOWN).\r\n </p>\r\n }\r\n @if (triggerFiresOnLoadByDefault(trigger)) {\r\n <p class=\"fb-field__hint\">\r\n Non serve: \u00AB{{ trigger.triggerFieldName }}\u00BB ha un valore di default, e un campo\r\n precompilato dal flow innesca l\u2019action gi\u00E0 all\u2019arrivo sulla schermata.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (triggerHasNoCause(trigger)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Senza un campo che la innesca e senza l\u2019invocazione al caricamento, questo trigger non\r\n scattera\u2019 mai (SCREEN_TRIGGER_WITHOUT_CAUSE).\r\n </p>\r\n }\r\n\r\n <fb-condition-editor\r\n [holder]=\"triggerConditions(trigger)\"\r\n title=\"Invoca l\u2019action quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'triggers[' + $index + ']'\"\r\n (changed)=\"onTriggerConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun trigger: le action dichiarate non verranno invocate.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenTrigger()\">Aggiungi trigger</button>\r\n </fieldset>\r\n } @else {\r\n <!-- ---------------------------------------------------- proprieta' del campo -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">{{ captionOf(selectedField()!) }}</h3>\r\n <div class=\"fb-scr__props-actions\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta su\" (click)=\"moveSelected(-1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta giu\u2019\" (click)=\"moveSelected(1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--sm\"\r\n title=\"Porta fuori dal contenitore\"\r\n [disabled]=\"!canOutdent()\"\r\n (click)=\"outdentSelected()\"\r\n >\r\n \u21E4\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Duplica\" (click)=\"duplicateSelected()\">\u29C9</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--sm\" title=\"Elimina\" (click)=\"removeSelected()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.fieldType || ''\"\r\n (change)=\"setFieldType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n <!--\r\n \u00A75.2 \u2014 dentro una riga la sezione ripetibile non c'e': **non si annidano**, perche' il\r\n cursore e' una variabile sola per sezione. La validazione lo rifiuterebbe comunque, ma\r\n lasciar comporre e poi accusare fa perdere tempo due volte.\r\n -->\r\n @for (entry of fieldTypeOptions(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo sta dentro la riga di <code>{{ selectedRepeater()!.name }}</code>: esiste una\r\n volta per ogni elemento della collection, quindi\r\n <strong>non e\u2019 una risorsa del flow</strong> e non si nomina per nome. Dentro una riga si\r\n nomina il cursore \u2014\r\n <code>{{ rowCursor() || 'itemReference' }}.<membro></code> \u2014 e scrivere\r\n <code>{{ selectedField()!.name }}</code> in una condizione, in una formula o in un parametro\r\n e\u2019 <code>SCREEN_FIELD_IN_REPEATER</code>.\r\n </p>\r\n }\r\n @if (!selectedField()!.fieldType) {\r\n <p class=\"fb-field__error\">Il tipo e\u2019 obbligatorio: senza, SCREEN_FIELD_TYPE_MISSING.</p>\r\n } @else if (isUnknownType(selectedField())) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo tipo non e\u2019 nel dizionario: l\u2019editor non sa quali campi ammetta e mostra solo i comuni.\r\n </p>\r\n } @else if (selectedType()?.description) {\r\n <p class=\"fb-field__hint\">{{ selectedType()?.description }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome</label>\r\n <!-- Sul `change` e non sull\u2019`input`: la rinomina riscrive i riferimenti nel documento,\r\n e farlo a ogni tasto significherebbe riscriverlo per ogni lettera. -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.name || ''\"\r\n (change)=\"setFieldName($any($event.target).value)\"\r\n />\r\n @if (selectedIsResource() && !isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo e\u2019 una <strong>risorsa</strong>: lo referenzi come\r\n <code>{{ selectedField()!.name || 'Nome' }}</code> in condizioni, formule e parametri. \u00C8 di\r\n sola lettura per il flow \u2014 un Assignment che ci scrive e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n } @else if (isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Il nome resta comunque <strong>unico</strong>: e\u2019 l\u2019indirizzo con cui il frontend rende il\r\n campo, e un omonimo di una variabile e\u2019 <code>NAME_DUPLICATED</code> come sempre.\r\n </p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 `memberName` e il riferimento con cui gli altri campi lo nominano sono la **stessa\r\n domanda posta due volte**: `memberName: \"PodId\"` e `PuntoCorrente.PodId` sono lo stesso\r\n membro, e la classe da cui vengono e' l'`objectType` della variabile in `collectionReference`.\r\n -->\r\n @if (isInsideRepeater() && selectedType()?.storesValue) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Membro della riga</label>\r\n @if (rowClass()) {\r\n <fb-structure-member-picker\r\n [value]=\"selectedField()!.memberName\"\r\n [className]=\"rowClass()\"\r\n usage=\"writable\"\r\n label=\"Membro della riga\"\r\n (valueChange)=\"setFieldProperty('memberName', $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.memberName || ''\"\r\n (change)=\"setFieldProperty('memberName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La classe delle righe non e\u2019 nota: la sezione non dichiara una <code>collectionReference</code>\r\n valida, quindi i membri non si possono proporre.\r\n </p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n Il membro della riga in cui finisce il valore. Senza, vale il nome del campo. Dentro una\r\n riga lo si legge come\r\n <code>{{ rowCursor() || 'itemReference' }}.{{ selectedField()!.memberName || selectedField()!.name }}</code>.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il ramo dei contenitori vale anche per una sezione ripetibile: l\u00EC `fieldText` e'\r\n l'intestazione del blocco, non l'etichetta di un valore \u2014 l'etichetta della **riga** e'\r\n `itemLabel`, ed e' un campo suo, piu' sotto (\u00A75.2).\r\n -->\r\n @if (!holdsFields(selectedField())) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ selectedField()!.fieldType === 'DisplayText' ? 'Testo' : 'Etichetta' }}\r\n </label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n ></textarea>\r\n <p class=\"fb-field__hint\">\r\n Supporta i merge field <code>{!Riferimento}</code>.\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 su un componente l\u2019etichetta e\u2019 **ammessa** e viene consegnata al frontend\r\n in `label`, ma la sua assenza non e\u2019 un rilievo: un componente di solito disegna\r\n la propria intestazione. Nasconderla, come faceva prima l\u2019editor, toglieva un\r\n campo che il contratto prevede.\r\n -->\r\n Facoltativa: un componente di solito disegna la propria intestazione.\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.helpText || ''\"\r\n (input)=\"setFieldProperty('helpText', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Larghezza</label>\r\n <div class=\"fb-scr__width\">\r\n <input\r\n type=\"range\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n [value]=\"widthOf(selectedField()!)\"\r\n (input)=\"setNumberProperty('width', $any($event.target).value)\"\r\n />\r\n <span class=\"fb-scr__width-value\">{{ widthOf(selectedField()!) }}/12</span>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Colonne della griglia della schermata. \u00C8 un\u2019indicazione: il frontend puo\u2019 ignorarla.\r\n </p>\r\n </div>\r\n\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 perche\u2019 qui non c\u2019e\u2019 \u00ABobbligatorio\u00BB. La domanda arriva sempre, e la risposta non\r\n si indovina guardando l\u2019interfaccia: un componente non ha un valore proprio, ce l\u2019hanno\r\n i suoi output, che vanno in **variabili** scelte dall\u2019autore. \u00ABObbligatorio\u00BB dovrebbe\r\n dire *quale output* deve essere valorizzato, e il metadata non ha modo di dirlo \u2014\r\n dichiararlo qui sarebbe `SCREEN_FIELD_CONFIGURATION_INVALID` e il runtime lo\r\n ignorerebbe. Le due strade vere stanno nel contratto e sono queste.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Un componente non raccoglie un valore \u2014 lo fanno i suoi output \u2014 quindi obbligatorieta\u2019, valore\r\n predefinito e regola di validazione non si applicano: dichiararli e\u2019\r\n <code>SCREEN_FIELD_CONFIGURATION_INVALID</code> e il runtime li ignora. Per pretendere una\r\n compilazione: la validazione la fa il componente stesso, oppure si controlla la variabile di\r\n destinazione con una Decision dopo la schermata.\r\n </p>\r\n }\r\n\r\n <!-- ------------------------------------------------ campi che raccolgono un valore -->\r\n @if (selectedType()?.storesValue) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valore</legend>\r\n\r\n @if (selectedField()!.fieldType !== 'ObjectProvided') {\r\n <div class=\"fb-field\">\r\n <label\r\n class=\"fb-field__label\"\r\n [class.fb-field__label--required]=\"!!selectedType()?.requiresDataType\"\r\n >\r\n Tipo di dato\r\n </label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.dataType || ''\"\r\n (change)=\"setFieldProperty('dataType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.dataTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (selectedType()?.requiresDataType && !selectedField()!.dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio per questo tipo di campo (DATA_TYPE_MISSING).</p>\r\n }\r\n @if (selectedType()?.isCollection) {\r\n <p class=\"fb-field__hint\">\r\n Il valore raccolto e\u2019 una <strong>collection</strong>, non una stringa con i valori\r\n separati: nelle condizioni si usano gli operatori di collection.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\" [class.fb-field__label--required]=\"objectTypeIsStructure()\">\r\n {{\r\n objectTypeIsStructure()\r\n ? 'Classe'\r\n : selectedField()!.dataType === 'Enum'\r\n ? 'Tipo di enumerazione'\r\n : 'Oggetto'\r\n }}\r\n </label>\r\n @if (selectedField()!.dataType === 'Enum') {\r\n <!-- Dizionario chiuso: si sceglie, non si scrive. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.objectType || ''\"\r\n (change)=\"setFieldProperty('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza il tipo concreto l\u2019editor non puo\u2019 proporre i valori dell\u2019enumerazione.\r\n </p>\r\n }\r\n } @else if (objectTypeIsStructure()) {\r\n <fb-structure-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Classe\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, il runtime non ha nulla da istanziare\r\n (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo dell\u2019entita\u2019</label>\r\n <p class=\"fb-field__hint\">\r\n Tipo, label, obbligatorieta\u2019, scala e \u2014 se il campo e\u2019 una picklist \u2014 le opzioni arrivano\r\n dallo schema dati: qui non si ridichiarano.\r\n </p>\r\n <fb-object-picker\r\n [value]=\"providedObject() || undefined\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setProvidedObject($event)\"\r\n />\r\n <fb-field-picker\r\n [value]=\"providedField() || undefined\"\r\n [object]=\"providedObject() || undefined\"\r\n label=\"Campo\"\r\n (valueChange)=\"setProvidedField($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsRequired()\"\r\n (change)=\"setRequired($any($event.target).checked)\"\r\n />\r\n Obbligatorio\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsEditable()\"\r\n (change)=\"setEditable($any($event.target).checked)\"\r\n />\r\n Modificabile\r\n </label>\r\n @if (!selectedIsEditable()) {\r\n <p class=\"fb-field__hint\">\r\n A <code>false</code> il runtime <strong>ignora</strong> cio\u2019 che il client rimanda indietro:\r\n e\u2019 l\u2019unico modo di rendere un campo davvero di sola lettura.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore di default</label>\r\n <fb-value-editor\r\n [value]=\"selectedField()!.defaultValue\"\r\n [dataType]=\"selectedField()!.dataType\"\r\n [objectType]=\"selectedField()!.objectType\"\r\n [isCollection]=\"selectedType()?.isCollection\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n label=\"Valore di default\"\r\n (valueChange)=\"setDefaultValue($event)\"\r\n />\r\n </div>\r\n\r\n @if (scaleApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.scale ?? ''\"\r\n (change)=\"setNumberProperty('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n } @else if (selectedField()!.scale !== undefined) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>scale</code> su un campo non numerico e\u2019 SCALE_NOT_APPLICABLE.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Lunghezza massima</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxLength ?? ''\"\r\n (change)=\"setNumberProperty('maxLength', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Verificata anche dal runtime (TOO_LONG).</p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tornando sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.inputsOnNextNavToAssocScrn || ''\"\r\n (change)=\"setFieldProperty('inputsOnNextNavToAssocScrn', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito (mantieni i valori)</option>\r\n @for (entry of dictionary.screenFieldInputsRevisited(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Regola di validazione</label>\r\n <!--\r\n `commitOn=\"change\"`: qui mutare il documento ridisegna l'anteprima, e riscriverla a\r\n ogni tasto costa. La verifica parte lo stesso mentre si digita (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"selectedField()!.validationRule?.formulaExpression || ''\"\r\n usage=\"ValidationRule\"\r\n expectedDataType=\"Boolean\"\r\n commitOn=\"change\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [rows]=\"2\"\r\n placeholder=\"Espressione, es. LEN(Nome) > 3\"\r\n ariaLabel=\"Regola di validazione\"\r\n (expressionChange)=\"setValidationRule('formulaExpression', $event)\"\r\n />\r\n <input\r\n class=\"fb-input\"\r\n placeholder=\"Messaggio mostrato quando l\u2019espressione e\u2019 falsa\"\r\n [value]=\"selectedField()!.validationRule?.errorMessage || ''\"\r\n (change)=\"setValidationRule('errorMessage', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione la valuta il motore di regole: il backend non la interpreta, la fa\r\n verificare al motore.\r\n </p>\r\n </div>\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------------------- choice -->\r\n @if (selectedType()?.acceptsChoices) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Opzioni</legend>\r\n <p class=\"fb-section__note\">\r\n Accetta <strong>Choice</strong> e <strong>Dynamic choice set</strong>, nell\u2019ordine in cui le\r\n opzioni compaiono. Puntare a una variabile e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\r\n </p>\r\n\r\n @if (!selectedField()!.choiceReferences?.length) {\r\n @if (selectedField()!.fieldType === 'ObjectProvided') {\r\n <!-- Le opzioni di una picklist arrivano dallo schema: qui si aggiungono solo se\r\n si vuole sostituirle, e non dichiararne nessuna e' il caso normale. -->\r\n <p class=\"fb-field__hint\">\r\n Se il campo dello schema e\u2019 una picklist, le opzioni arrivano da l\u00EC: dichiararle qui\r\n serve solo a sostituirle.\r\n </p>\r\n } @else {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessuna opzione: il campo non ha niente da mostrare (SCREEN_FIELD_CHOICES_MISSING).\r\n </p>\r\n }\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__title\">{{ choice }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, -1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, 1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--sm\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeChoice($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggiungi un\u2019opzione</label>\r\n <!--\r\n `commitOn=\"enter\"`: qui ogni emissione e\u2019 una riga in piu\u2019, non un campo che cambia\r\n valore \u2014 a tasto, scrivere \u00ABAlta\u00BB aggiungeva \u00ABA\u00BB, \u00ABAl\u00BB, \u00ABAlt\u00BB e \u00ABAlta\u00BB.\r\n -->\r\n <fb-name-picker\r\n [options]=\"choiceOptions()\"\r\n label=\"Choice\"\r\n commitOn=\"enter\"\r\n placeholder=\"Scegli una choice o un choice set\"\r\n unknownMessage=\"Questo nome non e\u2019 una choice ne\u2019 un choice set: e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Il flow non dichiara nessuna choice: creale nel pannello delle risorse.\"\r\n (valueChange)=\"addChoice($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Scegli dall\u2019elenco, oppure scrivi il nome e premi Invio.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Opzione selezionata all\u2019apertura</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.defaultSelectedChoiceReference || ''\"\r\n (change)=\"setFieldProperty('defaultSelectedChoiceReference', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 nessuna \u2014</option>\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <option [value]=\"choice\">{{ choice }}</option>\r\n }\r\n </select>\r\n @if (defaultChoiceIsForeign()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n \u00AB{{ selectedField()!.defaultSelectedChoiceReference }}\u00BB non e\u2019 fra le opzioni elencate qui\r\n sopra.\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedAutoSelectsSingleChoice()\"\r\n (change)=\"setAutoSelectSingleChoice($any($event.target).checked)\"\r\n />\r\n Scegli da sola l\u2019unica opzione\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Quando le opzioni risolte sono <strong>esattamente una</strong>, il campo la prende da se\u2019.\r\n Serve alle tendine che dipendono da un\u2019altra tendina \u2014 i CAP di un comune \u2014 dove chiedere\r\n di scegliere l\u2019unica voce non decide niente.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- contenitori -->\r\n @if (selectedType()?.isContainer) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Contenitore</legend>\r\n @if (selectedField()!.fieldType === 'RegionContainer') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di sezione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.regionContainerType || ''\"\r\n (change)=\"setFieldProperty('regionContainerType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (entry of dictionary.regionContainerTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il contenitore e\u2019 vuoto: non produce niente sulla schermata (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------ sezione ripetibile (\u00A75.2) -->\r\n @if (selectedIsRepeatingSection()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Righe</legend>\r\n <p class=\"fb-section__note\">\r\n I campi qui dentro sono il <strong>modello di una riga</strong>: la schermata ne rende una\r\n copia per ogni elemento della collection, e l\u2019utente decide quante con un tasto. I due\r\n riferimenti vanno pensati insieme \u2014 la <strong>collection</strong> e\u2019 dove le righe vivono,\r\n il <strong>cursore</strong> e\u2019 la riga corrente, come\r\n l\u2019<code>assignNextValueToReference</code> di un Loop.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection delle righe</label>\r\n <fb-reference-picker\r\n [value]=\"selectedField()!.collectionReference\"\r\n dataType=\"Structure\"\r\n [isCollection]=\"true\"\r\n [writableOnly]=\"true\"\r\n label=\"Collection delle righe\"\r\n placeholder=\"Una variabile collection di tipo Structure\"\r\n (valueChange)=\"setFieldProperty('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 quella che il resto del flow attraversa con un Loop: la sezione non introduce un posto\r\n nuovo in cui i dati stanno. Se la riga e\u2019 il DTO che poi finisce in una action, conviene\r\n che sia una collection <strong>di quel DTO</strong>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cursore della riga</label>\r\n <fb-reference-picker\r\n [value]=\"selectedField()!.itemReference\"\r\n dataType=\"Structure\"\r\n [isCollection]=\"false\"\r\n [objectType]=\"rowClass()\"\r\n [writableOnly]=\"true\"\r\n label=\"Cursore della riga\"\r\n placeholder=\"Una variabile Structure della stessa classe\"\r\n (valueChange)=\"setFieldProperty('itemReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il nome che si scrive <strong>dentro</strong> la riga: visibilita\u2019, regole di\r\n validazione, filtri delle tendine, parametri dei componenti e\r\n <code>itemLabel</code> nominano\r\n <code>{{ selectedField()!.itemReference || 'Cursore' }}.<membro></code>, mai il nome\r\n di un campo della riga.\r\n </p>\r\n </div>\r\n\r\n @if (repeaterIssue(); as issue) {\r\n <p class=\"fb-callout fb-callout--error\">{{ issue }} (REPEATING_SECTION_INVALID)</p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe minime</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.minInstances ?? ''\"\r\n (change)=\"setNumberProperty('minInstances', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe massime</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxInstances ?? ''\"\r\n (change)=\"setNumberProperty('maxInstances', $any($event.target).value)\"\r\n />\r\n @if (repeaterHasNoLimit()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza un massimo l\u2019utente puo\u2019 aggiungere righe all\u2019infinito\r\n (REPEATING_SECTION_WITHOUT_LIMIT).\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.allowAdd !== false\"\r\n (change)=\"setRepeaterFlag('allowAdd', $any($event.target).checked)\"\r\n />\r\n L\u2019utente puo\u2019 aggiungere righe\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.allowRemove !== false\"\r\n (change)=\"setRepeaterFlag('allowRemove', $any($event.target).checked)\"\r\n />\r\n L\u2019utente puo\u2019 togliere righe\r\n </label>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Scritta del comando \u00ABaggiungi\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.addButtonLabel || ''\"\r\n (input)=\"setFieldProperty('addButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Scritta del comando \u00ABtogli\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.removeButtonLabel || ''\"\r\n (input)=\"setFieldProperty('removeButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione della singola riga</label>\r\n <!-- `commitOn=\"change\"`: mutare il documento ridisegna l'anteprima. -->\r\n <fb-merge-text-editor\r\n [text]=\"selectedField()!.itemLabel || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Punto {{ '{' }}!Cursore.PodId}\"\r\n ariaLabel=\"Intestazione della riga\"\r\n (textChange)=\"setFieldProperty('itemLabel', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Risolta <strong>sulla riga</strong>: i merge field qui nominano il cursore\r\n (<code>{!{{ selectedField()!.itemReference || 'Cursore' }}.Membro}</code>), non\r\n un campo della riga.\r\n </p>\r\n </fb-merge-text-editor>\r\n </div>\r\n\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il modello della riga e\u2019 vuoto: la sezione non mostra niente (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------- ComponentInstance -->\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Componente</legend>\r\n <p class=\"fb-section__note\">\r\n Componenti e form sono la stessa domanda al frontend \u2014 cosa sa rendere, e con quali parametri\r\n \u2014 e passano dallo stesso catalogo. Un <code>ComponentInstance</code> non ha un valore proprio:\r\n lo hanno i suoi output.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Componente</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.extensionName\"\r\n [options]=\"componentOptions()\"\r\n [unusableOptions]=\"unusableComponents()\"\r\n label=\"Componente\"\r\n placeholder=\"Scrivi o scegli un componente\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo componente non esiste nel catalogo: e\u2019 SCREEN_COMPONENT_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 una schermata intera, non un componente montabile qui (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei componenti non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFieldProperty('extensionName', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.extensionName) {\r\n <p class=\"fb-field__error\">Obbligatorio: senza, SCREEN_COMPONENT_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 dentro una riga la spunta **non c'e'**: gli output automatici si espongono come\r\n `<NomeCampo>.<NomeOutput>`, che sarebbe **una risorsa sola per tutte le righe** \u2014 l'ultima\r\n che ha girato. \u00C8 `REPEATING_SECTION_INVALID`, e la strada giusta e' `assignToReference` su\r\n un membro del cursore, che una riga ce l'ha per davvero.\r\n -->\r\n @if (!isInsideRepeater()) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Rendi gli output referenziabili automaticamente\r\n </label>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n Gli output si referenziano come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, senza dichiarare\r\n variabili.\r\n </p>\r\n }\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Output automatici <strong>e</strong> parametri di uscita insieme:\r\n OUTPUT_CONFIGURATION_CONFLICT.\r\n </p>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli output automatici qui non si possono usare: si esporrebbero come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, cioe\u2019 una risorsa sola per\r\n tutte le righe \u2014 l\u2019ultima che ha girato (REPEATING_SECTION_INVALID). Assegnali con\r\n <strong>assignToReference</strong> a un membro di\r\n <code>{{ rowCursor() || 'Cursore' }}</code>.\r\n </p>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo componente dichiara gli output automatici e sta dentro una riga:\r\n REPEATING_SECTION_INVALID.\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"setStoreOutputAutomatically(false)\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n }\r\n\r\n <fb-parameter-editor\r\n [holder]=\"$any(selectedField())\"\r\n [catalogParameters]=\"componentParameterList()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n inputTitle=\"Valori passati al componente\"\r\n outputTitle=\"Valori raccolti dal componente\"\r\n [showOutputs]=\"!selectedField()!.storeOutputAutomatically\"\r\n outputsDisabledReason=\"Gli output sono automatici: disattivalo per assegnarli a variabili.\"\r\n (changed)=\"onComponentParametersChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- visibilita' -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Visibilita\u2019</legend>\r\n <p class=\"fb-section__note\">\r\n Le regole si rivalutano <strong>sui valori appena inviati</strong>: e\u2019 cos\u00EC che un campo compare\r\n in funzione di un altro campo della stessa schermata. Un campo risultato nascosto viene\r\n <strong>azzerato</strong> e non viene validato.\r\n </p>\r\n <fb-condition-editor\r\n [holder]=\"visibilityRule()\"\r\n title=\"Mostra il campo quando\"\r\n [allowFormula]=\"false\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [issuePath]=\"'fields[' + (selectedField()!.name || '') + '].visibilityRule'\"\r\n (changed)=\"onVisibilityChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<!--\r\n ============================================================== l'anteprima\r\n Due template ricorsivi. `listTpl` disegna i figli di un contenitore, e nient'altro: `data-item`\r\n marca cio' che conta come fratello nel calcolo del punto di rilascio, e il segnaposto del\r\n contenitore vuoto ne resta fuori. La barra di inserimento non e' qui \u2014 sta sopra l'anteprima,\r\n vedi il velo nel frame.\r\n-->\r\n<ng-template #listTpl let-fields let-parent=\"parent\" let-axis=\"axis\">\r\n @for (child of fields; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"fieldTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: child, path: childPath(parent, $index), axis: axis }\"\r\n />\r\n }\r\n @if (!fields.length) {\r\n <p class=\"fb-scr__drop-hint\">Trascina qui un componente</p>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #fieldTpl let-field let-path=\"path\" let-axis=\"axis\">\r\n <div\r\n class=\"fb-scr__item\"\r\n data-item=\"\"\r\n cdkDrag\r\n [style.grid-column]=\"'span ' + widthOf(field)\"\r\n [class.fb-scr__item--selected]=\"isSelected(path)\"\r\n [class.fb-scr__item--dragging]=\"isDragging(path)\"\r\n [class.fb-scr__item--container]=\"holdsFields(field)\"\r\n (cdkDragStarted)=\"onFieldDragStarted(path)\"\r\n (cdkDragMoved)=\"onFieldDragMoved($event)\"\r\n (cdkDragEnded)=\"onFieldDragEnded($event, path)\"\r\n (click)=\"select(path); $event.stopPropagation()\"\r\n >\r\n <span class=\"fb-scr__grip\" cdkDragHandle title=\"Trascina per spostare\" aria-hidden=\"true\">\u283F</span>\r\n @if (field.visibilityRule?.conditions?.length) {\r\n <span class=\"fb-scr__flag\" title=\"Ha una regola di visibilita\u2019\">\u25D0</span>\r\n }\r\n\r\n @switch (field.fieldType) {\r\n @case ('RegionContainer') {\r\n <div class=\"fb-scr__section\">\r\n @if (field.regionContainerType !== 'SectionWithoutHeader') {\r\n <header class=\"fb-scr__section-head\">{{ field.fieldText || field.name }}</header>\r\n }\r\n <div class=\"fb-scr__cols\" [attr.data-drop]=\"dropId(path)\" data-axis=\"row\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'row' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('Region') {\r\n <div class=\"fb-scr__region\">\r\n <span class=\"fb-scr__region-tag\">{{ field.name }} \u00B7 {{ widthOf(field) }}/12</span>\r\n <div class=\"fb-scr__region-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('RepeatingSection') {\r\n <!--\r\n \u00A75.2 \u2014 si disegna **una** riga, ed e' il modello: a runtime la schermata ne rende una copia\r\n per elemento della collection. Disegnarne due sarebbe finto \u2014 quante siano lo decide chi\r\n compila \u2014 e disegnarla senza cornice farebbe credere che i campi siano della schermata.\r\n -->\r\n <div class=\"fb-scr__repeat\">\r\n <header class=\"fb-scr__repeat-head\">\r\n <span>{{ field.fieldText || field.name }}</span>\r\n <span class=\"fb-scr__repeat-meta\">\r\n {{ field.collectionReference || 'collection non indicata' }}\r\n @if (field.minInstances !== undefined || field.maxInstances !== undefined) {\r\n \u00B7 {{ field.minInstances ?? 0 }}\u2013{{ field.maxInstances ?? '\u221E' }}\r\n }\r\n </span>\r\n </header>\r\n <div class=\"fb-scr__repeat-row\">\r\n <span class=\"fb-scr__repeat-tag\">\r\n {{ field.itemLabel || 'Riga 1' }}\r\n @if (field.allowRemove !== false) {\r\n <span class=\"fb-scr__repeat-x\" aria-hidden=\"true\">\u00D7</span>\r\n }\r\n </span>\r\n <div class=\"fb-scr__repeat-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n @if (field.allowAdd !== false) {\r\n <span class=\"fb-scr__repeat-add\">+ {{ field.addButtonLabel || 'Aggiungi' }}</span>\r\n }\r\n </div>\r\n }\r\n @case ('DisplayText') {\r\n <p class=\"fb-scr__display\">{{ field.fieldText || '(testo vuoto)' }}</p>\r\n }\r\n @case ('LargeTextArea') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--area\">{{ placeholderOf(field) }}</div>\r\n }\r\n @case ('PasswordField') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022</div>\r\n }\r\n @case ('DropdownBox') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ field.defaultSelectedChoiceReference || choiceLabelsOf(field)[0] || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u25BE</span>\r\n </div>\r\n }\r\n @case ('MultiSelectPicklist') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ choiceLabelsOf(field).join(', ') || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u2261</span>\r\n </div>\r\n }\r\n @case ('RadioButtons') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u25EF {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('MultiSelectCheckboxes') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u2610 {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('ComponentInstance') {\r\n <!--\r\n L\u2019etichetta si disegna se c\u2019e\u2019: il runtime la consegna in `label` come per gli altri\r\n campi (\u00A75.2). Nessun asterisco: un componente non raccoglie un valore, quindi\r\n \u00ABobbligatorio\u00BB non ha niente su cui applicarsi.\r\n -->\r\n @if (field.fieldText) {\r\n <label class=\"fb-scr__label\">{{ field.fieldText }}</label>\r\n }\r\n <div class=\"fb-scr__component\">\r\n <span aria-hidden=\"true\">\u2B21</span>\r\n {{ field.extensionName || 'Componente non indicato' }}\r\n </div>\r\n }\r\n @case ('ObjectProvided') {\r\n <label class=\"fb-scr__label\">\r\n {{ field.fieldText || field.objectFieldReference || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n <span class=\"fb-scr__tag\">{{ field.objectFieldReference || 'campo non indicato' }}</span>\r\n }\r\n @default {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n @if (isUnknownType(field)) {\r\n <span class=\"fb-scr__tag fb-scr__tag--warn\">{{ field.fieldType }}: tipo non nel dizionario</span>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".fb-scr{display:grid;grid-template-columns:220px minmax(0,1fr) 340px;gap:12px;align-items:start;margin-bottom:14px}@media(max-width:1100px){.fb-scr{grid-template-columns:minmax(0,1fr)}}.fb-scr__side,.fb-scr__props{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__tabs{display:flex;gap:4px;margin-bottom:8px}.fb-scr__tab{flex:1;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;font-weight:600;cursor:pointer}.fb-scr__tab--active{border-color:var(--fb-accent, #4f6ef7);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent);color:var(--fb-accent-strong, #3d59e0)}.fb-scr__side-hint,.fb-scr__side-empty{margin:6px 0;font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__palette{display:flex;flex-direction:column;gap:4px;margin:8px 0 0;padding:0;max-height:320px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-scr__chip{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-scr__chip:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-scr__chip-icon{flex:none;width:20px;text-align:center;color:var(--fb-text-muted, #6b7086)}.fb-scr__chip-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-scr__chip-label small{margin-left:4px;color:var(--fb-text-subtle, #98a2b3);font-size:10px}.fb-scr__canvas{min-width:0;padding:14px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-canvas-bg, #f4f5f7);max-height:60vh;overflow-y:auto}.fb-scr__frame{position:relative;display:flex;flex-direction:column;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));overflow:hidden}.fb-scr__frame-head{display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:13px;font-weight:600}.fb-scr__help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px}.fb-scr__body,.fb-scr__region-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:12px;min-height:90px}.fb-scr__region-body{padding:8px;min-height:60px}.fb-scr__cols{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-items:stretch;gap:0;padding:8px;min-height:60px}.fb-scr__cols>.fb-scr__item{display:flex;align-self:stretch}.fb-scr__cols>.fb-scr__item>.fb-scr__region{flex:1;min-width:0}.fb-scr__drop-hint{grid-column:1 / -1;margin:0;padding:10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);color:var(--fb-text-subtle, #98a2b3);font-size:11px;text-align:center}.fb-scr__marker{position:absolute;z-index:2;border-radius:2px;background:var(--fb-accent, #4f6ef7);pointer-events:none}.fb-scr__item{position:relative;box-sizing:border-box;padding:6px 8px;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);cursor:pointer}.fb-scr__item:hover{border-color:var(--fb-border-strong, #cfd4de);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 4%,transparent)}.fb-scr__item--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:inset 0 0 0 1px var(--fb-accent, #4f6ef7)}.fb-scr__item--dragging{opacity:.45;pointer-events:none}.fb-scr__grip{position:absolute;top:2px;left:-2px;padding:0 3px;color:var(--fb-text-subtle, #98a2b3);font-size:11px;line-height:1;opacity:0;cursor:grab}.fb-scr__item:hover>.fb-scr__grip,.fb-scr__item--selected>.fb-scr__grip{opacity:1}.fb-scr__flag{position:absolute;top:2px;right:4px;color:var(--fb-text-muted, #6b7086);font-size:11px}.fb-scr__label{display:block;margin-bottom:3px;font-size:11px;font-weight:600;color:var(--fb-text, #1a1c23)}.fb-scr__req{color:var(--fb-error, #dc2626)}.fb-scr__control{display:flex;align-items:center;justify-content:space-between;gap:6px;min-height:26px;padding:4px 8px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-subtle, #98a2b3);font-size:12px;pointer-events:none}.fb-scr__control--area{min-height:54px;align-items:flex-start}.fb-scr__display{margin:0;font-size:12px;color:var(--fb-text, #1a1c23)}.fb-scr__options{display:flex;flex-direction:column;gap:2px;pointer-events:none}.fb-scr__option{font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__option--missing{color:var(--fb-warning, #b7791f)}.fb-scr__component{display:flex;align-items:center;gap:6px;padding:10px;border:1px dashed var(--fb-accent, #4f6ef7);border-radius:var(--fb-radius-xs, 6px);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 5%,transparent);font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__tag{display:inline-block;margin-top:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__tag--warn{color:var(--fb-warning, #b7791f)}.fb-scr__section{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__section-head{padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__region{border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__region-tag{display:block;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__repeat-head{display:flex;align-items:baseline;gap:8px;padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__repeat-meta{margin-left:auto;font-size:10px;font-weight:400;text-transform:none;letter-spacing:0;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat-row{margin:8px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__repeat-tag{display:flex;align-items:center;gap:6px;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat-x{margin-left:auto}.fb-scr__repeat-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:8px;min-height:60px}.fb-scr__repeat-add{display:inline-block;margin:0 8px 8px;padding:3px 10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__frame-foot{display:flex;align-items:center;gap:6px;padding:8px 12px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__spacer{flex:1}.fb-scr__btn{padding:3px 10px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__btn--primary{border-color:var(--fb-accent, #4f6ef7);background:var(--fb-accent, #4f6ef7);color:var(--fb-accent-contrast, #fff)}.fb-scr__canvas-note{margin-top:10px}.fb-scr__props{background:var(--fb-surface, #fff)}.fb-scr__props-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.fb-scr__props-title{flex:1;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__props-actions{display:flex;flex-wrap:wrap;gap:3px}.fb-btn--sm{padding:3px 7px;font-size:11px}.fb-scr__width{display:flex;align-items:center;gap:8px}.fb-scr__width input[type=range]{flex:1;min-width:0}.fb-scr__width-value{font-size:11px;color:var(--fb-text-muted, #6b7086)}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "preferredRoots", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: MergeTextEditorComponent, selector: "fb-merge-text-editor", inputs: ["text", "usage", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["textChange"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }, { kind: "component", type: StructureMemberPickerComponent, selector: "fb-structure-member-picker", inputs: ["value", "className", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12341
12892
|
}
|
|
12342
12893
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: DynamicScreenInspectorComponent, decorators: [{
|
|
12343
12894
|
type: Component,
|
|
@@ -12348,14 +12899,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
12348
12899
|
ConnectorEditorComponent,
|
|
12349
12900
|
FieldPickerComponent,
|
|
12350
12901
|
FormulaEditorComponent,
|
|
12902
|
+
MergeTextEditorComponent,
|
|
12351
12903
|
NamePickerComponent,
|
|
12352
12904
|
NgTemplateOutlet,
|
|
12353
12905
|
ObjectPickerComponent,
|
|
12354
12906
|
ParameterEditorComponent,
|
|
12907
|
+
ReferencePickerComponent,
|
|
12355
12908
|
SelectValueDirective,
|
|
12909
|
+
StructureMemberPickerComponent,
|
|
12356
12910
|
StructurePickerComponent,
|
|
12357
12911
|
ValueEditorComponent,
|
|
12358
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-scr\" #composer>\r\n <!-- ============================================================ sinistra -->\r\n <aside class=\"fb-scr__side\" aria-label=\"Componenti e campi\">\r\n <div class=\"fb-scr__tabs\" role=\"tablist\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'components'\"\r\n [attr.aria-selected]=\"paletteTab() === 'components'\"\r\n (click)=\"setPaletteTab('components')\"\r\n >\r\n Componenti\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'fields'\"\r\n [attr.aria-selected]=\"paletteTab() === 'fields'\"\r\n (click)=\"setPaletteTab('fields')\"\r\n >\r\n Campi\r\n </button>\r\n </div>\r\n\r\n @if (paletteTab() === 'components') {\r\n @if (!dictionary.screenFieldTypes().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il dizionario <code>screenFieldTypes</code> non e\u2019 disponibile: senza i suoi flag l\u2019editor non\r\n sa quali campi ammette ciascun tipo, e non li inventa.\r\n </p>\r\n }\r\n <p class=\"fb-scr__side-hint\">Trascina sulla schermata, oppure clicca per aggiungere.</p>\r\n <ul class=\"fb-scr__palette\">\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"entry.description || entry.label\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onComponentDragEnded($event, entry.value)\"\r\n (click)=\"addComponentAtSelection(entry.value)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">{{ iconOf(entry.value) }}</span>\r\n <span class=\"fb-scr__chip-label\">{{ entry.label }}</span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p class=\"fb-scr__side-hint\">\r\n Un campo dell\u2019entita\u2019 porta con se\u2019 tipo, etichetta, obbligatorieta\u2019 e opzioni: arrivano dallo\r\n schema dati, qui non si ridichiarano.\r\n </p>\r\n <!-- Il catalogo non e' un dizionario chiuso: si sceglie o si scrive, come ovunque si\r\n indichi un'entita' (vedi `object-picker`). -->\r\n <fb-object-picker\r\n [value]=\"fieldsObject()\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Scrivi o scegli un\u2019entita\u2019\"\r\n (valueChange)=\"setFieldsObject($event)\"\r\n />\r\n @if (fieldsObject() && !fieldsOfObject().length) {\r\n <p class=\"fb-scr__side-empty\">\r\n Nessun campo dichiarato per questa entita\u2019: il catalogo non li espone, non significa che non\r\n ci siano.\r\n </p>\r\n }\r\n <ul class=\"fb-scr__palette\">\r\n @for (field of fieldsOfObject(); track $index) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"field.name + (field.dataType ? ' \u00B7 ' + field.dataType : '')\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onObjectFieldDragEnded($event, field)\"\r\n (click)=\"addObjectFieldAtSelection(field)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">\u2317</span>\r\n <span class=\"fb-scr__chip-label\">\r\n {{ field.label || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n <small>{{ field.dataType }}</small>\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </aside>\r\n\r\n <!-- ============================================================== centro -->\r\n <!-- Cliccare fuori da un campo riporta a destra le proprieta' della schermata. -->\r\n <section class=\"fb-scr__canvas\" aria-label=\"Anteprima della schermata\" (click)=\"selectScreen()\">\r\n <div class=\"fb-scr__frame\">\r\n @if (showHeader()) {\r\n <header class=\"fb-scr__frame-head\">\r\n {{ $any(node()).label || name() }}\r\n @if (screen().helpText) {\r\n <span class=\"fb-scr__help\" [title]=\"screen().helpText || ''\">?</span>\r\n }\r\n </header>\r\n }\r\n\r\n <div\r\n class=\"fb-scr__body\"\r\n [attr.data-drop]=\"''\"\r\n data-axis=\"column\"\r\n [class.fb-scr__body--empty]=\"isEmpty()\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: screen().fields || [], parent: [], axis: 'column' }\"\r\n />\r\n </div>\r\n\r\n <!--\r\n La barra di inserimento e' un **velo** sopra l'anteprima, non un figlio del contenitore di\r\n rilascio: dentro la griglia occupava una riga intera e spostava in basso proprio la sezione\r\n che si stava puntando, che allora usciva da sotto il puntatore e il bersaglio oscillava.\r\n -->\r\n @if (dropMarker(); as bar) {\r\n <div\r\n class=\"fb-scr__marker\"\r\n [style.left.px]=\"bar.left\"\r\n [style.top.px]=\"bar.top\"\r\n [style.width.px]=\"bar.width\"\r\n [style.height.px]=\"bar.height\"\r\n ></div>\r\n }\r\n\r\n @if (showFooter()) {\r\n <footer class=\"fb-scr__frame-foot\">\r\n @if (allowPause()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().pauseButtonLabel || 'Pausa' }}</span>\r\n }\r\n <span class=\"fb-scr__spacer\"></span>\r\n @if (allowBack()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().backButtonLabel || 'Indietro' }}</span>\r\n }\r\n <span class=\"fb-scr__btn fb-scr__btn--primary\">\r\n {{ screen().nextOrFinishButtonLabel || (allowFinish() ? 'Fine' : 'Avanti') }}\r\n </span>\r\n </footer>\r\n }\r\n </div>\r\n\r\n @if (isEmpty()) {\r\n <p class=\"fb-callout fb-callout--warn fb-scr__canvas-note\">\r\n La schermata non ha campi: non c\u2019e\u2019 niente da mostrare all\u2019utente (SCREEN_WITHOUT_FIELDS).\r\n </p>\r\n }\r\n </section>\r\n\r\n <!-- ============================================================== destra -->\r\n <aside class=\"fb-scr__props\" aria-label=\"Proprieta\u2019\">\r\n @if (!selectedField()) {\r\n <!-- ------------------------------------------------ proprieta' della schermata -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">Schermata</h3>\r\n </header>\r\n <p class=\"fb-scr__side-hint\">Seleziona un campo nell\u2019anteprima per configurarlo.</p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setScreenText('helpText', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta della\r\n schermata.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setScreenFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setScreenFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setScreenFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showHeader()\" (change)=\"setScreenFlag('showHeader', $any($event.target).checked)\" />\r\n Mostra l\u2019intestazione\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showFooter()\" (change)=\"setScreenFlag('showFooter', $any($event.target).checked)\" />\r\n Mostra il piede\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setScreenText('pausedText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questa schermata non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e\r\n l\u2019utente resterebbe bloccato (SCREEN_DEAD_END).\r\n </p>\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pulsanti</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABindietro\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().backButtonLabel || ''\"\r\n (input)=\"setScreenText('backButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABavanti / fine\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().nextOrFinishButtonLabel || ''\"\r\n (input)=\"setScreenText('nextOrFinishButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABpausa\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pauseButtonLabel || ''\"\r\n (input)=\"setScreenText('pauseButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Stage mostrato nell\u2019avanzamento</label>\r\n <fb-name-picker\r\n [value]=\"screen().stageReference\"\r\n [options]=\"stageOptions()\"\r\n label=\"Stage\"\r\n placeholder=\"Scegli uno stage\"\r\n unknownMessage=\"Questo stage non e\u2019 dichiarato dal flow.\"\r\n emptyMessage=\"Il flow non dichiara stage: creali nel pannello delle risorse.\"\r\n (valueChange)=\"setScreenText('stageReference', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <!--\r\n Screen action (\u00A75.2). Due elenchi e non uno perche' il contratto li separa: piu' trigger\r\n possono invocare la stessa action, quindi \u00ABchi la chiama\u00BB non e' una sua proprieta'. Le\r\n action si aprono una alla volta: i cataloghi dipendono da tipo e nome, e tenerne N in volo\r\n sarebbe N volte la corsa fra risposte.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Azioni della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Un campo che l\u2019utente compila puo\u2019 innescare un\u2019action i cui risultati finiscono negli\r\n <strong>altri campi di questa schermata</strong>: e\u2019 il caso \u00ABscrivi il codice fiscale e nome\r\n e cognome compaiono da soli\u00BB. L\u2019alternativa sarebbe spezzare la schermata in due con un\r\n elemento Action in mezzo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of screenActions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [attr.aria-expanded]=\"selectedActionIndex() === $index\"\r\n (click)=\"selectAction(selectedActionIndex() === $index ? null : $index)\"\r\n >\r\n {{ selectedActionIndex() === $index ? '\u25BE' : '\u25B8' }}\r\n {{ action.name || '(senza nome)' }}\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <span class=\"fb-scr__side-hint\">{{ action.actionName || 'action non scelta' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019action\"\r\n (click)=\"removeScreenAction($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (actionLosesResult(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questa action viene invocata ma il suo risultato si perde: aggiungi un parametro di\r\n uscita, oppure accendi l\u2019output automatico (SCREEN_ACTION_WITHOUT_OUTPUTS).\r\n </p>\r\n }\r\n @if (actionHasNoTrigger(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun trigger la invoca: senza, non girera\u2019 mai.\r\n </p>\r\n }\r\n\r\n @if (selectedActionIndex() === $index) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"action.name || ''\"\r\n (change)=\"setActionName($index, $any($event.target).value)\"\r\n />\r\n @if (actionNameError(action); as error) {\r\n <p class=\"fb-field__error\">{{ error }}</p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n E\u2019 il nome con cui la citano i trigger, e vive nello spazio dei nomi del flow. Con\r\n l\u2019output automatico e\u2019 anche la radice del riferimento:\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: e\u2019 ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionTarget($index, $event ?? '')\"\r\n />\r\n @if (!action.actionName) {\r\n <p class=\"fb-field__error\">Obbligatoria: senza, ACTION_NAME_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action.storeOutputAutomatically === true\"\r\n (change)=\"setActionStoreOutput($index, $any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n L\u2019output automatico rende il risultato leggibile come\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>, ma\r\n <strong>non scrive in nessun campo</strong>: e\u2019 la forma da usare quando il risultato\r\n serve a una condizione, non a precompilare.\r\n </p>\r\n\r\n <!--\r\n L'unico posto in cui un campo di schermata e' una destinazione (\u00A75.2):\r\n `POST /flows/references/writable` continua a escluderlo, e un Assignment che ci\r\n scrive resta TARGET_NOT_WRITABLE.\r\n -->\r\n <fb-parameter-editor\r\n [holder]=\"action\"\r\n [catalogParameters]=\"actionParameterCatalog()\"\r\n [showOutputs]=\"action.storeOutputAutomatically !== true\"\r\n outputTitle=\"Campi da riempire\"\r\n [extraTargets]=\"screenFieldTargets()\"\r\n [outputsDisabledReason]=\"\r\n action.storeOutputAutomatically === true\r\n ? 'Con l\u2019output automatico il risultato non viene scritto nei campi.'\r\n : null\r\n \"\r\n (changed)=\"onActionParametersChanged($index, $event)\"\r\n />\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna action: i campi di questa schermata li compila solo l\u2019utente.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenAction()\">Aggiungi action</button>\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trigger</legend>\r\n <p class=\"fb-section__note\">\r\n Quale campo invoca quale action. Senza condizioni l\u2019action gira a ogni cambio del campo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (trigger of screenTriggers(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il trigger\"\r\n (click)=\"removeScreenTrigger($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action da invocare</label>\r\n <fb-name-picker\r\n [value]=\"trigger.screenActionName\"\r\n [options]=\"screenActionOptions()\"\r\n label=\"Action della schermata\"\r\n placeholder=\"Scegli un\u2019action\"\r\n unknownMessage=\"Questa schermata non dichiara un\u2019action con questo nome: e\u2019 SCREEN_ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Aggiungi prima un\u2019action qui sopra.\"\r\n (valueChange)=\"setTriggerProperty($index, 'screenActionName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo che la innesca</label>\r\n <!-- I campi di **questa** schermata, non i riferimenti in generale (\u00A75.2). -->\r\n <fb-name-picker\r\n [value]=\"trigger.triggerFieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo della schermata\"\r\n placeholder=\"Scegli un campo\"\r\n unknownMessage=\"Questo non e\u2019 un campo di questa schermata: e\u2019 SCREEN_TRIGGER_FIELD_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha ancora campi che raccolgono un valore.\"\r\n (valueChange)=\"setTriggerProperty($index, 'triggerFieldName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">All\u2019arrivo sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"trigger.initBehavior || ''\"\r\n (change)=\"setTriggerProperty($index, 'initBehavior', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 non invocare \u2014</option>\r\n <option value=\"runOnLoad\">Invoca al caricamento (runOnLoad)</option>\r\n </select>\r\n @if (triggerInitIsUnknown(trigger)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ trigger.initBehavior }}\u00BB non e\u2019 ammesso: l\u2019unico valore e\u2019 <code>runOnLoad</code>\r\n (SCREEN_TRIGGER_INIT_BEHAVIOR_UNKNOWN).\r\n </p>\r\n }\r\n @if (triggerFiresOnLoadByDefault(trigger)) {\r\n <p class=\"fb-field__hint\">\r\n Non serve: \u00AB{{ trigger.triggerFieldName }}\u00BB ha un valore di default, e un campo\r\n precompilato dal flow innesca l\u2019action gi\u00E0 all\u2019arrivo sulla schermata.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (triggerHasNoCause(trigger)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Senza un campo che la innesca e senza l\u2019invocazione al caricamento, questo trigger non\r\n scattera\u2019 mai (SCREEN_TRIGGER_WITHOUT_CAUSE).\r\n </p>\r\n }\r\n\r\n <fb-condition-editor\r\n [holder]=\"triggerConditions(trigger)\"\r\n title=\"Invoca l\u2019action quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'triggers[' + $index + ']'\"\r\n (changed)=\"onTriggerConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun trigger: le action dichiarate non verranno invocate.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenTrigger()\">Aggiungi trigger</button>\r\n </fieldset>\r\n } @else {\r\n <!-- ---------------------------------------------------- proprieta' del campo -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">{{ captionOf(selectedField()!) }}</h3>\r\n <div class=\"fb-scr__props-actions\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta su\" (click)=\"moveSelected(-1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta giu\u2019\" (click)=\"moveSelected(1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--sm\"\r\n title=\"Porta fuori dal contenitore\"\r\n [disabled]=\"!canOutdent()\"\r\n (click)=\"outdentSelected()\"\r\n >\r\n \u21E4\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Duplica\" (click)=\"duplicateSelected()\">\u29C9</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--sm\" title=\"Elimina\" (click)=\"removeSelected()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.fieldType || ''\"\r\n (change)=\"setFieldType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.fieldType) {\r\n <p class=\"fb-field__error\">Il tipo e\u2019 obbligatorio: senza, SCREEN_FIELD_TYPE_MISSING.</p>\r\n } @else if (isUnknownType(selectedField())) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo tipo non e\u2019 nel dizionario: l\u2019editor non sa quali campi ammetta e mostra solo i comuni.\r\n </p>\r\n } @else if (selectedType()?.description) {\r\n <p class=\"fb-field__hint\">{{ selectedType()?.description }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome</label>\r\n <!-- Sul `change` e non sull\u2019`input`: la rinomina riscrive i riferimenti nel documento,\r\n e farlo a ogni tasto significherebbe riscriverlo per ogni lettera. -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.name || ''\"\r\n (change)=\"setFieldName($any($event.target).value)\"\r\n />\r\n @if (selectedIsResource()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo e\u2019 una <strong>risorsa</strong>: lo referenzi come\r\n <code>{{ selectedField()!.name || 'Nome' }}</code> in condizioni, formule e parametri. \u00C8 di\r\n sola lettura per il flow \u2014 un Assignment che ci scrive e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (!selectedType()?.isContainer) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ selectedField()!.fieldType === 'DisplayText' ? 'Testo' : 'Etichetta' }}\r\n </label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n ></textarea>\r\n <p class=\"fb-field__hint\">\r\n Supporta i merge field <code>{!Riferimento}</code>.\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 su un componente l\u2019etichetta e\u2019 **ammessa** e viene consegnata al frontend\r\n in `label`, ma la sua assenza non e\u2019 un rilievo: un componente di solito disegna\r\n la propria intestazione. Nasconderla, come faceva prima l\u2019editor, toglieva un\r\n campo che il contratto prevede.\r\n -->\r\n Facoltativa: un componente di solito disegna la propria intestazione.\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.helpText || ''\"\r\n (input)=\"setFieldProperty('helpText', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Larghezza</label>\r\n <div class=\"fb-scr__width\">\r\n <input\r\n type=\"range\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n [value]=\"widthOf(selectedField()!)\"\r\n (input)=\"setNumberProperty('width', $any($event.target).value)\"\r\n />\r\n <span class=\"fb-scr__width-value\">{{ widthOf(selectedField()!) }}/12</span>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Colonne della griglia della schermata. \u00C8 un\u2019indicazione: il frontend puo\u2019 ignorarla.\r\n </p>\r\n </div>\r\n\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 perche\u2019 qui non c\u2019e\u2019 \u00ABobbligatorio\u00BB. La domanda arriva sempre, e la risposta non\r\n si indovina guardando l\u2019interfaccia: un componente non ha un valore proprio, ce l\u2019hanno\r\n i suoi output, che vanno in **variabili** scelte dall\u2019autore. \u00ABObbligatorio\u00BB dovrebbe\r\n dire *quale output* deve essere valorizzato, e il metadata non ha modo di dirlo \u2014\r\n dichiararlo qui sarebbe `SCREEN_FIELD_CONFIGURATION_INVALID` e il runtime lo\r\n ignorerebbe. Le due strade vere stanno nel contratto e sono queste.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Un componente non raccoglie un valore \u2014 lo fanno i suoi output \u2014 quindi obbligatorieta\u2019, valore\r\n predefinito e regola di validazione non si applicano: dichiararli e\u2019\r\n <code>SCREEN_FIELD_CONFIGURATION_INVALID</code> e il runtime li ignora. Per pretendere una\r\n compilazione: la validazione la fa il componente stesso, oppure si controlla la variabile di\r\n destinazione con una Decision dopo la schermata.\r\n </p>\r\n }\r\n\r\n <!-- ------------------------------------------------ campi che raccolgono un valore -->\r\n @if (selectedType()?.storesValue) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valore</legend>\r\n\r\n @if (selectedField()!.fieldType !== 'ObjectProvided') {\r\n <div class=\"fb-field\">\r\n <label\r\n class=\"fb-field__label\"\r\n [class.fb-field__label--required]=\"!!selectedType()?.requiresDataType\"\r\n >\r\n Tipo di dato\r\n </label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.dataType || ''\"\r\n (change)=\"setFieldProperty('dataType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.dataTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (selectedType()?.requiresDataType && !selectedField()!.dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio per questo tipo di campo (DATA_TYPE_MISSING).</p>\r\n }\r\n @if (selectedType()?.isCollection) {\r\n <p class=\"fb-field__hint\">\r\n Il valore raccolto e\u2019 una <strong>collection</strong>, non una stringa con i valori\r\n separati: nelle condizioni si usano gli operatori di collection.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\" [class.fb-field__label--required]=\"objectTypeIsStructure()\">\r\n {{\r\n objectTypeIsStructure()\r\n ? 'Classe'\r\n : selectedField()!.dataType === 'Enum'\r\n ? 'Tipo di enumerazione'\r\n : 'Oggetto'\r\n }}\r\n </label>\r\n @if (selectedField()!.dataType === 'Enum') {\r\n <!-- Dizionario chiuso: si sceglie, non si scrive. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.objectType || ''\"\r\n (change)=\"setFieldProperty('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza il tipo concreto l\u2019editor non puo\u2019 proporre i valori dell\u2019enumerazione.\r\n </p>\r\n }\r\n } @else if (objectTypeIsStructure()) {\r\n <fb-structure-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Classe\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, il runtime non ha nulla da istanziare\r\n (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo dell\u2019entita\u2019</label>\r\n <p class=\"fb-field__hint\">\r\n Tipo, label, obbligatorieta\u2019, scala e \u2014 se il campo e\u2019 una picklist \u2014 le opzioni arrivano\r\n dallo schema dati: qui non si ridichiarano.\r\n </p>\r\n <fb-object-picker\r\n [value]=\"providedObject() || undefined\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setProvidedObject($event)\"\r\n />\r\n <fb-field-picker\r\n [value]=\"providedField() || undefined\"\r\n [object]=\"providedObject() || undefined\"\r\n label=\"Campo\"\r\n (valueChange)=\"setProvidedField($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsRequired()\"\r\n (change)=\"setRequired($any($event.target).checked)\"\r\n />\r\n Obbligatorio\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsEditable()\"\r\n (change)=\"setEditable($any($event.target).checked)\"\r\n />\r\n Modificabile\r\n </label>\r\n @if (!selectedIsEditable()) {\r\n <p class=\"fb-field__hint\">\r\n A <code>false</code> il runtime <strong>ignora</strong> cio\u2019 che il client rimanda indietro:\r\n e\u2019 l\u2019unico modo di rendere un campo davvero di sola lettura.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore di default</label>\r\n <fb-value-editor\r\n [value]=\"selectedField()!.defaultValue\"\r\n [dataType]=\"selectedField()!.dataType\"\r\n [objectType]=\"selectedField()!.objectType\"\r\n [isCollection]=\"selectedType()?.isCollection\"\r\n label=\"Valore di default\"\r\n (valueChange)=\"setDefaultValue($event)\"\r\n />\r\n </div>\r\n\r\n @if (scaleApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.scale ?? ''\"\r\n (change)=\"setNumberProperty('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n } @else if (selectedField()!.scale !== undefined) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>scale</code> su un campo non numerico e\u2019 SCALE_NOT_APPLICABLE.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Lunghezza massima</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxLength ?? ''\"\r\n (change)=\"setNumberProperty('maxLength', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Verificata anche dal runtime (TOO_LONG).</p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tornando sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.inputsOnNextNavToAssocScrn || ''\"\r\n (change)=\"setFieldProperty('inputsOnNextNavToAssocScrn', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito (mantieni i valori)</option>\r\n @for (entry of dictionary.screenFieldInputsRevisited(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Regola di validazione</label>\r\n <!--\r\n `commitOn=\"change\"`: qui mutare il documento ridisegna l'anteprima, e riscriverla a\r\n ogni tasto costa. La verifica parte lo stesso mentre si digita (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"selectedField()!.validationRule?.formulaExpression || ''\"\r\n usage=\"ValidationRule\"\r\n expectedDataType=\"Boolean\"\r\n commitOn=\"change\"\r\n [rows]=\"2\"\r\n placeholder=\"Espressione, es. LEN(Nome) > 3\"\r\n ariaLabel=\"Regola di validazione\"\r\n (expressionChange)=\"setValidationRule('formulaExpression', $event)\"\r\n />\r\n <input\r\n class=\"fb-input\"\r\n placeholder=\"Messaggio mostrato quando l\u2019espressione e\u2019 falsa\"\r\n [value]=\"selectedField()!.validationRule?.errorMessage || ''\"\r\n (change)=\"setValidationRule('errorMessage', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione la valuta il motore di regole: il backend non la interpreta, la fa\r\n verificare al motore.\r\n </p>\r\n </div>\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------------------- choice -->\r\n @if (selectedType()?.acceptsChoices) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Opzioni</legend>\r\n <p class=\"fb-section__note\">\r\n Accetta <strong>Choice</strong> e <strong>Dynamic choice set</strong>, nell\u2019ordine in cui le\r\n opzioni compaiono. Puntare a una variabile e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\r\n </p>\r\n\r\n @if (!selectedField()!.choiceReferences?.length) {\r\n @if (selectedField()!.fieldType === 'ObjectProvided') {\r\n <!-- Le opzioni di una picklist arrivano dallo schema: qui si aggiungono solo se\r\n si vuole sostituirle, e non dichiararne nessuna e' il caso normale. -->\r\n <p class=\"fb-field__hint\">\r\n Se il campo dello schema e\u2019 una picklist, le opzioni arrivano da l\u00EC: dichiararle qui\r\n serve solo a sostituirle.\r\n </p>\r\n } @else {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessuna opzione: il campo non ha niente da mostrare (SCREEN_FIELD_CHOICES_MISSING).\r\n </p>\r\n }\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__title\">{{ choice }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, -1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, 1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--sm\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeChoice($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggiungi un\u2019opzione</label>\r\n <!--\r\n `commitOn=\"enter\"`: qui ogni emissione e\u2019 una riga in piu\u2019, non un campo che cambia\r\n valore \u2014 a tasto, scrivere \u00ABAlta\u00BB aggiungeva \u00ABA\u00BB, \u00ABAl\u00BB, \u00ABAlt\u00BB e \u00ABAlta\u00BB.\r\n -->\r\n <fb-name-picker\r\n [options]=\"choiceOptions()\"\r\n label=\"Choice\"\r\n commitOn=\"enter\"\r\n placeholder=\"Scegli una choice o un choice set\"\r\n unknownMessage=\"Questo nome non e\u2019 una choice ne\u2019 un choice set: e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Il flow non dichiara nessuna choice: creale nel pannello delle risorse.\"\r\n (valueChange)=\"addChoice($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Scegli dall\u2019elenco, oppure scrivi il nome e premi Invio.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Opzione selezionata all\u2019apertura</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.defaultSelectedChoiceReference || ''\"\r\n (change)=\"setFieldProperty('defaultSelectedChoiceReference', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 nessuna \u2014</option>\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <option [value]=\"choice\">{{ choice }}</option>\r\n }\r\n </select>\r\n @if (defaultChoiceIsForeign()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n \u00AB{{ selectedField()!.defaultSelectedChoiceReference }}\u00BB non e\u2019 fra le opzioni elencate qui\r\n sopra.\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedAutoSelectsSingleChoice()\"\r\n (change)=\"setAutoSelectSingleChoice($any($event.target).checked)\"\r\n />\r\n Scegli da sola l\u2019unica opzione\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Quando le opzioni risolte sono <strong>esattamente una</strong>, il campo la prende da se\u2019.\r\n Serve alle tendine che dipendono da un\u2019altra tendina \u2014 i CAP di un comune \u2014 dove chiedere\r\n di scegliere l\u2019unica voce non decide niente.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- contenitori -->\r\n @if (selectedType()?.isContainer) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Contenitore</legend>\r\n @if (selectedField()!.fieldType === 'RegionContainer') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di sezione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.regionContainerType || ''\"\r\n (change)=\"setFieldProperty('regionContainerType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (entry of dictionary.regionContainerTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il contenitore e\u2019 vuoto: non produce niente sulla schermata (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------- ComponentInstance -->\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Componente</legend>\r\n <p class=\"fb-section__note\">\r\n Componenti e form sono la stessa domanda al frontend \u2014 cosa sa rendere, e con quali parametri\r\n \u2014 e passano dallo stesso catalogo. Un <code>ComponentInstance</code> non ha un valore proprio:\r\n lo hanno i suoi output.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Componente</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.extensionName\"\r\n [options]=\"componentOptions()\"\r\n [unusableOptions]=\"unusableComponents()\"\r\n label=\"Componente\"\r\n placeholder=\"Scrivi o scegli un componente\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo componente non esiste nel catalogo: e\u2019 SCREEN_COMPONENT_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 una schermata intera, non un componente montabile qui (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei componenti non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFieldProperty('extensionName', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.extensionName) {\r\n <p class=\"fb-field__error\">Obbligatorio: senza, SCREEN_COMPONENT_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Rendi gli output referenziabili automaticamente\r\n </label>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n Gli output si referenziano come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, senza dichiarare variabili.\r\n </p>\r\n }\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Output automatici <strong>e</strong> parametri di uscita insieme:\r\n OUTPUT_CONFIGURATION_CONFLICT.\r\n </p>\r\n }\r\n\r\n <fb-parameter-editor\r\n [holder]=\"$any(selectedField())\"\r\n [catalogParameters]=\"componentParameterList()\"\r\n inputTitle=\"Valori passati al componente\"\r\n outputTitle=\"Valori raccolti dal componente\"\r\n [showOutputs]=\"!selectedField()!.storeOutputAutomatically\"\r\n outputsDisabledReason=\"Gli output sono automatici: disattivalo per assegnarli a variabili.\"\r\n (changed)=\"onComponentParametersChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- visibilita' -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Visibilita\u2019</legend>\r\n <p class=\"fb-section__note\">\r\n Le regole si rivalutano <strong>sui valori appena inviati</strong>: e\u2019 cos\u00EC che un campo compare\r\n in funzione di un altro campo della stessa schermata. Un campo risultato nascosto viene\r\n <strong>azzerato</strong> e non viene validato.\r\n </p>\r\n <fb-condition-editor\r\n [holder]=\"visibilityRule()\"\r\n title=\"Mostra il campo quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'fields[' + (selectedField()!.name || '') + '].visibilityRule'\"\r\n (changed)=\"onVisibilityChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<!--\r\n ============================================================== l'anteprima\r\n Due template ricorsivi. `listTpl` disegna i figli di un contenitore, e nient'altro: `data-item`\r\n marca cio' che conta come fratello nel calcolo del punto di rilascio, e il segnaposto del\r\n contenitore vuoto ne resta fuori. La barra di inserimento non e' qui \u2014 sta sopra l'anteprima,\r\n vedi il velo nel frame.\r\n-->\r\n<ng-template #listTpl let-fields let-parent=\"parent\" let-axis=\"axis\">\r\n @for (child of fields; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"fieldTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: child, path: childPath(parent, $index), axis: axis }\"\r\n />\r\n }\r\n @if (!fields.length) {\r\n <p class=\"fb-scr__drop-hint\">Trascina qui un componente</p>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #fieldTpl let-field let-path=\"path\" let-axis=\"axis\">\r\n <div\r\n class=\"fb-scr__item\"\r\n data-item=\"\"\r\n cdkDrag\r\n [style.grid-column]=\"'span ' + widthOf(field)\"\r\n [class.fb-scr__item--selected]=\"isSelected(path)\"\r\n [class.fb-scr__item--dragging]=\"isDragging(path)\"\r\n [class.fb-scr__item--container]=\"isContainer(field)\"\r\n (cdkDragStarted)=\"onFieldDragStarted(path)\"\r\n (cdkDragMoved)=\"onFieldDragMoved($event)\"\r\n (cdkDragEnded)=\"onFieldDragEnded($event, path)\"\r\n (click)=\"select(path); $event.stopPropagation()\"\r\n >\r\n <span class=\"fb-scr__grip\" cdkDragHandle title=\"Trascina per spostare\" aria-hidden=\"true\">\u283F</span>\r\n @if (field.visibilityRule?.conditions?.length) {\r\n <span class=\"fb-scr__flag\" title=\"Ha una regola di visibilita\u2019\">\u25D0</span>\r\n }\r\n\r\n @switch (field.fieldType) {\r\n @case ('RegionContainer') {\r\n <div class=\"fb-scr__section\">\r\n @if (field.regionContainerType !== 'SectionWithoutHeader') {\r\n <header class=\"fb-scr__section-head\">{{ field.fieldText || field.name }}</header>\r\n }\r\n <div class=\"fb-scr__cols\" [attr.data-drop]=\"dropId(path)\" data-axis=\"row\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'row' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('Region') {\r\n <div class=\"fb-scr__region\">\r\n <span class=\"fb-scr__region-tag\">{{ field.name }} \u00B7 {{ widthOf(field) }}/12</span>\r\n <div class=\"fb-scr__region-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('DisplayText') {\r\n <p class=\"fb-scr__display\">{{ field.fieldText || '(testo vuoto)' }}</p>\r\n }\r\n @case ('LargeTextArea') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--area\">{{ placeholderOf(field) }}</div>\r\n }\r\n @case ('PasswordField') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022</div>\r\n }\r\n @case ('DropdownBox') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ field.defaultSelectedChoiceReference || choiceLabelsOf(field)[0] || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u25BE</span>\r\n </div>\r\n }\r\n @case ('MultiSelectPicklist') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ choiceLabelsOf(field).join(', ') || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u2261</span>\r\n </div>\r\n }\r\n @case ('RadioButtons') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u25EF {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('MultiSelectCheckboxes') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u2610 {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('ComponentInstance') {\r\n <!--\r\n L\u2019etichetta si disegna se c\u2019e\u2019: il runtime la consegna in `label` come per gli altri\r\n campi (\u00A75.2). Nessun asterisco: un componente non raccoglie un valore, quindi\r\n \u00ABobbligatorio\u00BB non ha niente su cui applicarsi.\r\n -->\r\n @if (field.fieldText) {\r\n <label class=\"fb-scr__label\">{{ field.fieldText }}</label>\r\n }\r\n <div class=\"fb-scr__component\">\r\n <span aria-hidden=\"true\">\u2B21</span>\r\n {{ field.extensionName || 'Componente non indicato' }}\r\n </div>\r\n }\r\n @case ('ObjectProvided') {\r\n <label class=\"fb-scr__label\">\r\n {{ field.fieldText || field.objectFieldReference || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n <span class=\"fb-scr__tag\">{{ field.objectFieldReference || 'campo non indicato' }}</span>\r\n }\r\n @default {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n @if (isUnknownType(field)) {\r\n <span class=\"fb-scr__tag fb-scr__tag--warn\">{{ field.fieldType }}: tipo non nel dizionario</span>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".fb-scr{display:grid;grid-template-columns:220px minmax(0,1fr) 340px;gap:12px;align-items:start;margin-bottom:14px}@media(max-width:1100px){.fb-scr{grid-template-columns:minmax(0,1fr)}}.fb-scr__side,.fb-scr__props{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__tabs{display:flex;gap:4px;margin-bottom:8px}.fb-scr__tab{flex:1;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;font-weight:600;cursor:pointer}.fb-scr__tab--active{border-color:var(--fb-accent, #4f6ef7);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent);color:var(--fb-accent-strong, #3d59e0)}.fb-scr__side-hint,.fb-scr__side-empty{margin:6px 0;font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__palette{display:flex;flex-direction:column;gap:4px;margin:8px 0 0;padding:0;max-height:320px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-scr__chip{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-scr__chip:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-scr__chip-icon{flex:none;width:20px;text-align:center;color:var(--fb-text-muted, #6b7086)}.fb-scr__chip-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-scr__chip-label small{margin-left:4px;color:var(--fb-text-subtle, #98a2b3);font-size:10px}.fb-scr__canvas{min-width:0;padding:14px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-canvas-bg, #f4f5f7);max-height:60vh;overflow-y:auto}.fb-scr__frame{position:relative;display:flex;flex-direction:column;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));overflow:hidden}.fb-scr__frame-head{display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:13px;font-weight:600}.fb-scr__help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px}.fb-scr__body,.fb-scr__region-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:12px;min-height:90px}.fb-scr__region-body{padding:8px;min-height:60px}.fb-scr__cols{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-items:stretch;gap:0;padding:8px;min-height:60px}.fb-scr__cols>.fb-scr__item{display:flex;align-self:stretch}.fb-scr__cols>.fb-scr__item>.fb-scr__region{flex:1;min-width:0}.fb-scr__drop-hint{grid-column:1 / -1;margin:0;padding:10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);color:var(--fb-text-subtle, #98a2b3);font-size:11px;text-align:center}.fb-scr__marker{position:absolute;z-index:2;border-radius:2px;background:var(--fb-accent, #4f6ef7);pointer-events:none}.fb-scr__item{position:relative;box-sizing:border-box;padding:6px 8px;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);cursor:pointer}.fb-scr__item:hover{border-color:var(--fb-border-strong, #cfd4de);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 4%,transparent)}.fb-scr__item--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:inset 0 0 0 1px var(--fb-accent, #4f6ef7)}.fb-scr__item--dragging{opacity:.45;pointer-events:none}.fb-scr__grip{position:absolute;top:2px;left:-2px;padding:0 3px;color:var(--fb-text-subtle, #98a2b3);font-size:11px;line-height:1;opacity:0;cursor:grab}.fb-scr__item:hover>.fb-scr__grip,.fb-scr__item--selected>.fb-scr__grip{opacity:1}.fb-scr__flag{position:absolute;top:2px;right:4px;color:var(--fb-text-muted, #6b7086);font-size:11px}.fb-scr__label{display:block;margin-bottom:3px;font-size:11px;font-weight:600;color:var(--fb-text, #1a1c23)}.fb-scr__req{color:var(--fb-error, #dc2626)}.fb-scr__control{display:flex;align-items:center;justify-content:space-between;gap:6px;min-height:26px;padding:4px 8px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-subtle, #98a2b3);font-size:12px;pointer-events:none}.fb-scr__control--area{min-height:54px;align-items:flex-start}.fb-scr__display{margin:0;font-size:12px;color:var(--fb-text, #1a1c23)}.fb-scr__options{display:flex;flex-direction:column;gap:2px;pointer-events:none}.fb-scr__option{font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__option--missing{color:var(--fb-warning, #b7791f)}.fb-scr__component{display:flex;align-items:center;gap:6px;padding:10px;border:1px dashed var(--fb-accent, #4f6ef7);border-radius:var(--fb-radius-xs, 6px);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 5%,transparent);font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__tag{display:inline-block;margin-top:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__tag--warn{color:var(--fb-warning, #b7791f)}.fb-scr__section{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__section-head{padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__region{border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__region-tag{display:block;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__frame-foot{display:flex;align-items:center;gap:6px;padding:8px 12px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__spacer{flex:1}.fb-scr__btn{padding:3px 10px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__btn--primary{border-color:var(--fb-accent, #4f6ef7);background:var(--fb-accent, #4f6ef7);color:var(--fb-accent-contrast, #fff)}.fb-scr__canvas-note{margin-top:10px}.fb-scr__props{background:var(--fb-surface, #fff)}.fb-scr__props-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.fb-scr__props-title{flex:1;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__props-actions{display:flex;flex-wrap:wrap;gap:3px}.fb-btn--sm{padding:3px 7px;font-size:11px}.fb-scr__width{display:flex;align-items:center;gap:8px}.fb-scr__width input[type=range]{flex:1;min-width:0}.fb-scr__width-value{font-size:11px;color:var(--fb-text-muted, #6b7086)}\n"] }]
|
|
12912
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-scr\" #composer>\r\n <!-- ============================================================ sinistra -->\r\n <aside class=\"fb-scr__side\" aria-label=\"Componenti e campi\">\r\n <div class=\"fb-scr__tabs\" role=\"tablist\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'components'\"\r\n [attr.aria-selected]=\"paletteTab() === 'components'\"\r\n (click)=\"setPaletteTab('components')\"\r\n >\r\n Componenti\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"fb-scr__tab\"\r\n [class.fb-scr__tab--active]=\"paletteTab() === 'fields'\"\r\n [attr.aria-selected]=\"paletteTab() === 'fields'\"\r\n (click)=\"setPaletteTab('fields')\"\r\n >\r\n Campi\r\n </button>\r\n </div>\r\n\r\n @if (paletteTab() === 'components') {\r\n @if (!dictionary.screenFieldTypes().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il dizionario <code>screenFieldTypes</code> non e\u2019 disponibile: senza i suoi flag l\u2019editor non\r\n sa quali campi ammette ciascun tipo, e non li inventa.\r\n </p>\r\n }\r\n <p class=\"fb-scr__side-hint\">Trascina sulla schermata, oppure clicca per aggiungere.</p>\r\n <ul class=\"fb-scr__palette\">\r\n @for (entry of dictionary.screenFieldTypes(); track entry.value) {\r\n <li>\r\n <!--\r\n \u00A75.2 \u2014 l'unico chip che si spegne: una sezione ripetibile dentro un'altra. Non si\r\n annidano, e il cursore e' una variabile sola per sezione.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [disabled]=\"!canAddAtSelection(entry.value)\"\r\n [title]=\"\r\n canAddAtSelection(entry.value)\r\n ? entry.description || entry.label\r\n : 'Le sezioni ripetibili non si annidano: seleziona un punto fuori dalla riga.'\r\n \"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onComponentDragEnded($event, entry.value)\"\r\n (click)=\"addComponentAtSelection(entry.value)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">{{ iconOf(entry.value) }}</span>\r\n <span class=\"fb-scr__chip-label\">{{ entry.label }}</span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p class=\"fb-scr__side-hint\">\r\n Un campo dell\u2019entita\u2019 porta con se\u2019 tipo, etichetta, obbligatorieta\u2019 e opzioni: arrivano dallo\r\n schema dati, qui non si ridichiarano.\r\n </p>\r\n <!-- Il catalogo non e' un dizionario chiuso: si sceglie o si scrive, come ovunque si\r\n indichi un'entita' (vedi `object-picker`). -->\r\n <fb-object-picker\r\n [value]=\"fieldsObject()\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Scrivi o scegli un\u2019entita\u2019\"\r\n (valueChange)=\"setFieldsObject($event)\"\r\n />\r\n @if (fieldsObject() && !fieldsOfObject().length) {\r\n <p class=\"fb-scr__side-empty\">\r\n Nessun campo dichiarato per questa entita\u2019: il catalogo non li espone, non significa che non\r\n ci siano.\r\n </p>\r\n }\r\n <ul class=\"fb-scr__palette\">\r\n @for (field of fieldsOfObject(); track $index) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"fb-scr__chip\"\r\n cdkDrag\r\n [title]=\"field.name + (field.dataType ? ' \u00B7 ' + field.dataType : '')\"\r\n (cdkDragMoved)=\"onPaletteDragMoved($event)\"\r\n (cdkDragEnded)=\"onObjectFieldDragEnded($event, field)\"\r\n (click)=\"addObjectFieldAtSelection(field)\"\r\n >\r\n <span class=\"fb-scr__chip-icon\" aria-hidden=\"true\">\u2317</span>\r\n <span class=\"fb-scr__chip-label\">\r\n {{ field.label || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n <small>{{ field.dataType }}</small>\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </aside>\r\n\r\n <!-- ============================================================== centro -->\r\n <!-- Cliccare fuori da un campo riporta a destra le proprieta' della schermata. -->\r\n <section class=\"fb-scr__canvas\" aria-label=\"Anteprima della schermata\" (click)=\"selectScreen()\">\r\n <div class=\"fb-scr__frame\">\r\n @if (showHeader()) {\r\n <header class=\"fb-scr__frame-head\">\r\n {{ $any(node()).label || name() }}\r\n @if (screen().helpText) {\r\n <span class=\"fb-scr__help\" [title]=\"screen().helpText || ''\">?</span>\r\n }\r\n </header>\r\n }\r\n\r\n <div\r\n class=\"fb-scr__body\"\r\n [attr.data-drop]=\"''\"\r\n data-axis=\"column\"\r\n [class.fb-scr__body--empty]=\"isEmpty()\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: screen().fields || [], parent: [], axis: 'column' }\"\r\n />\r\n </div>\r\n\r\n <!--\r\n La barra di inserimento e' un **velo** sopra l'anteprima, non un figlio del contenitore di\r\n rilascio: dentro la griglia occupava una riga intera e spostava in basso proprio la sezione\r\n che si stava puntando, che allora usciva da sotto il puntatore e il bersaglio oscillava.\r\n -->\r\n @if (dropMarker(); as bar) {\r\n <div\r\n class=\"fb-scr__marker\"\r\n [style.left.px]=\"bar.left\"\r\n [style.top.px]=\"bar.top\"\r\n [style.width.px]=\"bar.width\"\r\n [style.height.px]=\"bar.height\"\r\n ></div>\r\n }\r\n\r\n @if (showFooter()) {\r\n <footer class=\"fb-scr__frame-foot\">\r\n @if (allowPause()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().pauseButtonLabel || 'Pausa' }}</span>\r\n }\r\n <span class=\"fb-scr__spacer\"></span>\r\n @if (allowBack()) {\r\n <span class=\"fb-scr__btn fb-scr__btn--ghost\">{{ screen().backButtonLabel || 'Indietro' }}</span>\r\n }\r\n <span class=\"fb-scr__btn fb-scr__btn--primary\">\r\n {{ screen().nextOrFinishButtonLabel || (allowFinish() ? 'Fine' : 'Avanti') }}\r\n </span>\r\n </footer>\r\n }\r\n </div>\r\n\r\n @if (isEmpty()) {\r\n <p class=\"fb-callout fb-callout--warn fb-scr__canvas-note\">\r\n La schermata non ha campi: non c\u2019e\u2019 niente da mostrare all\u2019utente (SCREEN_WITHOUT_FIELDS).\r\n </p>\r\n }\r\n </section>\r\n\r\n <!-- ============================================================== destra -->\r\n <aside class=\"fb-scr__props\" aria-label=\"Proprieta\u2019\">\r\n @if (!selectedField()) {\r\n <!-- ------------------------------------------------ proprieta' della schermata -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">Schermata</h3>\r\n </header>\r\n <p class=\"fb-scr__side-hint\">Seleziona un campo nell\u2019anteprima per configurarlo.</p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setScreenText('helpText', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta della\r\n schermata.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setScreenFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setScreenFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setScreenFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showHeader()\" (change)=\"setScreenFlag('showHeader', $any($event.target).checked)\" />\r\n Mostra l\u2019intestazione\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"showFooter()\" (change)=\"setScreenFlag('showFooter', $any($event.target).checked)\" />\r\n Mostra il piede\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setScreenText('pausedText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questa schermata non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e\r\n l\u2019utente resterebbe bloccato (SCREEN_DEAD_END).\r\n </p>\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pulsanti</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABindietro\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().backButtonLabel || ''\"\r\n (input)=\"setScreenText('backButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABavanti / fine\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().nextOrFinishButtonLabel || ''\"\r\n (input)=\"setScreenText('nextOrFinishButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta \u00ABpausa\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pauseButtonLabel || ''\"\r\n (input)=\"setScreenText('pauseButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Stage mostrato nell\u2019avanzamento</label>\r\n <fb-name-picker\r\n [value]=\"screen().stageReference\"\r\n [options]=\"stageOptions()\"\r\n label=\"Stage\"\r\n placeholder=\"Scegli uno stage\"\r\n unknownMessage=\"Questo stage non e\u2019 dichiarato dal flow.\"\r\n emptyMessage=\"Il flow non dichiara stage: creali nel pannello delle risorse.\"\r\n (valueChange)=\"setScreenText('stageReference', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <!--\r\n Screen action (\u00A75.2). Due elenchi e non uno perche' il contratto li separa: piu' trigger\r\n possono invocare la stessa action, quindi \u00ABchi la chiama\u00BB non e' una sua proprieta'. Le\r\n action si aprono una alla volta: i cataloghi dipendono da tipo e nome, e tenerne N in volo\r\n sarebbe N volte la corsa fra risposte.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Azioni della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Un campo che l\u2019utente compila puo\u2019 innescare un\u2019action i cui risultati finiscono negli\r\n <strong>altri campi di questa schermata</strong>: e\u2019 il caso \u00ABscrivi il codice fiscale e nome\r\n e cognome compaiono da soli\u00BB. L\u2019alternativa sarebbe spezzare la schermata in due con un\r\n elemento Action in mezzo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of screenActions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [attr.aria-expanded]=\"selectedActionIndex() === $index\"\r\n (click)=\"selectAction(selectedActionIndex() === $index ? null : $index)\"\r\n >\r\n {{ selectedActionIndex() === $index ? '\u25BE' : '\u25B8' }}\r\n {{ action.name || '(senza nome)' }}\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <span class=\"fb-scr__side-hint\">{{ action.actionName || 'action non scelta' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019action\"\r\n (click)=\"removeScreenAction($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (actionLosesResult(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questa action viene invocata ma il suo risultato si perde: aggiungi un parametro di\r\n uscita, oppure accendi l\u2019output automatico (SCREEN_ACTION_WITHOUT_OUTPUTS).\r\n </p>\r\n }\r\n @if (actionHasNoTrigger(action)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun trigger la invoca: senza, non girera\u2019 mai.\r\n </p>\r\n }\r\n\r\n @if (selectedActionIndex() === $index) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"action.name || ''\"\r\n (change)=\"setActionName($index, $any($event.target).value)\"\r\n />\r\n @if (actionNameError(action); as error) {\r\n <p class=\"fb-field__error\">{{ error }}</p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n E\u2019 il nome con cui la citano i trigger, e vive nello spazio dei nomi del flow. Con\r\n l\u2019output automatico e\u2019 anche la radice del riferimento:\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionType\"\r\n [options]=\"actionTypeOptions()\"\r\n label=\"Tipo di action\"\r\n placeholder=\"Scrivi o scegli un tipo\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo tipo di action non e\u2019 fra quelli dichiarati dal sistema ospite.\"\r\n emptyMessage=\"Catalogo dei tipi di action non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setActionType($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action</label>\r\n <fb-name-picker\r\n [value]=\"action.actionName\"\r\n [options]=\"actionOptions()\"\r\n label=\"Action\"\r\n placeholder=\"Scrivi o scegli un\u2019action\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questa action non esiste nel catalogo del tipo scelto: e\u2019 ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Scegli prima il tipo di action, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setActionTarget($index, $event ?? '')\"\r\n />\r\n @if (!action.actionName) {\r\n <p class=\"fb-field__error\">Obbligatoria: senza, ACTION_NAME_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"action.storeOutputAutomatically === true\"\r\n (change)=\"setActionStoreOutput($index, $any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n L\u2019output automatico rende il risultato leggibile come\r\n <code>{{ action.name || 'Cerca' }}.NomeOutput</code>, ma\r\n <strong>non scrive in nessun campo</strong>: e\u2019 la forma da usare quando il risultato\r\n serve a una condizione, non a precompilare.\r\n </p>\r\n\r\n <!--\r\n L'unico posto in cui un campo di schermata e' una destinazione (\u00A75.2):\r\n `POST /flows/references/writable` continua a escluderlo, e un Assignment che ci\r\n scrive resta TARGET_NOT_WRITABLE.\r\n -->\r\n <fb-parameter-editor\r\n [holder]=\"action\"\r\n [catalogParameters]=\"actionParameterCatalog()\"\r\n [showOutputs]=\"action.storeOutputAutomatically !== true\"\r\n outputTitle=\"Campi da riempire\"\r\n [extraTargets]=\"screenFieldTargets()\"\r\n [outputsDisabledReason]=\"\r\n action.storeOutputAutomatically === true\r\n ? 'Con l\u2019output automatico il risultato non viene scritto nei campi.'\r\n : null\r\n \"\r\n (changed)=\"onActionParametersChanged($index, $event)\"\r\n />\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna action: i campi di questa schermata li compila solo l\u2019utente.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenAction()\">Aggiungi action</button>\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trigger</legend>\r\n <p class=\"fb-section__note\">\r\n Quale campo invoca quale action. Senza condizioni l\u2019action gira a ogni cambio del campo.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (trigger of screenTriggers(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il trigger\"\r\n (click)=\"removeScreenTrigger($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Action da invocare</label>\r\n <fb-name-picker\r\n [value]=\"trigger.screenActionName\"\r\n [options]=\"screenActionOptions()\"\r\n label=\"Action della schermata\"\r\n placeholder=\"Scegli un\u2019action\"\r\n unknownMessage=\"Questa schermata non dichiara un\u2019action con questo nome: e\u2019 SCREEN_ACTION_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Aggiungi prima un\u2019action qui sopra.\"\r\n (valueChange)=\"setTriggerProperty($index, 'screenActionName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo che la innesca</label>\r\n <!-- I campi di **questa** schermata, non i riferimenti in generale (\u00A75.2). -->\r\n <fb-name-picker\r\n [value]=\"trigger.triggerFieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo della schermata\"\r\n placeholder=\"Scegli un campo\"\r\n unknownMessage=\"Questo non e\u2019 un campo di questa schermata: e\u2019 SCREEN_TRIGGER_FIELD_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha ancora campi che raccolgono un valore.\"\r\n (valueChange)=\"setTriggerProperty($index, 'triggerFieldName', $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">All\u2019arrivo sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"trigger.initBehavior || ''\"\r\n (change)=\"setTriggerProperty($index, 'initBehavior', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 non invocare \u2014</option>\r\n <option value=\"runOnLoad\">Invoca al caricamento (runOnLoad)</option>\r\n </select>\r\n @if (triggerInitIsUnknown(trigger)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ trigger.initBehavior }}\u00BB non e\u2019 ammesso: l\u2019unico valore e\u2019 <code>runOnLoad</code>\r\n (SCREEN_TRIGGER_INIT_BEHAVIOR_UNKNOWN).\r\n </p>\r\n }\r\n @if (triggerFiresOnLoadByDefault(trigger)) {\r\n <p class=\"fb-field__hint\">\r\n Non serve: \u00AB{{ trigger.triggerFieldName }}\u00BB ha un valore di default, e un campo\r\n precompilato dal flow innesca l\u2019action gi\u00E0 all\u2019arrivo sulla schermata.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (triggerHasNoCause(trigger)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Senza un campo che la innesca e senza l\u2019invocazione al caricamento, questo trigger non\r\n scattera\u2019 mai (SCREEN_TRIGGER_WITHOUT_CAUSE).\r\n </p>\r\n }\r\n\r\n <fb-condition-editor\r\n [holder]=\"triggerConditions(trigger)\"\r\n title=\"Invoca l\u2019action quando\"\r\n [allowFormula]=\"false\"\r\n [issuePath]=\"'triggers[' + $index + ']'\"\r\n (changed)=\"onTriggerConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun trigger: le action dichiarate non verranno invocate.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenTrigger()\">Aggiungi trigger</button>\r\n </fieldset>\r\n } @else {\r\n <!-- ---------------------------------------------------- proprieta' del campo -->\r\n <header class=\"fb-scr__props-head\">\r\n <h3 class=\"fb-scr__props-title\">{{ captionOf(selectedField()!) }}</h3>\r\n <div class=\"fb-scr__props-actions\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta su\" (click)=\"moveSelected(-1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Sposta giu\u2019\" (click)=\"moveSelected(1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--sm\"\r\n title=\"Porta fuori dal contenitore\"\r\n [disabled]=\"!canOutdent()\"\r\n (click)=\"outdentSelected()\"\r\n >\r\n \u21E4\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" title=\"Duplica\" (click)=\"duplicateSelected()\">\u29C9</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--sm\" title=\"Elimina\" (click)=\"removeSelected()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.fieldType || ''\"\r\n (change)=\"setFieldType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n <!--\r\n \u00A75.2 \u2014 dentro una riga la sezione ripetibile non c'e': **non si annidano**, perche' il\r\n cursore e' una variabile sola per sezione. La validazione lo rifiuterebbe comunque, ma\r\n lasciar comporre e poi accusare fa perdere tempo due volte.\r\n -->\r\n @for (entry of fieldTypeOptions(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo sta dentro la riga di <code>{{ selectedRepeater()!.name }}</code>: esiste una\r\n volta per ogni elemento della collection, quindi\r\n <strong>non e\u2019 una risorsa del flow</strong> e non si nomina per nome. Dentro una riga si\r\n nomina il cursore \u2014\r\n <code>{{ rowCursor() || 'itemReference' }}.<membro></code> \u2014 e scrivere\r\n <code>{{ selectedField()!.name }}</code> in una condizione, in una formula o in un parametro\r\n e\u2019 <code>SCREEN_FIELD_IN_REPEATER</code>.\r\n </p>\r\n }\r\n @if (!selectedField()!.fieldType) {\r\n <p class=\"fb-field__error\">Il tipo e\u2019 obbligatorio: senza, SCREEN_FIELD_TYPE_MISSING.</p>\r\n } @else if (isUnknownType(selectedField())) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo tipo non e\u2019 nel dizionario: l\u2019editor non sa quali campi ammetta e mostra solo i comuni.\r\n </p>\r\n } @else if (selectedType()?.description) {\r\n <p class=\"fb-field__hint\">{{ selectedType()?.description }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome</label>\r\n <!-- Sul `change` e non sull\u2019`input`: la rinomina riscrive i riferimenti nel documento,\r\n e farlo a ogni tasto significherebbe riscriverlo per ogni lettera. -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.name || ''\"\r\n (change)=\"setFieldName($any($event.target).value)\"\r\n />\r\n @if (selectedIsResource() && !isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Questo campo e\u2019 una <strong>risorsa</strong>: lo referenzi come\r\n <code>{{ selectedField()!.name || 'Nome' }}</code> in condizioni, formule e parametri. \u00C8 di\r\n sola lettura per il flow \u2014 un Assignment che ci scrive e\u2019 TARGET_NOT_WRITABLE.\r\n </p>\r\n } @else if (isInsideRepeater()) {\r\n <p class=\"fb-field__hint\">\r\n Il nome resta comunque <strong>unico</strong>: e\u2019 l\u2019indirizzo con cui il frontend rende il\r\n campo, e un omonimo di una variabile e\u2019 <code>NAME_DUPLICATED</code> come sempre.\r\n </p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 `memberName` e il riferimento con cui gli altri campi lo nominano sono la **stessa\r\n domanda posta due volte**: `memberName: \"PodId\"` e `PuntoCorrente.PodId` sono lo stesso\r\n membro, e la classe da cui vengono e' l'`objectType` della variabile in `collectionReference`.\r\n -->\r\n @if (isInsideRepeater() && selectedType()?.storesValue) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Membro della riga</label>\r\n @if (rowClass()) {\r\n <fb-structure-member-picker\r\n [value]=\"selectedField()!.memberName\"\r\n [className]=\"rowClass()\"\r\n usage=\"writable\"\r\n label=\"Membro della riga\"\r\n (valueChange)=\"setFieldProperty('memberName', $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"selectedField()!.memberName || ''\"\r\n (change)=\"setFieldProperty('memberName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La classe delle righe non e\u2019 nota: la sezione non dichiara una <code>collectionReference</code>\r\n valida, quindi i membri non si possono proporre.\r\n </p>\r\n }\r\n <p class=\"fb-field__hint\">\r\n Il membro della riga in cui finisce il valore. Senza, vale il nome del campo. Dentro una\r\n riga lo si legge come\r\n <code>{{ rowCursor() || 'itemReference' }}.{{ selectedField()!.memberName || selectedField()!.name }}</code>.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il ramo dei contenitori vale anche per una sezione ripetibile: l\u00EC `fieldText` e'\r\n l'intestazione del blocco, non l'etichetta di un valore \u2014 l'etichetta della **riga** e'\r\n `itemLabel`, ed e' un campo suo, piu' sotto (\u00A75.2).\r\n -->\r\n @if (!holdsFields(selectedField())) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ selectedField()!.fieldType === 'DisplayText' ? 'Testo' : 'Etichetta' }}\r\n </label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n ></textarea>\r\n <p class=\"fb-field__hint\">\r\n Supporta i merge field <code>{!Riferimento}</code>.\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 su un componente l\u2019etichetta e\u2019 **ammessa** e viene consegnata al frontend\r\n in `label`, ma la sua assenza non e\u2019 un rilievo: un componente di solito disegna\r\n la propria intestazione. Nasconderla, come faceva prima l\u2019editor, toglieva un\r\n campo che il contratto prevede.\r\n -->\r\n Facoltativa: un componente di solito disegna la propria intestazione.\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.fieldText || ''\"\r\n (input)=\"setFieldProperty('fieldText', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.helpText || ''\"\r\n (input)=\"setFieldProperty('helpText', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Larghezza</label>\r\n <div class=\"fb-scr__width\">\r\n <input\r\n type=\"range\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n [value]=\"widthOf(selectedField()!)\"\r\n (input)=\"setNumberProperty('width', $any($event.target).value)\"\r\n />\r\n <span class=\"fb-scr__width-value\">{{ widthOf(selectedField()!) }}/12</span>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Colonne della griglia della schermata. \u00C8 un\u2019indicazione: il frontend puo\u2019 ignorarla.\r\n </p>\r\n </div>\r\n\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <!--\r\n \u00A75.2 \u2014 perche\u2019 qui non c\u2019e\u2019 \u00ABobbligatorio\u00BB. La domanda arriva sempre, e la risposta non\r\n si indovina guardando l\u2019interfaccia: un componente non ha un valore proprio, ce l\u2019hanno\r\n i suoi output, che vanno in **variabili** scelte dall\u2019autore. \u00ABObbligatorio\u00BB dovrebbe\r\n dire *quale output* deve essere valorizzato, e il metadata non ha modo di dirlo \u2014\r\n dichiararlo qui sarebbe `SCREEN_FIELD_CONFIGURATION_INVALID` e il runtime lo\r\n ignorerebbe. Le due strade vere stanno nel contratto e sono queste.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Un componente non raccoglie un valore \u2014 lo fanno i suoi output \u2014 quindi obbligatorieta\u2019, valore\r\n predefinito e regola di validazione non si applicano: dichiararli e\u2019\r\n <code>SCREEN_FIELD_CONFIGURATION_INVALID</code> e il runtime li ignora. Per pretendere una\r\n compilazione: la validazione la fa il componente stesso, oppure si controlla la variabile di\r\n destinazione con una Decision dopo la schermata.\r\n </p>\r\n }\r\n\r\n <!-- ------------------------------------------------ campi che raccolgono un valore -->\r\n @if (selectedType()?.storesValue) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valore</legend>\r\n\r\n @if (selectedField()!.fieldType !== 'ObjectProvided') {\r\n <div class=\"fb-field\">\r\n <label\r\n class=\"fb-field__label\"\r\n [class.fb-field__label--required]=\"!!selectedType()?.requiresDataType\"\r\n >\r\n Tipo di dato\r\n </label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.dataType || ''\"\r\n (change)=\"setFieldProperty('dataType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of dictionary.dataTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n @if (selectedType()?.requiresDataType && !selectedField()!.dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio per questo tipo di campo (DATA_TYPE_MISSING).</p>\r\n }\r\n @if (selectedType()?.isCollection) {\r\n <p class=\"fb-field__hint\">\r\n Il valore raccolto e\u2019 una <strong>collection</strong>, non una stringa con i valori\r\n separati: nelle condizioni si usano gli operatori di collection.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\" [class.fb-field__label--required]=\"objectTypeIsStructure()\">\r\n {{\r\n objectTypeIsStructure()\r\n ? 'Classe'\r\n : selectedField()!.dataType === 'Enum'\r\n ? 'Tipo di enumerazione'\r\n : 'Oggetto'\r\n }}\r\n </label>\r\n @if (selectedField()!.dataType === 'Enum') {\r\n <!-- Dizionario chiuso: si sceglie, non si scrive. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.objectType || ''\"\r\n (change)=\"setFieldProperty('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza il tipo concreto l\u2019editor non puo\u2019 proporre i valori dell\u2019enumerazione.\r\n </p>\r\n }\r\n } @else if (objectTypeIsStructure()) {\r\n <fb-structure-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Classe\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.objectType) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, il runtime non ha nulla da istanziare\r\n (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"selectedField()!.objectType\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setFieldProperty('objectType', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo dell\u2019entita\u2019</label>\r\n <p class=\"fb-field__hint\">\r\n Tipo, label, obbligatorieta\u2019, scala e \u2014 se il campo e\u2019 una picklist \u2014 le opzioni arrivano\r\n dallo schema dati: qui non si ridichiarano.\r\n </p>\r\n <fb-object-picker\r\n [value]=\"providedObject() || undefined\"\r\n label=\"Oggetto\"\r\n (valueChange)=\"setProvidedObject($event)\"\r\n />\r\n <fb-field-picker\r\n [value]=\"providedField() || undefined\"\r\n [object]=\"providedObject() || undefined\"\r\n label=\"Campo\"\r\n (valueChange)=\"setProvidedField($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsRequired()\"\r\n (change)=\"setRequired($any($event.target).checked)\"\r\n />\r\n Obbligatorio\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedIsEditable()\"\r\n (change)=\"setEditable($any($event.target).checked)\"\r\n />\r\n Modificabile\r\n </label>\r\n @if (!selectedIsEditable()) {\r\n <p class=\"fb-field__hint\">\r\n A <code>false</code> il runtime <strong>ignora</strong> cio\u2019 che il client rimanda indietro:\r\n e\u2019 l\u2019unico modo di rendere un campo davvero di sola lettura.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore di default</label>\r\n <fb-value-editor\r\n [value]=\"selectedField()!.defaultValue\"\r\n [dataType]=\"selectedField()!.dataType\"\r\n [objectType]=\"selectedField()!.objectType\"\r\n [isCollection]=\"selectedType()?.isCollection\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n label=\"Valore di default\"\r\n (valueChange)=\"setDefaultValue($event)\"\r\n />\r\n </div>\r\n\r\n @if (scaleApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.scale ?? ''\"\r\n (change)=\"setNumberProperty('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n } @else if (selectedField()!.scale !== undefined) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>scale</code> su un campo non numerico e\u2019 SCALE_NOT_APPLICABLE.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Lunghezza massima</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxLength ?? ''\"\r\n (change)=\"setNumberProperty('maxLength', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Verificata anche dal runtime (TOO_LONG).</p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tornando sulla schermata</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.inputsOnNextNavToAssocScrn || ''\"\r\n (change)=\"setFieldProperty('inputsOnNextNavToAssocScrn', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito (mantieni i valori)</option>\r\n @for (entry of dictionary.screenFieldInputsRevisited(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Regola di validazione</label>\r\n <!--\r\n `commitOn=\"change\"`: qui mutare il documento ridisegna l'anteprima, e riscriverla a\r\n ogni tasto costa. La verifica parte lo stesso mentre si digita (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"selectedField()!.validationRule?.formulaExpression || ''\"\r\n usage=\"ValidationRule\"\r\n expectedDataType=\"Boolean\"\r\n commitOn=\"change\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [rows]=\"2\"\r\n placeholder=\"Espressione, es. LEN(Nome) > 3\"\r\n ariaLabel=\"Regola di validazione\"\r\n (expressionChange)=\"setValidationRule('formulaExpression', $event)\"\r\n />\r\n <input\r\n class=\"fb-input\"\r\n placeholder=\"Messaggio mostrato quando l\u2019espressione e\u2019 falsa\"\r\n [value]=\"selectedField()!.validationRule?.errorMessage || ''\"\r\n (change)=\"setValidationRule('errorMessage', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione la valuta il motore di regole: il backend non la interpreta, la fa\r\n verificare al motore.\r\n </p>\r\n </div>\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------------------- choice -->\r\n @if (selectedType()?.acceptsChoices) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Opzioni</legend>\r\n <p class=\"fb-section__note\">\r\n Accetta <strong>Choice</strong> e <strong>Dynamic choice set</strong>, nell\u2019ordine in cui le\r\n opzioni compaiono. Puntare a una variabile e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\r\n </p>\r\n\r\n @if (!selectedField()!.choiceReferences?.length) {\r\n @if (selectedField()!.fieldType === 'ObjectProvided') {\r\n <!-- Le opzioni di una picklist arrivano dallo schema: qui si aggiungono solo se\r\n si vuole sostituirle, e non dichiararne nessuna e' il caso normale. -->\r\n <p class=\"fb-field__hint\">\r\n Se il campo dello schema e\u2019 una picklist, le opzioni arrivano da l\u00EC: dichiararle qui\r\n serve solo a sostituirle.\r\n </p>\r\n } @else {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessuna opzione: il campo non ha niente da mostrare (SCREEN_FIELD_CHOICES_MISSING).\r\n </p>\r\n }\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__title\">{{ choice }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, -1)\">\u2191</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"moveChoice($index, 1)\">\u2193</button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--sm\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeChoice($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggiungi un\u2019opzione</label>\r\n <!--\r\n `commitOn=\"enter\"`: qui ogni emissione e\u2019 una riga in piu\u2019, non un campo che cambia\r\n valore \u2014 a tasto, scrivere \u00ABAlta\u00BB aggiungeva \u00ABA\u00BB, \u00ABAl\u00BB, \u00ABAlt\u00BB e \u00ABAlta\u00BB.\r\n -->\r\n <fb-name-picker\r\n [options]=\"choiceOptions()\"\r\n label=\"Choice\"\r\n commitOn=\"enter\"\r\n placeholder=\"Scegli una choice o un choice set\"\r\n unknownMessage=\"Questo nome non e\u2019 una choice ne\u2019 un choice set: e\u2019 SCREEN_FIELD_CHOICE_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Il flow non dichiara nessuna choice: creale nel pannello delle risorse.\"\r\n (valueChange)=\"addChoice($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Scegli dall\u2019elenco, oppure scrivi il nome e premi Invio.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Opzione selezionata all\u2019apertura</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.defaultSelectedChoiceReference || ''\"\r\n (change)=\"setFieldProperty('defaultSelectedChoiceReference', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 nessuna \u2014</option>\r\n @for (choice of selectedField()!.choiceReferences || []; track $index) {\r\n <option [value]=\"choice\">{{ choice }}</option>\r\n }\r\n </select>\r\n @if (defaultChoiceIsForeign()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n \u00AB{{ selectedField()!.defaultSelectedChoiceReference }}\u00BB non e\u2019 fra le opzioni elencate qui\r\n sopra.\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedAutoSelectsSingleChoice()\"\r\n (change)=\"setAutoSelectSingleChoice($any($event.target).checked)\"\r\n />\r\n Scegli da sola l\u2019unica opzione\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Quando le opzioni risolte sono <strong>esattamente una</strong>, il campo la prende da se\u2019.\r\n Serve alle tendine che dipendono da un\u2019altra tendina \u2014 i CAP di un comune \u2014 dove chiedere\r\n di scegliere l\u2019unica voce non decide niente.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- contenitori -->\r\n @if (selectedType()?.isContainer) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Contenitore</legend>\r\n @if (selectedField()!.fieldType === 'RegionContainer') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Tipo di sezione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"selectedField()!.regionContainerType || ''\"\r\n (change)=\"setFieldProperty('regionContainerType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (entry of dictionary.regionContainerTypes(); track entry.value) {\r\n <option [value]=\"entry.value\">{{ entry.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il contenitore e\u2019 vuoto: non produce niente sulla schermata (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------ sezione ripetibile (\u00A75.2) -->\r\n @if (selectedIsRepeatingSection()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Righe</legend>\r\n <p class=\"fb-section__note\">\r\n I campi qui dentro sono il <strong>modello di una riga</strong>: la schermata ne rende una\r\n copia per ogni elemento della collection, e l\u2019utente decide quante con un tasto. I due\r\n riferimenti vanno pensati insieme \u2014 la <strong>collection</strong> e\u2019 dove le righe vivono,\r\n il <strong>cursore</strong> e\u2019 la riga corrente, come\r\n l\u2019<code>assignNextValueToReference</code> di un Loop.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection delle righe</label>\r\n <fb-reference-picker\r\n [value]=\"selectedField()!.collectionReference\"\r\n dataType=\"Structure\"\r\n [isCollection]=\"true\"\r\n [writableOnly]=\"true\"\r\n label=\"Collection delle righe\"\r\n placeholder=\"Una variabile collection di tipo Structure\"\r\n (valueChange)=\"setFieldProperty('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 quella che il resto del flow attraversa con un Loop: la sezione non introduce un posto\r\n nuovo in cui i dati stanno. Se la riga e\u2019 il DTO che poi finisce in una action, conviene\r\n che sia una collection <strong>di quel DTO</strong>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cursore della riga</label>\r\n <fb-reference-picker\r\n [value]=\"selectedField()!.itemReference\"\r\n dataType=\"Structure\"\r\n [isCollection]=\"false\"\r\n [objectType]=\"rowClass()\"\r\n [writableOnly]=\"true\"\r\n label=\"Cursore della riga\"\r\n placeholder=\"Una variabile Structure della stessa classe\"\r\n (valueChange)=\"setFieldProperty('itemReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il nome che si scrive <strong>dentro</strong> la riga: visibilita\u2019, regole di\r\n validazione, filtri delle tendine, parametri dei componenti e\r\n <code>itemLabel</code> nominano\r\n <code>{{ selectedField()!.itemReference || 'Cursore' }}.<membro></code>, mai il nome\r\n di un campo della riga.\r\n </p>\r\n </div>\r\n\r\n @if (repeaterIssue(); as issue) {\r\n <p class=\"fb-callout fb-callout--error\">{{ issue }} (REPEATING_SECTION_INVALID)</p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe minime</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.minInstances ?? ''\"\r\n (change)=\"setNumberProperty('minInstances', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe massime</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"selectedField()!.maxInstances ?? ''\"\r\n (change)=\"setNumberProperty('maxInstances', $any($event.target).value)\"\r\n />\r\n @if (repeaterHasNoLimit()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza un massimo l\u2019utente puo\u2019 aggiungere righe all\u2019infinito\r\n (REPEATING_SECTION_WITHOUT_LIMIT).\r\n </p>\r\n }\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.allowAdd !== false\"\r\n (change)=\"setRepeaterFlag('allowAdd', $any($event.target).checked)\"\r\n />\r\n L\u2019utente puo\u2019 aggiungere righe\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.allowRemove !== false\"\r\n (change)=\"setRepeaterFlag('allowRemove', $any($event.target).checked)\"\r\n />\r\n L\u2019utente puo\u2019 togliere righe\r\n </label>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Scritta del comando \u00ABaggiungi\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.addButtonLabel || ''\"\r\n (input)=\"setFieldProperty('addButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Scritta del comando \u00ABtogli\u00BB</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"selectedField()!.removeButtonLabel || ''\"\r\n (input)=\"setFieldProperty('removeButtonLabel', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Intestazione della singola riga</label>\r\n <!-- `commitOn=\"change\"`: mutare il documento ridisegna l'anteprima. -->\r\n <fb-merge-text-editor\r\n [text]=\"selectedField()!.itemLabel || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Punto {{ '{' }}!Cursore.PodId}\"\r\n ariaLabel=\"Intestazione della riga\"\r\n (textChange)=\"setFieldProperty('itemLabel', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Risolta <strong>sulla riga</strong>: i merge field qui nominano il cursore\r\n (<code>{!{{ selectedField()!.itemReference || 'Cursore' }}.Membro}</code>), non\r\n un campo della riga.\r\n </p>\r\n </fb-merge-text-editor>\r\n </div>\r\n\r\n @if (!selectedField()!.fields?.length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il modello della riga e\u2019 vuoto: la sezione non mostra niente (SCREEN_CONTAINER_EMPTY).\r\n </p>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!-- --------------------------------------------------- ComponentInstance -->\r\n @if (selectedField()!.fieldType === 'ComponentInstance') {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Componente</legend>\r\n <p class=\"fb-section__note\">\r\n Componenti e form sono la stessa domanda al frontend \u2014 cosa sa rendere, e con quali parametri\r\n \u2014 e passano dallo stesso catalogo. Un <code>ComponentInstance</code> non ha un valore proprio:\r\n lo hanno i suoi output.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Componente</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.extensionName\"\r\n [options]=\"componentOptions()\"\r\n [unusableOptions]=\"unusableComponents()\"\r\n label=\"Componente\"\r\n placeholder=\"Scrivi o scegli un componente\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo componente non esiste nel catalogo: e\u2019 SCREEN_COMPONENT_UNKNOWN.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 una schermata intera, non un componente montabile qui (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei componenti non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFieldProperty('extensionName', $event ?? '')\"\r\n />\r\n @if (!selectedField()!.extensionName) {\r\n <p class=\"fb-field__error\">Obbligatorio: senza, SCREEN_COMPONENT_MISSING.</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 dentro una riga la spunta **non c'e'**: gli output automatici si espongono come\r\n `<NomeCampo>.<NomeOutput>`, che sarebbe **una risorsa sola per tutte le righe** \u2014 l'ultima\r\n che ha girato. \u00C8 `REPEATING_SECTION_INVALID`, e la strada giusta e' `assignToReference` su\r\n un membro del cursore, che una riga ce l'ha per davvero.\r\n -->\r\n @if (!isInsideRepeater()) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"selectedField()!.storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Rendi gli output referenziabili automaticamente\r\n </label>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n Gli output si referenziano come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, senza dichiarare\r\n variabili.\r\n </p>\r\n }\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Output automatici <strong>e</strong> parametri di uscita insieme:\r\n OUTPUT_CONFIGURATION_CONFLICT.\r\n </p>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli output automatici qui non si possono usare: si esporrebbero come\r\n <code>{{ selectedField()!.name || 'Campo' }}.nomeOutput</code>, cioe\u2019 una risorsa sola per\r\n tutte le righe \u2014 l\u2019ultima che ha girato (REPEATING_SECTION_INVALID). Assegnali con\r\n <strong>assignToReference</strong> a un membro di\r\n <code>{{ rowCursor() || 'Cursore' }}</code>.\r\n </p>\r\n @if (selectedField()!.storeOutputAutomatically) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo componente dichiara gli output automatici e sta dentro una riga:\r\n REPEATING_SECTION_INVALID.\r\n <button type=\"button\" class=\"fb-btn fb-btn--sm\" (click)=\"setStoreOutputAutomatically(false)\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n }\r\n\r\n <fb-parameter-editor\r\n [holder]=\"$any(selectedField())\"\r\n [catalogParameters]=\"componentParameterList()\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n inputTitle=\"Valori passati al componente\"\r\n outputTitle=\"Valori raccolti dal componente\"\r\n [showOutputs]=\"!selectedField()!.storeOutputAutomatically\"\r\n outputsDisabledReason=\"Gli output sono automatici: disattivalo per assegnarli a variabili.\"\r\n (changed)=\"onComponentParametersChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- ------------------------------------------------------- visibilita' -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Visibilita\u2019</legend>\r\n <p class=\"fb-section__note\">\r\n Le regole si rivalutano <strong>sui valori appena inviati</strong>: e\u2019 cos\u00EC che un campo compare\r\n in funzione di un altro campo della stessa schermata. Un campo risultato nascosto viene\r\n <strong>azzerato</strong> e non viene validato.\r\n </p>\r\n <fb-condition-editor\r\n [holder]=\"visibilityRule()\"\r\n title=\"Mostra il campo quando\"\r\n [allowFormula]=\"false\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [issuePath]=\"'fields[' + (selectedField()!.name || '') + '].visibilityRule'\"\r\n (changed)=\"onVisibilityChanged($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<!--\r\n ============================================================== l'anteprima\r\n Due template ricorsivi. `listTpl` disegna i figli di un contenitore, e nient'altro: `data-item`\r\n marca cio' che conta come fratello nel calcolo del punto di rilascio, e il segnaposto del\r\n contenitore vuoto ne resta fuori. La barra di inserimento non e' qui \u2014 sta sopra l'anteprima,\r\n vedi il velo nel frame.\r\n-->\r\n<ng-template #listTpl let-fields let-parent=\"parent\" let-axis=\"axis\">\r\n @for (child of fields; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"fieldTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: child, path: childPath(parent, $index), axis: axis }\"\r\n />\r\n }\r\n @if (!fields.length) {\r\n <p class=\"fb-scr__drop-hint\">Trascina qui un componente</p>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #fieldTpl let-field let-path=\"path\" let-axis=\"axis\">\r\n <div\r\n class=\"fb-scr__item\"\r\n data-item=\"\"\r\n cdkDrag\r\n [style.grid-column]=\"'span ' + widthOf(field)\"\r\n [class.fb-scr__item--selected]=\"isSelected(path)\"\r\n [class.fb-scr__item--dragging]=\"isDragging(path)\"\r\n [class.fb-scr__item--container]=\"holdsFields(field)\"\r\n (cdkDragStarted)=\"onFieldDragStarted(path)\"\r\n (cdkDragMoved)=\"onFieldDragMoved($event)\"\r\n (cdkDragEnded)=\"onFieldDragEnded($event, path)\"\r\n (click)=\"select(path); $event.stopPropagation()\"\r\n >\r\n <span class=\"fb-scr__grip\" cdkDragHandle title=\"Trascina per spostare\" aria-hidden=\"true\">\u283F</span>\r\n @if (field.visibilityRule?.conditions?.length) {\r\n <span class=\"fb-scr__flag\" title=\"Ha una regola di visibilita\u2019\">\u25D0</span>\r\n }\r\n\r\n @switch (field.fieldType) {\r\n @case ('RegionContainer') {\r\n <div class=\"fb-scr__section\">\r\n @if (field.regionContainerType !== 'SectionWithoutHeader') {\r\n <header class=\"fb-scr__section-head\">{{ field.fieldText || field.name }}</header>\r\n }\r\n <div class=\"fb-scr__cols\" [attr.data-drop]=\"dropId(path)\" data-axis=\"row\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'row' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('Region') {\r\n <div class=\"fb-scr__region\">\r\n <span class=\"fb-scr__region-tag\">{{ field.name }} \u00B7 {{ widthOf(field) }}/12</span>\r\n <div class=\"fb-scr__region-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n @case ('RepeatingSection') {\r\n <!--\r\n \u00A75.2 \u2014 si disegna **una** riga, ed e' il modello: a runtime la schermata ne rende una copia\r\n per elemento della collection. Disegnarne due sarebbe finto \u2014 quante siano lo decide chi\r\n compila \u2014 e disegnarla senza cornice farebbe credere che i campi siano della schermata.\r\n -->\r\n <div class=\"fb-scr__repeat\">\r\n <header class=\"fb-scr__repeat-head\">\r\n <span>{{ field.fieldText || field.name }}</span>\r\n <span class=\"fb-scr__repeat-meta\">\r\n {{ field.collectionReference || 'collection non indicata' }}\r\n @if (field.minInstances !== undefined || field.maxInstances !== undefined) {\r\n \u00B7 {{ field.minInstances ?? 0 }}\u2013{{ field.maxInstances ?? '\u221E' }}\r\n }\r\n </span>\r\n </header>\r\n <div class=\"fb-scr__repeat-row\">\r\n <span class=\"fb-scr__repeat-tag\">\r\n {{ field.itemLabel || 'Riga 1' }}\r\n @if (field.allowRemove !== false) {\r\n <span class=\"fb-scr__repeat-x\" aria-hidden=\"true\">\u00D7</span>\r\n }\r\n </span>\r\n <div class=\"fb-scr__repeat-body\" [attr.data-drop]=\"dropId(path)\" data-axis=\"column\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listTpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: field.fields || [], parent: path, axis: 'column' }\"\r\n />\r\n </div>\r\n </div>\r\n @if (field.allowAdd !== false) {\r\n <span class=\"fb-scr__repeat-add\">+ {{ field.addButtonLabel || 'Aggiungi' }}</span>\r\n }\r\n </div>\r\n }\r\n @case ('DisplayText') {\r\n <p class=\"fb-scr__display\">{{ field.fieldText || '(testo vuoto)' }}</p>\r\n }\r\n @case ('LargeTextArea') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--area\">{{ placeholderOf(field) }}</div>\r\n }\r\n @case ('PasswordField') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022</div>\r\n }\r\n @case ('DropdownBox') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ field.defaultSelectedChoiceReference || choiceLabelsOf(field)[0] || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u25BE</span>\r\n </div>\r\n }\r\n @case ('MultiSelectPicklist') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control fb-scr__control--select\">\r\n <span>{{ choiceLabelsOf(field).join(', ') || '\u2014 scegli \u2014' }}</span>\r\n <span aria-hidden=\"true\">\u2261</span>\r\n </div>\r\n }\r\n @case ('RadioButtons') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u25EF {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('MultiSelectCheckboxes') {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__options\">\r\n @for (option of choiceLabelsOf(field); track $index) {\r\n <span class=\"fb-scr__option\">\u2610 {{ option }}</span>\r\n }\r\n @if (!choiceLabelsOf(field).length) {\r\n <span class=\"fb-scr__option fb-scr__option--missing\">Nessuna opzione</span>\r\n }\r\n </div>\r\n }\r\n @case ('ComponentInstance') {\r\n <!--\r\n L\u2019etichetta si disegna se c\u2019e\u2019: il runtime la consegna in `label` come per gli altri\r\n campi (\u00A75.2). Nessun asterisco: un componente non raccoglie un valore, quindi\r\n \u00ABobbligatorio\u00BB non ha niente su cui applicarsi.\r\n -->\r\n @if (field.fieldText) {\r\n <label class=\"fb-scr__label\">{{ field.fieldText }}</label>\r\n }\r\n <div class=\"fb-scr__component\">\r\n <span aria-hidden=\"true\">\u2B21</span>\r\n {{ field.extensionName || 'Componente non indicato' }}\r\n </div>\r\n }\r\n @case ('ObjectProvided') {\r\n <label class=\"fb-scr__label\">\r\n {{ field.fieldText || field.objectFieldReference || field.name }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n <span class=\"fb-scr__tag\">{{ field.objectFieldReference || 'campo non indicato' }}</span>\r\n }\r\n @default {\r\n <label class=\"fb-scr__label\">\r\n {{ captionOf(field) }}\r\n @if (field.isRequired) {\r\n <span class=\"fb-scr__req\">*</span>\r\n }\r\n </label>\r\n <div class=\"fb-scr__control\">{{ placeholderOf(field) }}</div>\r\n @if (isUnknownType(field)) {\r\n <span class=\"fb-scr__tag fb-scr__tag--warn\">{{ field.fieldType }}: tipo non nel dizionario</span>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".fb-scr{display:grid;grid-template-columns:220px minmax(0,1fr) 340px;gap:12px;align-items:start;margin-bottom:14px}@media(max-width:1100px){.fb-scr{grid-template-columns:minmax(0,1fr)}}.fb-scr__side,.fb-scr__props{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__tabs{display:flex;gap:4px;margin-bottom:8px}.fb-scr__tab{flex:1;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;font-weight:600;cursor:pointer}.fb-scr__tab--active{border-color:var(--fb-accent, #4f6ef7);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent);color:var(--fb-accent-strong, #3d59e0)}.fb-scr__side-hint,.fb-scr__side-empty{margin:6px 0;font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__palette{display:flex;flex-direction:column;gap:4px;margin:8px 0 0;padding:0;max-height:320px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-scr__chip{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-scr__chip:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-scr__chip-icon{flex:none;width:20px;text-align:center;color:var(--fb-text-muted, #6b7086)}.fb-scr__chip-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-scr__chip-label small{margin-left:4px;color:var(--fb-text-subtle, #98a2b3);font-size:10px}.fb-scr__canvas{min-width:0;padding:14px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-canvas-bg, #f4f5f7);max-height:60vh;overflow-y:auto}.fb-scr__frame{position:relative;display:flex;flex-direction:column;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));overflow:hidden}.fb-scr__frame-head{display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:13px;font-weight:600}.fb-scr__help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px}.fb-scr__body,.fb-scr__region-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:12px;min-height:90px}.fb-scr__region-body{padding:8px;min-height:60px}.fb-scr__cols{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-items:stretch;gap:0;padding:8px;min-height:60px}.fb-scr__cols>.fb-scr__item{display:flex;align-self:stretch}.fb-scr__cols>.fb-scr__item>.fb-scr__region{flex:1;min-width:0}.fb-scr__drop-hint{grid-column:1 / -1;margin:0;padding:10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);color:var(--fb-text-subtle, #98a2b3);font-size:11px;text-align:center}.fb-scr__marker{position:absolute;z-index:2;border-radius:2px;background:var(--fb-accent, #4f6ef7);pointer-events:none}.fb-scr__item{position:relative;box-sizing:border-box;padding:6px 8px;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);cursor:pointer}.fb-scr__item:hover{border-color:var(--fb-border-strong, #cfd4de);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 4%,transparent)}.fb-scr__item--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:inset 0 0 0 1px var(--fb-accent, #4f6ef7)}.fb-scr__item--dragging{opacity:.45;pointer-events:none}.fb-scr__grip{position:absolute;top:2px;left:-2px;padding:0 3px;color:var(--fb-text-subtle, #98a2b3);font-size:11px;line-height:1;opacity:0;cursor:grab}.fb-scr__item:hover>.fb-scr__grip,.fb-scr__item--selected>.fb-scr__grip{opacity:1}.fb-scr__flag{position:absolute;top:2px;right:4px;color:var(--fb-text-muted, #6b7086);font-size:11px}.fb-scr__label{display:block;margin-bottom:3px;font-size:11px;font-weight:600;color:var(--fb-text, #1a1c23)}.fb-scr__req{color:var(--fb-error, #dc2626)}.fb-scr__control{display:flex;align-items:center;justify-content:space-between;gap:6px;min-height:26px;padding:4px 8px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text-subtle, #98a2b3);font-size:12px;pointer-events:none}.fb-scr__control--area{min-height:54px;align-items:flex-start}.fb-scr__display{margin:0;font-size:12px;color:var(--fb-text, #1a1c23)}.fb-scr__options{display:flex;flex-direction:column;gap:2px;pointer-events:none}.fb-scr__option{font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__option--missing{color:var(--fb-warning, #b7791f)}.fb-scr__component{display:flex;align-items:center;gap:6px;padding:10px;border:1px dashed var(--fb-accent, #4f6ef7);border-radius:var(--fb-radius-xs, 6px);background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 5%,transparent);font-size:12px;color:var(--fb-text-muted, #6b7086)}.fb-scr__tag{display:inline-block;margin-top:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__tag--warn{color:var(--fb-warning, #b7791f)}.fb-scr__section{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__section-head{padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__region{border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__region-tag{display:block;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat{border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__repeat-head{display:flex;align-items:baseline;gap:8px;padding:5px 10px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__repeat-meta{margin-left:auto;font-size:10px;font-weight:400;text-transform:none;letter-spacing:0;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat-row{margin:8px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-scr__repeat-tag{display:flex;align-items:center;gap:6px;padding:3px 6px 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-scr__repeat-x{margin-left:auto}.fb-scr__repeat-body{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 0;padding:8px;min-height:60px}.fb-scr__repeat-add{display:inline-block;margin:0 8px 8px;padding:3px 10px;border:1px dashed var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__frame-foot{display:flex;align-items:center;gap:6px;padding:8px 12px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-scr__spacer{flex:1}.fb-scr__btn{padding:3px 10px;border:1px solid var(--fb-border-strong, #cfd4de);border-radius:var(--fb-radius-xs, 6px);font-size:11px;color:var(--fb-text-muted, #6b7086)}.fb-scr__btn--primary{border-color:var(--fb-accent, #4f6ef7);background:var(--fb-accent, #4f6ef7);color:var(--fb-accent-contrast, #fff)}.fb-scr__canvas-note{margin-top:10px}.fb-scr__props{background:var(--fb-surface, #fff)}.fb-scr__props-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.fb-scr__props-title{flex:1;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-scr__props-actions{display:flex;flex-wrap:wrap;gap:3px}.fb-btn--sm{padding:3px 7px;font-size:11px}.fb-scr__width{display:flex;align-items:center;gap:8px}.fb-scr__width input[type=range]{flex:1;min-width:0}.fb-scr__width-value{font-size:11px;color:var(--fb-text-muted, #6b7086)}\n"] }]
|
|
12359
12913
|
}], ctorParameters: () => [], propDecorators: { initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], composer: [{ type: i0.ViewChild, args: ['composer', { isSignal: true }] }] } });
|
|
12360
12914
|
|
|
12361
12915
|
/**
|
|
@@ -12429,7 +12983,7 @@ class LoopInspectorComponent extends NodeInspectorBase {
|
|
|
12429
12983
|
this.setField('iterationOrder', order);
|
|
12430
12984
|
}
|
|
12431
12985
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: LoopInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
12432
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: LoopInspectorComponent, isStandalone: true, selector: "fb-loop-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection da iterare</label>\r\n <!-- Solo collection: iterare una variabile singola e' un errore (LOOP_COLLECTION_MISSING). -->\r\n <fb-reference-picker\r\n [value]=\"loop().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Anche l\u2019output automatico di un Get Records senza \u00ABsolo il primo record\u00BB e\u2019 iterabile.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento corrente</label>\r\n <fb-reference-picker\r\n [value]=\"loop().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"collectionType()\"\r\n [objectType]=\"collectionObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che riceve l\u2019elemento\"\r\n (valueChange)=\"setCurrentValueVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve essere una variabile <strong>singola</strong> dello stesso tipo degli elementi: senza, il corpo del\r\n ciclo non ha modo di leggere l\u2019elemento corrente.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordine di iterazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"loop().iterationOrder || ''\"\r\n (change)=\"setIterationOrder($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n</div>\r\n\r\n@if (hasBody() && !bodyReturnsToLoop()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il corpo del ciclo non torna sul Loop: nessun percorso dal ramo \u00ABper ogni valore\u00BB rientra qui, quindi\r\n verrebbe eseguita una sola iterazione. Collega l\u2019ultimo elemento del corpo di nuovo al Loop, marcando\r\n l\u2019arco come salto indietro.\r\n </p>\r\n}\r\n@if (!hasBody()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Il ramo \u00ABper ogni valore\u00BB non ha destinazione: il ciclo non ha corpo.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami del ciclo\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<p class=\"fb-field__hint\">\r\n Il cursore si azzera all\u2019uscita: rientrare nello stesso Loop ricomincia da capo.\r\n</p>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12986
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: LoopInspectorComponent, isStandalone: true, selector: "fb-loop-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection da iterare</label>\r\n <!-- Solo collection: iterare una variabile singola e' un errore (LOOP_COLLECTION_MISSING). -->\r\n <fb-reference-picker\r\n [value]=\"loop().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Anche l\u2019output automatico di un Get Records senza \u00ABsolo il primo record\u00BB e\u2019 iterabile.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento corrente</label>\r\n <fb-reference-picker\r\n [value]=\"loop().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"collectionType()\"\r\n [objectType]=\"collectionObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che riceve l\u2019elemento\"\r\n (valueChange)=\"setCurrentValueVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve essere una variabile <strong>singola</strong> dello stesso tipo degli elementi: senza, il corpo del\r\n ciclo non ha modo di leggere l\u2019elemento corrente.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordine di iterazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"loop().iterationOrder || ''\"\r\n (change)=\"setIterationOrder($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n</div>\r\n\r\n@if (hasBody() && !bodyReturnsToLoop()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il corpo del ciclo non torna sul Loop: nessun percorso dal ramo \u00ABper ogni valore\u00BB rientra qui, quindi\r\n verrebbe eseguita una sola iterazione. Collega l\u2019ultimo elemento del corpo di nuovo al Loop, marcando\r\n l\u2019arco come salto indietro.\r\n </p>\r\n}\r\n@if (!hasBody()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Il ramo \u00ABper ogni valore\u00BB non ha destinazione: il ciclo non ha corpo.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami del ciclo\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<p class=\"fb-field__hint\">\r\n Il cursore si azzera all\u2019uscita: rientrare nello stesso Loop ricomincia da capo.\r\n</p>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12433
12987
|
}
|
|
12434
12988
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: LoopInspectorComponent, decorators: [{
|
|
12435
12989
|
type: Component,
|
|
@@ -12770,7 +13324,7 @@ class OrchestratedStageInspectorComponent extends NodeInspectorBase {
|
|
|
12770
13324
|
/** C'e' almeno uno step il cui rifiuto e' un esito previsto. */
|
|
12771
13325
|
hasApprovalStep = computed(() => this.steps().some((step) => this.supportsRejection(step)), ...(ngDevMode ? [{ debugName: "hasApprovalStep" }] : []));
|
|
12772
13326
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: OrchestratedStageInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12773
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: OrchestratedStageInspectorComponent, isStandalone: true, selector: "fb-orchestrated-stage-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step dello stage</legend>\r\n <p class=\"fb-section__note\">\r\n Gli step <strong>non sono una sequenza</strong>: parte quello le cui condizioni d\u2019ingresso sono vere, e\r\n l\u2019ordine qui sotto e\u2019 solo l\u2019ordine in cui vengono esaminati.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n <!-- `stepIndex` esplicito: dentro l'elenco degli assegnatari `$index` e' quello dell'assegnatario. -->\r\n @for (step of steps(); track $index; let stepIndex = $index, isFirst = $first, isLast = $last) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- Titolo, non indice: gli step non sono numerati perche' non sono una sequenza. -->\r\n <span class=\"fb-list__title\">{{ step.label || step.name || '\u2014' }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Esamina prima\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isFirst\"\r\n (click)=\"moveStep(stepIndex, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Esamina dopo\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isLast\"\r\n (click)=\"moveStep(stepIndex, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi lo step\"\r\n (click)=\"removeStep(stepIndex)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"step.label || ''\"\r\n placeholder=\"Approva la pratica\"\r\n (input)=\"setStepLabel(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!stepNameError(step)\"\r\n [value]=\"step.name || ''\"\r\n (input)=\"setStepName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (stepNameError(step)) {\r\n <p class=\"fb-field__error\">{{ stepNameError(step) }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Sta nello stesso spazio dei nomi di elementi e risorse. Gli output dello step sono\r\n referenziabili come <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di step</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"!step.actionType || isUnknownStepType(step)\"\r\n [fbValue]=\"step.actionType || ''\"\r\n (change)=\"setStepType(stepIndex, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of stepTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!step.actionType) {\r\n <p class=\"fb-field__error\">Senza tipo lo step non e\u2019 valido (STAGE_STEP_TYPE_MISSING).</p>\r\n } @else if (isUnknownStepType(step)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ step.actionType }}\u00BB non e\u2019 un tipo di step di questo sistema (STAGE_STEP_TYPE_UNKNOWN).\r\n </p>\r\n } @else if (stepTypeDescription(step)) {\r\n <p class=\"fb-field__hint\">{{ stepTypeDescription(step) }}</p>\r\n }\r\n </div>\r\n\r\n @if (requiresActionName(step)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da eseguire</label>\r\n <!--\r\n Una lista di candidati vuota significa \"non lo so\", non \"nessuno\": il campo resta\r\n scrivibile a mano invece di bloccare l'utente (\u00A77).\r\n -->\r\n <fb-name-picker\r\n [value]=\"step.actionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Flow da eseguire\"\r\n placeholder=\"Preparazione_Pratica\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setStepActionName(stepIndex, $event ?? '')\"\r\n />\r\n @if (!step.actionName) {\r\n <p class=\"fb-field__error\">\r\n Uno step in background esegue un flow: senza, e\u2019 STAGE_STEP_FLOW_MISSING.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Il motore lo esegue subito, senza coinvolgere nessuno.</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (requiresAssignees(step)) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Assegnatari</legend>\r\n <p class=\"fb-section__note\">\r\n Su questo step l\u2019interview si <strong>sospende</strong>: resta aperto un work item finche\u2019\r\n una persona non lo conclude.\r\n </p>\r\n <div class=\"fb-list\">\r\n @for (assignee of assigneesOf(step); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019assegnatario\"\r\n (click)=\"removeAssignee(stepIndex, $index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignee.assigneeType || ''\"\r\n (change)=\"setAssigneeType(stepIndex, $index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of assigneeTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Chi</label>\r\n <fb-value-editor\r\n [value]=\"assignee.assignee\"\r\n label=\"Assegnatario\"\r\n dataType=\"String\"\r\n (valueChange)=\"setAssigneeValue(stepIndex, $index, $event)\"\r\n />\r\n </div>\r\n @if (isAssigneeIncomplete(assignee)) {\r\n <p class=\"fb-field__error\">Servono tipo e destinatario (STAGE_STEP_ASSIGNEE_INVALID).</p>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Senza assegnatari questo step non e\u2019 valido (STAGE_STEP_ASSIGNEES_MISSING).\r\n </p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAssignee(stepIndex)\">\r\n Aggiungi assegnatario\r\n </button>\r\n\r\n @if (supportsRejection(step)) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isMultiMemberApproval(step)\"\r\n (change)=\"setMultiMemberApproval(stepIndex, $any($event.target).checked)\"\r\n />\r\n Serve l\u2019approvazione di tutti i membri\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--\r\n Ingresso e uscita non sono simmetriche: se l'ingresso non si avvera lo step viene\r\n saltato, se l'uscita resta falsa lo stage va in stallo e l'esecuzione fallisce (\u00A75.14).\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].entry\"\r\n title=\"Condizioni d\u2019ingresso (se lo step si applica)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onEntryConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Se non si avverano, lo step viene <strong>saltato</strong> e lo stage prosegue.\r\n </p>\r\n\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].exit\"\r\n title=\"Condizioni d\u2019uscita (quando lo step libera lo stage)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onExitConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Se restano false quando non c\u2019e\u2019 piu\u2019 niente in esecuzione, lo stage e\u2019 in stallo e\r\n l\u2019esecuzione <strong>fallisce</strong>.\r\n </p>\r\n\r\n @if (stepOutputInConditions(step)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una condizione referenzia l\u2019output dello step \u00AB{{ stepOutputInConditions(step) }}\u00BB: finche\u2019\r\n quello step non ha girato il riferimento e\u2019 irrisolvibile, e a runtime e\u2019 un errore. Fai\r\n scrivere quel risultato in una variabile (parametro di uscita \u2192 destinazione) e condiziona su\r\n quella.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019ingresso</label>\r\n <fb-name-picker\r\n [value]=\"step.entryActionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Evaluation flow per l\u2019ingresso\"\r\n placeholder=\"Valuta_Ingresso\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setEntryActionName(stepIndex, $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve restituire l\u2019output booleano <code>{{ conditionOutputName }}</code>: e\u2019 l\u2019unico che il\r\n runtime legge, dichiararne altri e\u2019 STAGE_ACTION_INVALID.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019uscita</label>\r\n <fb-name-picker\r\n [value]=\"step.exitActionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Evaluation flow per l\u2019uscita\"\r\n placeholder=\"Valuta_Uscita\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setExitActionName(stepIndex, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"step\"\r\n inputTitle=\"Parametri dello step\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dallo step\"\r\n (changed)=\"onParametersChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n La destinazione e\u2019 <strong>facoltativa</strong>: l\u2019output e\u2019 gi\u00E0 referenziabile come\r\n <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>. Serve una variabile solo se un altro step\r\n deve condizionare su quel risultato.\r\n </p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"hasSimulatedOutputs(step)\"\r\n (change)=\"setSimulateStep(stepIndex, $any($event.target).checked)\"\r\n />\r\n Simula lo step nella prova\r\n </label>\r\n @if (hasSimulatedOutputs(step)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Con la simulazione lo step non viene eseguito ne\u2019 assegnato: si usano gli output finti di\r\n <code>outputConfigParams</code>. Non memorizzarci dati personali.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"step.description || ''\"\r\n (input)=\"setStepDescription(stepIndex, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Uno stage senza step non fa nulla ed e\u2019 un errore di validazione (STAGE_WITHOUT_STEPS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addStep()\">Aggiungi step</button>\r\n</fieldset>\r\n\r\n@if (hasApprovalStep() && !hasRejectionBranch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n C\u2019e\u2019 uno step di approvazione ma il ramo \u00ABStep rifiutato\u00BB non e\u2019 disegnato: senza, un rifiuto fa\r\n <strong>fallire</strong> l\u2019interview. Non e\u2019 un ramo di guasto, e\u2019 l\u2019esito previsto del rifiuto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13327
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: OrchestratedStageInspectorComponent, isStandalone: true, selector: "fb-orchestrated-stage-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step dello stage</legend>\r\n <p class=\"fb-section__note\">\r\n Gli step <strong>non sono una sequenza</strong>: parte quello le cui condizioni d\u2019ingresso sono vere, e\r\n l\u2019ordine qui sotto e\u2019 solo l\u2019ordine in cui vengono esaminati.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n <!-- `stepIndex` esplicito: dentro l'elenco degli assegnatari `$index` e' quello dell'assegnatario. -->\r\n @for (step of steps(); track $index; let stepIndex = $index, isFirst = $first, isLast = $last) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- Titolo, non indice: gli step non sono numerati perche' non sono una sequenza. -->\r\n <span class=\"fb-list__title\">{{ step.label || step.name || '\u2014' }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Esamina prima\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isFirst\"\r\n (click)=\"moveStep(stepIndex, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Esamina dopo\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isLast\"\r\n (click)=\"moveStep(stepIndex, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi lo step\"\r\n (click)=\"removeStep(stepIndex)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"step.label || ''\"\r\n placeholder=\"Approva la pratica\"\r\n (input)=\"setStepLabel(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!stepNameError(step)\"\r\n [value]=\"step.name || ''\"\r\n (input)=\"setStepName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (stepNameError(step)) {\r\n <p class=\"fb-field__error\">{{ stepNameError(step) }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Sta nello stesso spazio dei nomi di elementi e risorse. Gli output dello step sono\r\n referenziabili come <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di step</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"!step.actionType || isUnknownStepType(step)\"\r\n [fbValue]=\"step.actionType || ''\"\r\n (change)=\"setStepType(stepIndex, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of stepTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!step.actionType) {\r\n <p class=\"fb-field__error\">Senza tipo lo step non e\u2019 valido (STAGE_STEP_TYPE_MISSING).</p>\r\n } @else if (isUnknownStepType(step)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ step.actionType }}\u00BB non e\u2019 un tipo di step di questo sistema (STAGE_STEP_TYPE_UNKNOWN).\r\n </p>\r\n } @else if (stepTypeDescription(step)) {\r\n <p class=\"fb-field__hint\">{{ stepTypeDescription(step) }}</p>\r\n }\r\n </div>\r\n\r\n @if (requiresActionName(step)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da eseguire</label>\r\n <!--\r\n Una lista di candidati vuota significa \"non lo so\", non \"nessuno\": il campo resta\r\n scrivibile a mano invece di bloccare l'utente (\u00A77).\r\n -->\r\n <fb-name-picker\r\n [value]=\"step.actionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Flow da eseguire\"\r\n placeholder=\"Preparazione_Pratica\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setStepActionName(stepIndex, $event ?? '')\"\r\n />\r\n @if (!step.actionName) {\r\n <p class=\"fb-field__error\">\r\n Uno step in background esegue un flow: senza, e\u2019 STAGE_STEP_FLOW_MISSING.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Il motore lo esegue subito, senza coinvolgere nessuno.</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (requiresAssignees(step)) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Assegnatari</legend>\r\n <p class=\"fb-section__note\">\r\n Su questo step l\u2019interview si <strong>sospende</strong>: resta aperto un work item finche\u2019\r\n una persona non lo conclude.\r\n </p>\r\n <div class=\"fb-list\">\r\n @for (assignee of assigneesOf(step); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019assegnatario\"\r\n (click)=\"removeAssignee(stepIndex, $index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignee.assigneeType || ''\"\r\n (change)=\"setAssigneeType(stepIndex, $index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of assigneeTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Chi</label>\r\n <fb-value-editor\r\n [value]=\"assignee.assignee\"\r\n label=\"Assegnatario\"\r\n dataType=\"String\"\r\n (valueChange)=\"setAssigneeValue(stepIndex, $index, $event)\"\r\n />\r\n </div>\r\n @if (isAssigneeIncomplete(assignee)) {\r\n <p class=\"fb-field__error\">Servono tipo e destinatario (STAGE_STEP_ASSIGNEE_INVALID).</p>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Senza assegnatari questo step non e\u2019 valido (STAGE_STEP_ASSIGNEES_MISSING).\r\n </p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAssignee(stepIndex)\">\r\n Aggiungi assegnatario\r\n </button>\r\n\r\n @if (supportsRejection(step)) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isMultiMemberApproval(step)\"\r\n (change)=\"setMultiMemberApproval(stepIndex, $any($event.target).checked)\"\r\n />\r\n Serve l\u2019approvazione di tutti i membri\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--\r\n Ingresso e uscita non sono simmetriche: se l'ingresso non si avvera lo step viene\r\n saltato, se l'uscita resta falsa lo stage va in stallo e l'esecuzione fallisce (\u00A75.14).\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].entry\"\r\n title=\"Condizioni d\u2019ingresso (se lo step si applica)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onEntryConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Se non si avverano, lo step viene <strong>saltato</strong> e lo stage prosegue.\r\n </p>\r\n\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].exit\"\r\n title=\"Condizioni d\u2019uscita (quando lo step libera lo stage)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onExitConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Se restano false quando non c\u2019e\u2019 piu\u2019 niente in esecuzione, lo stage e\u2019 in stallo e\r\n l\u2019esecuzione <strong>fallisce</strong>.\r\n </p>\r\n\r\n @if (stepOutputInConditions(step)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una condizione referenzia l\u2019output dello step \u00AB{{ stepOutputInConditions(step) }}\u00BB: finche\u2019\r\n quello step non ha girato il riferimento e\u2019 irrisolvibile, e a runtime e\u2019 un errore. Fai\r\n scrivere quel risultato in una variabile (parametro di uscita \u2192 destinazione) e condiziona su\r\n quella.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019ingresso</label>\r\n <fb-name-picker\r\n [value]=\"step.entryActionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Evaluation flow per l\u2019ingresso\"\r\n placeholder=\"Valuta_Ingresso\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setEntryActionName(stepIndex, $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve restituire l\u2019output booleano <code>{{ conditionOutputName }}</code>: e\u2019 l\u2019unico che il\r\n runtime legge, dichiararne altri e\u2019 STAGE_ACTION_INVALID.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019uscita</label>\r\n <fb-name-picker\r\n [value]=\"step.exitActionName\"\r\n [options]=\"candidateOptions()\"\r\n label=\"Evaluation flow per l\u2019uscita\"\r\n placeholder=\"Valuta_Uscita\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: senza una versione attiva il motore non lo trova.\"\r\n emptyMessage=\"Elenco dei flow non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setExitActionName(stepIndex, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"step\"\r\n inputTitle=\"Parametri dello step\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dallo step\"\r\n (changed)=\"onParametersChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n La destinazione e\u2019 <strong>facoltativa</strong>: l\u2019output e\u2019 gi\u00E0 referenziabile come\r\n <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>. Serve una variabile solo se un altro step\r\n deve condizionare su quel risultato.\r\n </p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"hasSimulatedOutputs(step)\"\r\n (change)=\"setSimulateStep(stepIndex, $any($event.target).checked)\"\r\n />\r\n Simula lo step nella prova\r\n </label>\r\n @if (hasSimulatedOutputs(step)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Con la simulazione lo step non viene eseguito ne\u2019 assegnato: si usano gli output finti di\r\n <code>outputConfigParams</code>. Non memorizzarci dati personali.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"step.description || ''\"\r\n (input)=\"setStepDescription(stepIndex, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Uno stage senza step non fa nulla ed e\u2019 un errore di validazione (STAGE_WITHOUT_STEPS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addStep()\">Aggiungi step</button>\r\n</fieldset>\r\n\r\n@if (hasApprovalStep() && !hasRejectionBranch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n C\u2019e\u2019 uno step di approvazione ma il ramo \u00ABStep rifiutato\u00BB non e\u2019 disegnato: senza, un rifiuto fa\r\n <strong>fallire</strong> l\u2019interview. Non e\u2019 un ramo di guasto, e\u2019 l\u2019esito previsto del rifiuto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "preferredRoots", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12774
13328
|
}
|
|
12775
13329
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: OrchestratedStageInspectorComponent, decorators: [{
|
|
12776
13330
|
type: Component,
|
|
@@ -12968,7 +13522,7 @@ class RecordLookupInspectorComponent extends NodeInspectorBase {
|
|
|
12968
13522
|
/** `relatedRecords` e' modellato ma non tradotto in query: se c'e', si avvisa (§5.7). */
|
|
12969
13523
|
hasRelatedRecords = computed(() => (this.lookup().relatedRecords?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasRelatedRecords" }] : []));
|
|
12970
13524
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordLookupInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12971
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordLookupInspectorComponent, isStandalone: true, selector: "fb-record-lookup-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"lookup().object\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<fb-record-filter-editor\r\n [holder]=\"lookup()\"\r\n [object]=\"lookup().object\"\r\n title=\"Quali record leggere\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza filtri legge tutti i record dell\u2019oggetto.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n/>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Quanti e in che ordine</legend>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"lookup().getFirstRecordOnly === true\"\r\n (change)=\"setFirstOnly($any($event.target).checked)\"\r\n />\r\n Solo il primo record\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n @if (returnsCollection()) {\r\n Il risultato e\u2019 una <strong>collection</strong>: puo\u2019 essere iterata da un Loop.\r\n } @else {\r\n Il risultato e\u2019 un <strong>record singolo</strong>: non e\u2019 iterabile da un Loop.\r\n }\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n <fb-field-picker\r\n [value]=\"lookup().sortField\"\r\n [object]=\"lookup().object\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setSortField($event ?? '')\"\r\n />\r\n </div>\r\n\r\n @if (lookup().sortField) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortOrder || ''\" (change)=\"setSortOrder($any($event.target).value)\">\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n\r\n @if (returnsCollection()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di record</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"lookup().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n</fieldset>\r\n\r\n@if (fieldOptions().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Campi da leggere</legend>\r\n <p class=\"fb-section__note\">Nessuna selezione = tutti i campi disponibili.</p>\r\n <div class=\"fb-fields-grid\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isQueried(field.name)\"\r\n (change)=\"toggleQueriedField(field.name, $any($event.target).checked)\"\r\n />\r\n {{ field.label || field.name }}\r\n </label>\r\n }\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Dove finisce il risultato</legend>\r\n\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono dichiarati insieme l\u2019output automatico e una destinazione esplicita: e\u2019 un conflitto\r\n (OUTPUT_CONFIGURATION_CONFLICT). Scegli una sola modalita\u2019 qui sotto.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'automatic'\"\r\n (change)=\"setOutputMode('automatic')\"\r\n />\r\n Output automatico <em>(consigliato)</em>\r\n </label>\r\n @if (outputMode() === 'automatic') {\r\n <p class=\"fb-field__hint\">\r\n Il risultato si referenzia con il nome dell\u2019elemento: <code>{{ name() }}</code>,\r\n <code>{{ name() }}.Campo</code>. Non serve dichiarare nessuna variabile.\r\n </p>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'variable'\"\r\n (change)=\"setOutputMode('variable')\"\r\n />\r\n In una variabile\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'assignments'\"\r\n (change)=\"setOutputMode('assignments')\"\r\n />\r\n Campo per campo\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'discard'\"\r\n (change)=\"setOutputMode('discard')\"\r\n />\r\n Scarta il risultato\r\n </label>\r\n </div>\r\n\r\n @if (outputMode() === 'variable') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Variabile di destinazione</label>\r\n <fb-reference-picker\r\n [value]=\"lookup().outputReference\"\r\n [writableOnly]=\"true\"\r\n [isCollection]=\"returnsCollection()\"\r\n [objectType]=\"lookup().object\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputReference($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (outputMode() === 'assignments') {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutputAssignment($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del record</label>\r\n <fb-field-picker\r\n [value]=\"assignment.field\"\r\n [object]=\"lookup().object\"\r\n usage=\"any\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setOutputAssignmentField($index, $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputAssignmentTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutputAssignment()\">Aggiungi campo</button>\r\n }\r\n\r\n @if (outputMode() === 'discard') {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n L\u2019elemento interroga il database e butta via il risultato (LOOKUP_RESULT_DISCARDED).\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n@if (hasRelatedRecords()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento dichiara <code>relatedRecords</code>: il campo e\u2019 modellato ma non viene tradotto in\r\n query, quindi non ha effetto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13525
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordLookupInspectorComponent, isStandalone: true, selector: "fb-record-lookup-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"lookup().object\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<fb-record-filter-editor\r\n [holder]=\"lookup()\"\r\n [object]=\"lookup().object\"\r\n title=\"Quali record leggere\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza filtri legge tutti i record dell\u2019oggetto.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n/>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Quanti e in che ordine</legend>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"lookup().getFirstRecordOnly === true\"\r\n (change)=\"setFirstOnly($any($event.target).checked)\"\r\n />\r\n Solo il primo record\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n @if (returnsCollection()) {\r\n Il risultato e\u2019 una <strong>collection</strong>: puo\u2019 essere iterata da un Loop.\r\n } @else {\r\n Il risultato e\u2019 un <strong>record singolo</strong>: non e\u2019 iterabile da un Loop.\r\n }\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n <fb-field-picker\r\n [value]=\"lookup().sortField\"\r\n [object]=\"lookup().object\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setSortField($event ?? '')\"\r\n />\r\n </div>\r\n\r\n @if (lookup().sortField) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortOrder || ''\" (change)=\"setSortOrder($any($event.target).value)\">\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n\r\n @if (returnsCollection()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di record</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"lookup().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n</fieldset>\r\n\r\n@if (fieldOptions().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Campi da leggere</legend>\r\n <p class=\"fb-section__note\">Nessuna selezione = tutti i campi disponibili.</p>\r\n <div class=\"fb-fields-grid\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isQueried(field.name)\"\r\n (change)=\"toggleQueriedField(field.name, $any($event.target).checked)\"\r\n />\r\n {{ field.label || field.name }}\r\n </label>\r\n }\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Dove finisce il risultato</legend>\r\n\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono dichiarati insieme l\u2019output automatico e una destinazione esplicita: e\u2019 un conflitto\r\n (OUTPUT_CONFIGURATION_CONFLICT). Scegli una sola modalita\u2019 qui sotto.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'automatic'\"\r\n (change)=\"setOutputMode('automatic')\"\r\n />\r\n Output automatico <em>(consigliato)</em>\r\n </label>\r\n @if (outputMode() === 'automatic') {\r\n <p class=\"fb-field__hint\">\r\n Il risultato si referenzia con il nome dell\u2019elemento: <code>{{ name() }}</code>,\r\n <code>{{ name() }}.Campo</code>. Non serve dichiarare nessuna variabile.\r\n </p>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'variable'\"\r\n (change)=\"setOutputMode('variable')\"\r\n />\r\n In una variabile\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'assignments'\"\r\n (change)=\"setOutputMode('assignments')\"\r\n />\r\n Campo per campo\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'discard'\"\r\n (change)=\"setOutputMode('discard')\"\r\n />\r\n Scarta il risultato\r\n </label>\r\n </div>\r\n\r\n @if (outputMode() === 'variable') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Variabile di destinazione</label>\r\n <fb-reference-picker\r\n [value]=\"lookup().outputReference\"\r\n [writableOnly]=\"true\"\r\n [isCollection]=\"returnsCollection()\"\r\n [objectType]=\"lookup().object\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputReference($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (outputMode() === 'assignments') {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutputAssignment($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del record</label>\r\n <fb-field-picker\r\n [value]=\"assignment.field\"\r\n [object]=\"lookup().object\"\r\n usage=\"any\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setOutputAssignmentField($index, $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputAssignmentTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutputAssignment()\">Aggiungi campo</button>\r\n }\r\n\r\n @if (outputMode() === 'discard') {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n L\u2019elemento interroga il database e butta via il risultato (LOOKUP_RESULT_DISCARDED).\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n@if (hasRelatedRecords()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento dichiara <code>relatedRecords</code>: il campo e\u2019 modellato ma non viene tradotto in\r\n query, quindi non ha effetto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12972
13526
|
}
|
|
12973
13527
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordLookupInspectorComponent, decorators: [{
|
|
12974
13528
|
type: Component,
|
|
@@ -13192,7 +13746,7 @@ class RecordWriteInspectorComponent extends NodeInspectorBase {
|
|
|
13192
13746
|
this.patch((node) => mutate(node));
|
|
13193
13747
|
}
|
|
13194
13748
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordWriteInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13195
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordWriteInspectorComponent, isStandalone: true, selector: "fb-record-write-inspector", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Come indicare i {{ title() }}</legend>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'object'\" (change)=\"setMode('object')\" />\r\n Per oggetto{{ needsFilters() ? ' e filtri' : ' e valori' }}\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'reference'\" (change)=\"setMode('reference')\" />\r\n Un record gi\u00E0 in memoria\r\n </label>\r\n</fieldset>\r\n\r\n@if (mode() === 'reference') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Record</label>\r\n <fb-reference-picker\r\n [value]=\"record().inputReference\"\r\n dataType=\"Object\"\r\n placeholder=\"Variabile di tipo record\"\r\n (valueChange)=\"setInputReference($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il record porta con se\u2019 i propri valori: non serve indicare oggetto ne\u2019 campi.\r\n </p>\r\n </div>\r\n} @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"record().object\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n\r\n @if (needsFilters()) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isDelete() ? 'Quali record cancellare' : 'Quali record aggiornare'\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"supportsFilterLogic()\"\r\n [emptyWarning]=\"emptyFilterWarning()\"\r\n [emptyWarningSeverity]=\"emptyFilterSeverity()\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showsBulkConfirm()) {\r\n <label class=\"fb-check fb-confirm\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"bulkUpdateConfirmed()\"\r\n (change)=\"confirmBulkUpdate($any($event.target).checked)\"\r\n />\r\n Confermo di voler aggiornare <strong>tutti</strong> i record di \u00AB{{ record().object || 'questo oggetto' }}\u00BB\r\n </label>\r\n }\r\n }\r\n\r\n @if (!isDelete()) {\r\n <!-- I due insiemi non coincidono: `createable` su un Create, `updateable` su un Update. -->\r\n <fb-field-assignment-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [usage]=\"assignmentUsage()\"\r\n [title]=\"isCreate() ? 'Valori del nuovo record' : 'Valori da scrivere'\"\r\n (changed)=\"onAssignmentsChanged($event)\"\r\n />\r\n }\r\n}\r\n\r\n@if (isCreate()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Upsert</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().doesUpsert === true\"\r\n (change)=\"setDoesUpsert($any($event.target).checked)\"\r\n />\r\n Aggiorna il record se esiste gi\u00E0\r\n </label>\r\n\r\n @if (record().doesUpsert) {\r\n @if (hasUpsertConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono indicati insieme il campo di id esterno e quello standard: va scelto uno solo\r\n (UPSERT_CONFIGURATION_INVALID).\r\n </p>\r\n }\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'external'\"\r\n (change)=\"setUpsertMode('external')\"\r\n />\r\n Riconosci il record da un id esterno\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'standard'\"\r\n (change)=\"setUpsertMode('standard')\"\r\n />\r\n Riconosci il record dall\u2019id standard\r\n </label>\r\n\r\n @if (upsertMode() === 'external') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id esterno</label>\r\n <fb-field-picker\r\n [value]=\"record().upsertExternalIdField\"\r\n [object]=\"record().object\"\r\n usage=\"any\"\r\n label=\"Campo id esterno\"\r\n (valueChange)=\"setUpsertExternalField($event ?? '')\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id standard</label>\r\n <fb-field-picker\r\n [value]=\"record().upsertStandardIdField\"\r\n [object]=\"record().object\"\r\n usage=\"any\"\r\n label=\"Campo id standard\"\r\n (valueChange)=\"setUpsertStandardField($event ?? '')\"\r\n />\r\n </div>\r\n }\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Identificativo creato</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (record().storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n L\u2019identificativo creato si referenzia col nome dell\u2019elemento: <code>{{ name() }}</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna l\u2019identificativo a</label>\r\n <fb-reference-picker\r\n [value]=\"record().assignRecordIdToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setAssignRecordId($event)\"\r\n />\r\n </div>\r\n }\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldAssignmentEditorComponent, selector: "fb-field-assignment-editor", inputs: ["holder", "object", "usage", "title", "disabledReason"], outputs: ["changed"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13749
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: RecordWriteInspectorComponent, isStandalone: true, selector: "fb-record-write-inspector", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Come indicare i {{ title() }}</legend>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'object'\" (change)=\"setMode('object')\" />\r\n Per oggetto{{ needsFilters() ? ' e filtri' : ' e valori' }}\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'reference'\" (change)=\"setMode('reference')\" />\r\n Un record gi\u00E0 in memoria\r\n </label>\r\n</fieldset>\r\n\r\n@if (mode() === 'reference') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Record</label>\r\n <fb-reference-picker\r\n [value]=\"record().inputReference\"\r\n dataType=\"Object\"\r\n placeholder=\"Variabile di tipo record\"\r\n (valueChange)=\"setInputReference($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il record porta con se\u2019 i propri valori: non serve indicare oggetto ne\u2019 campi.\r\n </p>\r\n </div>\r\n} @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"record().object\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n\r\n @if (needsFilters()) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isDelete() ? 'Quali record cancellare' : 'Quali record aggiornare'\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"supportsFilterLogic()\"\r\n [emptyWarning]=\"emptyFilterWarning()\"\r\n [emptyWarningSeverity]=\"emptyFilterSeverity()\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showsBulkConfirm()) {\r\n <label class=\"fb-check fb-confirm\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"bulkUpdateConfirmed()\"\r\n (change)=\"confirmBulkUpdate($any($event.target).checked)\"\r\n />\r\n Confermo di voler aggiornare <strong>tutti</strong> i record di \u00AB{{ record().object || 'questo oggetto' }}\u00BB\r\n </label>\r\n }\r\n }\r\n\r\n @if (!isDelete()) {\r\n <!-- I due insiemi non coincidono: `createable` su un Create, `updateable` su un Update. -->\r\n <fb-field-assignment-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [usage]=\"assignmentUsage()\"\r\n [title]=\"isCreate() ? 'Valori del nuovo record' : 'Valori da scrivere'\"\r\n (changed)=\"onAssignmentsChanged($event)\"\r\n />\r\n }\r\n}\r\n\r\n@if (isCreate()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Upsert</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().doesUpsert === true\"\r\n (change)=\"setDoesUpsert($any($event.target).checked)\"\r\n />\r\n Aggiorna il record se esiste gi\u00E0\r\n </label>\r\n\r\n @if (record().doesUpsert) {\r\n @if (hasUpsertConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono indicati insieme il campo di id esterno e quello standard: va scelto uno solo\r\n (UPSERT_CONFIGURATION_INVALID).\r\n </p>\r\n }\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'external'\"\r\n (change)=\"setUpsertMode('external')\"\r\n />\r\n Riconosci il record da un id esterno\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'standard'\"\r\n (change)=\"setUpsertMode('standard')\"\r\n />\r\n Riconosci il record dall\u2019id standard\r\n </label>\r\n\r\n @if (upsertMode() === 'external') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id esterno</label>\r\n <fb-field-picker\r\n [value]=\"record().upsertExternalIdField\"\r\n [object]=\"record().object\"\r\n usage=\"any\"\r\n label=\"Campo id esterno\"\r\n (valueChange)=\"setUpsertExternalField($event ?? '')\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id standard</label>\r\n <fb-field-picker\r\n [value]=\"record().upsertStandardIdField\"\r\n [object]=\"record().object\"\r\n usage=\"any\"\r\n label=\"Campo id standard\"\r\n (valueChange)=\"setUpsertStandardField($event ?? '')\"\r\n />\r\n </div>\r\n }\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Identificativo creato</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (record().storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n L\u2019identificativo creato si referenzia col nome dell\u2019elemento: <code>{{ name() }}</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna l\u2019identificativo a</label>\r\n <fb-reference-picker\r\n [value]=\"record().assignRecordIdToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setAssignRecordId($event)\"\r\n />\r\n </div>\r\n }\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldAssignmentEditorComponent, selector: "fb-field-assignment-editor", inputs: ["holder", "object", "usage", "title", "disabledReason"], outputs: ["changed"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13196
13750
|
}
|
|
13197
13751
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: RecordWriteInspectorComponent, decorators: [{
|
|
13198
13752
|
type: Component,
|
|
@@ -13304,7 +13858,7 @@ class ScreenInspectorComponent extends NodeInspectorBase {
|
|
|
13304
13858
|
this.patch((node) => mutate(node));
|
|
13305
13859
|
}
|
|
13306
13860
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ScreenInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13307
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ScreenInspectorComponent, isStandalone: true, selector: "fb-screen-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Lo screen dichiara <strong>quale form</strong> mostrare, non i suoi campi: il form e\u2019 un componente del\r\n frontend applicativo, e qui si definisce solo il contratto di dati.\r\n</p>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Form</label>\r\n <fb-name-picker\r\n [value]=\"screen().formName\"\r\n [options]=\"formOptions()\"\r\n [unusableOptions]=\"unusableForms()\"\r\n label=\"Form\"\r\n placeholder=\"Scrivi o scegli un form\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo form non esiste nel catalogo: la validazione lo segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 un componente di screen dinamico, non una schermata: qui serve un form (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei form non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFormName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setHelpText($any($event.target).value)\"\r\n ></textarea>\r\n</div>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta del form \u2014\r\n per esempio \u00ABindietro\u00BB conta solo se esiste uno screen precedente nel percorso.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setPausedText($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo screen non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e l\u2019utente resterebbe\r\n bloccato.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"screen()\"\r\n [catalogParameters]=\"parameters()\"\r\n inputTitle=\"Valori passati al form\"\r\n outputTitle=\"Valori raccolti dal form\"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13861
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ScreenInspectorComponent, isStandalone: true, selector: "fb-screen-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Lo screen dichiara <strong>quale form</strong> mostrare, non i suoi campi: il form e\u2019 un componente del\r\n frontend applicativo, e qui si definisce solo il contratto di dati.\r\n</p>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Form</label>\r\n <fb-name-picker\r\n [value]=\"screen().formName\"\r\n [options]=\"formOptions()\"\r\n [unusableOptions]=\"unusableForms()\"\r\n label=\"Form\"\r\n placeholder=\"Scrivi o scegli un form\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo form non esiste nel catalogo: la validazione lo segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n unusableMessage=\"Questo nome e\u2019 un componente di screen dinamico, non una schermata: qui serve un form (FORM_KIND_MISMATCH).\"\r\n emptyMessage=\"Il catalogo dei form non e\u2019 popolato: il nome non viene verificato.\"\r\n (valueChange)=\"setFormName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo di aiuto</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"screen().helpText || ''\"\r\n (input)=\"setHelpText($any($event.target).value)\"\r\n ></textarea>\r\n</div>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Navigazione</legend>\r\n <p class=\"fb-section__note\">\r\n Questi flag sono un\u2019intenzione, non la verita\u2019 finale: a runtime il motore comunica\r\n <code>canGoBack</code>, <code>canFinish</code> e <code>canPause</code> nella richiesta del form \u2014\r\n per esempio \u00ABindietro\u00BB conta solo se esiste uno screen precedente nel percorso.\r\n </p>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowBack()\" (change)=\"setFlag('allowBack', $any($event.target).checked)\" />\r\n Consenti \u00ABindietro\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowFinish()\" (change)=\"setFlag('allowFinish', $any($event.target).checked)\" />\r\n Consenti \u00ABfine\u00BB\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"checkbox\" [checked]=\"allowPause()\" (change)=\"setFlag('allowPause', $any($event.target).checked)\" />\r\n Consenti \u00ABpausa\u00BB\r\n </label>\r\n\r\n @if (allowPause()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Testo mostrato alla pausa</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"screen().pausedText || ''\"\r\n (input)=\"setPausedText($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isDeadEnd()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo screen non ha una destinazione e non consente \u00ABfine\u00BB: e\u2019 un vicolo cieco, e l\u2019utente resterebbe\r\n bloccato.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"screen()\"\r\n [catalogParameters]=\"parameters()\"\r\n inputTitle=\"Valori passati al form\"\r\n outputTitle=\"Valori raccolti dal form\"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13308
13862
|
}
|
|
13309
13863
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ScreenInspectorComponent, decorators: [{
|
|
13310
13864
|
type: Component,
|
|
@@ -13371,7 +13925,7 @@ class ScriptCallInspectorComponent extends NodeInspectorBase {
|
|
|
13371
13925
|
this.patch((node) => mutate(node));
|
|
13372
13926
|
}
|
|
13373
13927
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ScriptCallInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13374
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.28", type: ScriptCallInspectorComponent, isStandalone: true, selector: "fb-script-call-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Script</label>\r\n <fb-name-picker\r\n [value]=\"script().scriptName\"\r\n [options]=\"scriptOptions()\"\r\n label=\"Script\"\r\n placeholder=\"Scrivi o scegli uno script\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo script non esiste nel catalogo: la validazione lo segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Catalogo degli script non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setScriptName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n</label>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"script()\"\r\n [catalogParameters]=\"parameterCatalog()\"\r\n [showOutputs]=\"!storeOutputAutomatically()\"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13928
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.28", type: ScriptCallInspectorComponent, isStandalone: true, selector: "fb-script-call-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Script</label>\r\n <fb-name-picker\r\n [value]=\"script().scriptName\"\r\n [options]=\"scriptOptions()\"\r\n label=\"Script\"\r\n placeholder=\"Scrivi o scegli uno script\"\r\n [isMono]=\"false\"\r\n unknownMessage=\"Questo script non esiste nel catalogo: la validazione lo segnala come errore.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"Catalogo degli script non disponibile: puoi scrivere il nome a mano.\"\r\n (valueChange)=\"setScriptName($event ?? '')\"\r\n />\r\n</div>\r\n\r\n<label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n</label>\r\n\r\n<fb-parameter-editor\r\n [holder]=\"script()\"\r\n [catalogParameters]=\"parameterCatalog()\"\r\n [showOutputs]=\"!storeOutputAutomatically()\"\r\n (changed)=\"onParametersChanged($event)\"\r\n/>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13375
13929
|
}
|
|
13376
13930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ScriptCallInspectorComponent, decorators: [{
|
|
13377
13931
|
type: Component,
|
|
@@ -13706,7 +14260,7 @@ class SubflowInspectorComponent extends NodeInspectorBase {
|
|
|
13706
14260
|
});
|
|
13707
14261
|
}
|
|
13708
14262
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: SubflowInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13709
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: SubflowInspectorComponent, isStandalone: true, selector: "fb-subflow-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da invocare</label>\r\n <fb-name-picker\r\n [value]=\"subflow().flowName\"\r\n [options]=\"candidates()\"\r\n label=\"Flow da invocare\"\r\n placeholder=\"API name del flow\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: solo i flow con una versione attiva lo sono.\"\r\n emptyMessage=\"Nessun flow attivo disponibile come subflow: attivane uno, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setFlowName($event ?? '')\"\r\n />\r\n @if (hasCandidates()) {\r\n <p class=\"fb-field__hint\">Solo i flow con una versione attiva possono essere invocati.</p>\r\n }\r\n @if (isRecursive()) {\r\n <p class=\"fb-field__error\">Un flow non puo\u2019 invocare se stesso (SUBFLOW_RECURSIVE).</p>\r\n }\r\n</div>\r\n\r\n@if (couldNotLoadTarget()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile leggere la definizione del flow invocato: i nomi di input e output non vengono\r\n proposti, ma puoi scriverli a mano.\r\n </p>\r\n}\r\n\r\n<p class=\"fb-callout\">\r\n Un subflow che si sospende \u2014 cioe\u2019 che contiene screen o Wait \u2014 non e\u2019 supportato dal motore.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori passati al subflow</legend>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-section__note\">\r\n Sono le variabili di input del flow invocato: {{ inputVariables().length }} disponibili.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of inputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <fb-name-picker\r\n [value]=\"assignment.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Variabile del subflow\"\r\n placeholder=\"Nome della variabile di input\"\r\n unknownMessage=\"Questa variabile non e\u2019 fra gli input del flow invocato.\"\r\n emptyMessage=\"Input del flow invocato non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [objectType]=\"variableOf(assignment.name)?.objectType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n label=\"Valore\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore passato.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi valore</button>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori restituiti</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di output del subflow si referenziano col nome dell\u2019elemento:\r\n <code>{{ name() }}.NomeVariabile</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <fb-name-picker\r\n [value]=\"assignment.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Variabile di output del subflow\"\r\n placeholder=\"Nome della variabile di output\"\r\n unknownMessage=\"Questa variabile non e\u2019 fra gli output del flow invocato.\"\r\n emptyMessage=\"Output del flow invocato non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore raccolto.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi valore</button>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14263
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: SubflowInspectorComponent, isStandalone: true, selector: "fb-subflow-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da invocare</label>\r\n <fb-name-picker\r\n [value]=\"subflow().flowName\"\r\n [options]=\"candidates()\"\r\n label=\"Flow da invocare\"\r\n placeholder=\"API name del flow\"\r\n unknownMessage=\"Questo flow non e\u2019 fra quelli invocabili: solo i flow con una versione attiva lo sono.\"\r\n emptyMessage=\"Nessun flow attivo disponibile come subflow: attivane uno, oppure scrivi il nome a mano.\"\r\n (valueChange)=\"setFlowName($event ?? '')\"\r\n />\r\n @if (hasCandidates()) {\r\n <p class=\"fb-field__hint\">Solo i flow con una versione attiva possono essere invocati.</p>\r\n }\r\n @if (isRecursive()) {\r\n <p class=\"fb-field__error\">Un flow non puo\u2019 invocare se stesso (SUBFLOW_RECURSIVE).</p>\r\n }\r\n</div>\r\n\r\n@if (couldNotLoadTarget()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile leggere la definizione del flow invocato: i nomi di input e output non vengono\r\n proposti, ma puoi scriverli a mano.\r\n </p>\r\n}\r\n\r\n<p class=\"fb-callout\">\r\n Un subflow che si sospende \u2014 cioe\u2019 che contiene screen o Wait \u2014 non e\u2019 supportato dal motore.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori passati al subflow</legend>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-section__note\">\r\n Sono le variabili di input del flow invocato: {{ inputVariables().length }} disponibili.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of inputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <fb-name-picker\r\n [value]=\"assignment.name\"\r\n [options]=\"inputOptions()\"\r\n label=\"Variabile del subflow\"\r\n placeholder=\"Nome della variabile di input\"\r\n unknownMessage=\"Questa variabile non e\u2019 fra gli input del flow invocato.\"\r\n emptyMessage=\"Input del flow invocato non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setInputName($index, $event ?? '')\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [objectType]=\"variableOf(assignment.name)?.objectType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n label=\"Valore\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore passato.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi valore</button>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori restituiti</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di output del subflow si referenziano col nome dell\u2019elemento:\r\n <code>{{ name() }}.NomeVariabile</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <fb-name-picker\r\n [value]=\"assignment.name\"\r\n [options]=\"outputOptions()\"\r\n label=\"Variabile di output del subflow\"\r\n placeholder=\"Nome della variabile di output\"\r\n unknownMessage=\"Questa variabile non e\u2019 fra gli output del flow invocato.\"\r\n emptyMessage=\"Output del flow invocato non disponibili: scrivi il nome a mano.\"\r\n (valueChange)=\"setOutputName($index, $event ?? '')\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore raccolto.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi valore</button>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: NamePickerComponent, selector: "fb-name-picker", inputs: ["value", "options", "label", "placeholder", "disabled", "unknownMessage", "unknownSeverity", "unusableOptions", "unusableMessage", "emptyMessage", "isMono", "commitOn"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
13710
14264
|
}
|
|
13711
14265
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: SubflowInspectorComponent, decorators: [{
|
|
13712
14266
|
type: Component,
|
|
@@ -14370,7 +14924,7 @@ class TransformInspectorComponent extends NodeInspectorBase {
|
|
|
14370
14924
|
return null;
|
|
14371
14925
|
}
|
|
14372
14926
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: TransformInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14373
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: TransformInspectorComponent, isStandalone: true, selector: "fb-transform-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-tr\">\r\n <!-- ============================================================ sorgenti -->\r\n <aside class=\"fb-tr__panel\" aria-label=\"Sorgenti\">\r\n <h3 class=\"fb-tr__panel-title\">Sorgenti</h3>\r\n\r\n <input\r\n class=\"fb-input fb-tr__search\"\r\n type=\"search\"\r\n placeholder=\"Cerca una sorgente\"\r\n aria-label=\"Cerca una sorgente\"\r\n [value]=\"sourceQuery()\"\r\n (input)=\"sourceQuery.set($any($event.target).value)\"\r\n />\r\n\r\n @if (sourceCrumbs().length) {\r\n <nav class=\"fb-tr__crumbs\" aria-label=\"Percorso della sorgente\">\r\n <button type=\"button\" class=\"fb-tr__crumb\" (click)=\"enterSource('')\">Tutte</button>\r\n @for (crumb of sourceCrumbs(); track crumb.path) {\r\n <span class=\"fb-tr__crumb-sep\" aria-hidden=\"true\">\u203A</span>\r\n <button type=\"button\" class=\"fb-tr__crumb\" (click)=\"enterSource(crumb.path)\">{{ crumb.label }}</button>\r\n }\r\n </nav>\r\n }\r\n\r\n <p class=\"fb-tr__hint\">\r\n Trascina su una destinazione, oppure clicca per mapparla su quella selezionata.\r\n </p>\r\n\r\n @if (sourceError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n {{ sourceError() }} Le sorgenti si scrivono a mano nel dettaglio: l\u2019elenco non c\u2019e\u2019, i\r\n riferimenti s\u00EC.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-tr__list\">\r\n @for (source of sources(); track source.path) {\r\n <li class=\"fb-tr__source-row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-tr__source\"\r\n cdkDrag\r\n [class.fb-tr__source--dragging]=\"draggingPath() === source.path\"\r\n [title]=\"source.path + (source.detail ? ' \u00B7 ' + source.detail : '')\"\r\n (cdkDragStarted)=\"onSourceDragStarted(source)\"\r\n (cdkDragMoved)=\"onSourceDragMoved($event)\"\r\n (cdkDragEnded)=\"onSourceDragEnded($event, source)\"\r\n (click)=\"mapToSelected(source)\"\r\n >\r\n <span class=\"fb-tr__source-name\">\r\n {{ source.label }}\r\n @if (source.isCollection) {\r\n <span class=\"fb-tr__badge\" aria-label=\"collection\">[ ]</span>\r\n }\r\n </span>\r\n @if (source.detail) {\r\n <span class=\"fb-tr__source-detail\">{{ source.detail }}</span>\r\n }\r\n </button>\r\n @if (source.canEnter) {\r\n <!-- Entrare e mappare sono due gesti diversi: un record intero e' una sorgente valida. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-tr__enter\"\r\n [attr.aria-label]=\"'Entra in ' + source.label\"\r\n (click)=\"enterSource(source.path)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </li>\r\n } @empty {\r\n @if (sourcePending()) {\r\n <li class=\"fb-tr__empty\">Caricamento\u2026</li>\r\n } @else if (sourceUndeclared()) {\r\n <li class=\"fb-tr__empty\">\r\n Il catalogo non dichiara cosa c\u2019e\u2019 qui dentro: non significa che non ci sia niente. Il\r\n percorso si scrive a mano nel dettaglio.\r\n </li>\r\n } @else {\r\n <li class=\"fb-tr__empty\">Nessuna sorgente con questo nome.</li>\r\n }\r\n }\r\n </ul>\r\n </aside>\r\n\r\n <!-- ========================================================= destinazione -->\r\n <section class=\"fb-tr__panel fb-tr__map\" aria-label=\"Destinazione\">\r\n <h3 class=\"fb-tr__panel-title\">\r\n Destinazione\r\n @if (slotCount()) {\r\n <span class=\"fb-tr__count\">{{ mappedCount() }} di {{ slotCount() }} mappate</span>\r\n } @else if (mappedCount()) {\r\n <span class=\"fb-tr__count\">{{ mappedCount() }} mappate</span>\r\n }\r\n </h3>\r\n\r\n <!-- Il tipo del risultato e' la forma della colonna: sta in cima, non in fondo. -->\r\n <div class=\"fb-tr__result\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().dataType || ''\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n {{ isStructureTarget() ? 'Classe del risultato' : 'Tipo dell\u2019oggetto' }}\r\n </label>\r\n @if (isStructureTarget()) {\r\n <!-- \u00A74.7: una classe del backend. Le destinazioni delle azioni sono i suoi membri. -->\r\n <fb-structure-picker\r\n [value]=\"transform().objectType\"\r\n label=\"Classe del risultato\"\r\n (valueChange)=\"setObjectType($event ?? '')\"\r\n />\r\n } @else if (transform().dataType === 'Enum') {\r\n <!-- Le enumerazioni sono un dizionario chiuso: non c'e' scrittura libera da concedere. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().objectType || ''\"\r\n (change)=\"setObjectType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"transform().objectType\"\r\n label=\"Tipo dell\u2019oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObjectType($event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check fb-tr__collection\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"transform().isCollection === true\"\r\n (change)=\"setIsCollection($any($event.target).checked)\"\r\n />\r\n Collection\r\n </label>\r\n </div>\r\n\r\n @if (!transform().dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio (TRANSFORM_DATA_TYPE_MISSING).</p>\r\n }\r\n @if (missingObjectType()) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n @if (targetUndeclared()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il catalogo non dichiara i {{ isStructureTarget() ? 'membri' : 'campi' }} di\r\n <code>{{ transform().objectType }}</code>: le destinazioni si scrivono a mano, e nessuna di\r\n esse viene accusata di non esistere.\r\n </p>\r\n }\r\n </div>\r\n\r\n <ul class=\"fb-tr__list fb-tr__rows\">\r\n @for (row of targets(); track row.key) {\r\n <!--\r\n `data-fb-slot` e' cio' che il rilascio cerca: il bersaglio lo calcola `hitTest` dal DOM,\r\n non una drop list del CDK.\r\n -->\r\n <li\r\n class=\"fb-tr__row\"\r\n [attr.data-fb-slot]=\"row.key\"\r\n [class.fb-tr__row--selected]=\"isSelected(row)\"\r\n [class.fb-tr__row--drop]=\"dropKey() === row.key\"\r\n [class.fb-tr__row--empty]=\"row.index < 0\"\r\n >\r\n <button type=\"button\" class=\"fb-tr__row-main\" (click)=\"select(row.key)\">\r\n <span class=\"fb-tr__op\" [attr.title]=\"operationLabel(operationOf(row))\" aria-hidden=\"true\">\r\n {{ operationIcon(row) }}\r\n </span>\r\n <span class=\"fb-tr__row-text\">\r\n <span class=\"fb-tr__row-name\">\r\n {{ row.label }}\r\n @if (row.isCollection) {\r\n <span class=\"fb-tr__badge\" aria-label=\"collection\">[ ]</span>\r\n }\r\n @if (row.isUnknown) {\r\n <span class=\"fb-tr__badge fb-tr__badge--warn\">fuori catalogo</span>\r\n }\r\n </span>\r\n @if (row.index < 0) {\r\n <span class=\"fb-tr__row-src fb-tr__row-src--empty\">non mappata</span>\r\n } @else if (sourceSummary(row)) {\r\n <span class=\"fb-tr__row-src\">\u2190 {{ sourceSummary(row) }}</span>\r\n } @else {\r\n <span class=\"fb-tr__row-src fb-tr__row-src--empty\">sorgente da indicare</span>\r\n }\r\n </span>\r\n @if (row.dataType) {\r\n <span class=\"fb-tr__row-type\">{{ row.dataType }}</span>\r\n }\r\n @if (issuesOf(row).length) {\r\n <span class=\"fb-tr__row-issue\" [attr.title]=\"issuesOf(row)[0].message\">!</span>\r\n }\r\n </button>\r\n @if (row.index >= 0) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n [attr.aria-label]=\"'Togli la mappatura di ' + row.label\"\r\n (click)=\"removeAction(row)\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </li>\r\n } @empty {\r\n <li class=\"fb-empty\">\r\n Nessuna trasformazione: l\u2019elemento non produce nulla (TRANSFORM_WITHOUT_VALUES).\r\n </li>\r\n }\r\n </ul>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAction()\">\r\n {{ hasTargetCatalog() ? 'Aggiungi una destinazione fuori catalogo' : 'Aggiungi trasformazione' }}\r\n </button>\r\n\r\n <p class=\"fb-callout\">\r\n Il risultato e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento: si referenzia con\r\n <code>{{ name() }}</code>.\r\n </p>\r\n </section>\r\n\r\n <!-- ============================================================ dettaglio -->\r\n <aside class=\"fb-tr__panel fb-tr__detail\" aria-label=\"Dettaglio della mappatura\">\r\n @if (selected(); as row) {\r\n <h3 class=\"fb-tr__panel-title\">{{ row.label }}</h3>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <div class=\"fb-tr__modes\" role=\"group\" aria-label=\"Operazione\">\r\n @for (type of transformTypes(); track type.value) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-tr__mode\"\r\n [class.fb-tr__mode--active]=\"operationOf(row) === type.value\"\r\n (click)=\"setOperation(row, type.value)\"\r\n >\r\n {{ type.label }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n {{ isStructureTarget() ? 'Membro di destinazione' : 'Campo di destinazione' }}\r\n </label>\r\n @if (isStructureTarget()) {\r\n <!-- Solo i membri scrivibili: un membro calcolato e' TARGET_NOT_WRITABLE (\u00A74.7). -->\r\n <fb-structure-member-picker\r\n [value]=\"row.field\"\r\n [className]=\"transform().objectType\"\r\n usage=\"writable\"\r\n label=\"Membro di destinazione\"\r\n (valueChange)=\"setOutputField(row, $event ?? '')\"\r\n />\r\n } @else if (targetObject()) {\r\n <!-- Il risultato e' un record: le destinazioni sono i campi della sua entita' (\u00A74.4). -->\r\n <fb-field-picker\r\n [value]=\"row.field\"\r\n [object]=\"targetObject()\"\r\n label=\"Campo di destinazione\"\r\n (valueChange)=\"setOutputField(row, $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"row.field\"\r\n placeholder=\"Totale\"\r\n (input)=\"setOutputField(row, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n\r\n @if (isMap(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo di cio' che si scrive guida il controllo: su un campo o un membro Enum i valori\r\n proponibili sono quelli del suo tipo (\u00A74.6).\r\n -->\r\n <fb-value-editor\r\n [value]=\"row.action?.value ?? undefined\"\r\n label=\"Valore\"\r\n [dataType]=\"mapDataType(row)\"\r\n [objectType]=\"mapObjectType(row)\"\r\n (valueChange)=\"setMapValue(row, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationValues(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection su cui aggregare</label>\r\n <fb-reference-picker\r\n [value]=\"aggregationCollection(row.action)\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setAggregationCollection(row, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationField(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo da sommare</label>\r\n <!--\r\n Nel documento va il **nome** del campo (`stringValue`, \u00A75.13); dove il tipo della\r\n collection e' noto, il nome lo si sceglie da un elenco invece di ricordarlo.\r\n -->\r\n @if (aggregationObject(row); as object) {\r\n <fb-field-picker\r\n [value]=\"aggregationField(row.action)\"\r\n [object]=\"object\"\r\n label=\"Campo da sommare\"\r\n (valueChange)=\"setAggregationField(row, $event ?? '')\"\r\n />\r\n } @else if (aggregationClass(row); as className) {\r\n <fb-structure-member-picker\r\n [value]=\"aggregationField(row.action)\"\r\n [className]=\"className\"\r\n label=\"Membro da sommare\"\r\n (valueChange)=\"setAggregationField(row, $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"aggregationField(row.action)\"\r\n placeholder=\"Importo\"\r\n (input)=\"setAggregationField(row, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n @for (issue of issuesOf(row); track $index) {\r\n <p class=\"fb-callout fb-callout--error\">{{ issue.message }} ({{ issue.code }})</p>\r\n }\r\n } @else {\r\n <p class=\"fb-tr__hint\">\r\n Scegli il tipo del risultato: le destinazioni compaiono da l\u00EC, e la mappatura si fa\r\n trascinando una sorgente su una di esse.\r\n </p>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", styles: [":host{display:block;container-type:inline-size}.fb-tr{display:grid;grid-template-columns:240px minmax(0,1fr) 320px;gap:12px;align-items:start;margin-bottom:14px}@container (max-width: 1040px){.fb-tr{grid-template-columns:minmax(0,1fr)}}.fb-tr__panel{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-tr__map{background:var(--fb-surface, #fff)}.fb-tr__panel-title{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3);overflow-wrap:anywhere}.fb-tr__count{font-size:10px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--fb-text-muted, #6b7086)}.fb-tr__hint{margin:6px 0;font-size:11px;line-height:1.45;color:var(--fb-text-muted, #6b7086)}.fb-tr__search{margin-bottom:6px}.fb-tr__crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-bottom:6px}.fb-tr__crumb{padding:2px 4px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-tr__crumb:hover{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent)}.fb-tr__crumb-sep{color:var(--fb-text-subtle, #98a2b3);font-size:11px}.fb-tr__list{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;max-height:340px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-tr__empty{padding:8px;border:1px dashed var(--fb-border, #d6dae1);border-radius:var(--fb-radius-xs, 6px);font-size:11px;line-height:1.45;color:var(--fb-text-muted, #667085)}.fb-tr__source-row{display:flex;align-items:stretch;gap:2px}.fb-tr__source{display:flex;flex:1;flex-direction:column;gap:1px;min-width:0;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-tr__source:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-tr__source--dragging{opacity:.6}.fb-tr__source-name,.fb-tr__source-detail{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-tr__source-detail{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-tr__enter{flex:none}.fb-tr__badge{margin-left:4px;padding:0 4px;border-radius:4px;background:var(--fb-surface-sunken, #eef0f4);font-size:9px;font-weight:700;color:var(--fb-text-muted, #6b7086)}.fb-tr__badge--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-tr__result{margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-tr__collection{align-self:end;padding-bottom:6px;white-space:nowrap}.fb-tr__rows{max-height:420px;margin-bottom:8px}.fb-tr__row{display:flex;align-items:stretch;gap:2px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-tr__row--empty{border-style:dashed;background:var(--fb-surface-alt, #f7f8fa)}.fb-tr__row--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 1px var(--fb-accent, #4f6ef7) inset}.fb-tr__row--drop{border-color:var(--fb-accent, #4f6ef7);border-style:solid;background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent)}.fb-tr__row-main{display:flex;flex:1;align-items:center;gap:8px;min-width:0;padding:6px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:pointer}.fb-tr__op{display:grid;flex:none;place-items:center;width:20px;height:20px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);font-size:11px;font-weight:700;color:var(--fb-text-muted, #6b7086)}.fb-tr__row-text{display:flex;flex:1;flex-direction:column;gap:1px;min-width:0}.fb-tr__row-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:600}.fb-tr__row-src{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px;color:var(--fb-text-muted, #6b7086)}.fb-tr__row-src--empty{color:var(--fb-text-subtle, #98a2b3);font-style:italic}.fb-tr__row-type{flex:none;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-tr__row-issue{display:grid;flex:none;place-items:center;width:16px;height:16px;border-radius:50%;background:color-mix(in srgb,var(--fb-error, #dc2626) 14%,transparent);font-size:10px;font-weight:700;color:var(--fb-error, #dc2626)}.fb-tr__modes{margin-top:2px}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: StructureMemberPickerComponent, selector: "fb-structure-member-picker", inputs: ["value", "className", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14927
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: TransformInspectorComponent, isStandalone: true, selector: "fb-transform-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-tr\">\r\n <!-- ============================================================ sorgenti -->\r\n <aside class=\"fb-tr__panel\" aria-label=\"Sorgenti\">\r\n <h3 class=\"fb-tr__panel-title\">Sorgenti</h3>\r\n\r\n <input\r\n class=\"fb-input fb-tr__search\"\r\n type=\"search\"\r\n placeholder=\"Cerca una sorgente\"\r\n aria-label=\"Cerca una sorgente\"\r\n [value]=\"sourceQuery()\"\r\n (input)=\"sourceQuery.set($any($event.target).value)\"\r\n />\r\n\r\n @if (sourceCrumbs().length) {\r\n <nav class=\"fb-tr__crumbs\" aria-label=\"Percorso della sorgente\">\r\n <button type=\"button\" class=\"fb-tr__crumb\" (click)=\"enterSource('')\">Tutte</button>\r\n @for (crumb of sourceCrumbs(); track crumb.path) {\r\n <span class=\"fb-tr__crumb-sep\" aria-hidden=\"true\">\u203A</span>\r\n <button type=\"button\" class=\"fb-tr__crumb\" (click)=\"enterSource(crumb.path)\">{{ crumb.label }}</button>\r\n }\r\n </nav>\r\n }\r\n\r\n <p class=\"fb-tr__hint\">\r\n Trascina su una destinazione, oppure clicca per mapparla su quella selezionata.\r\n </p>\r\n\r\n @if (sourceError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n {{ sourceError() }} Le sorgenti si scrivono a mano nel dettaglio: l\u2019elenco non c\u2019e\u2019, i\r\n riferimenti s\u00EC.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-tr__list\">\r\n @for (source of sources(); track source.path) {\r\n <li class=\"fb-tr__source-row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-tr__source\"\r\n cdkDrag\r\n [class.fb-tr__source--dragging]=\"draggingPath() === source.path\"\r\n [title]=\"source.path + (source.detail ? ' \u00B7 ' + source.detail : '')\"\r\n (cdkDragStarted)=\"onSourceDragStarted(source)\"\r\n (cdkDragMoved)=\"onSourceDragMoved($event)\"\r\n (cdkDragEnded)=\"onSourceDragEnded($event, source)\"\r\n (click)=\"mapToSelected(source)\"\r\n >\r\n <span class=\"fb-tr__source-name\">\r\n {{ source.label }}\r\n @if (source.isCollection) {\r\n <span class=\"fb-tr__badge\" aria-label=\"collection\">[ ]</span>\r\n }\r\n </span>\r\n @if (source.detail) {\r\n <span class=\"fb-tr__source-detail\">{{ source.detail }}</span>\r\n }\r\n </button>\r\n @if (source.canEnter) {\r\n <!-- Entrare e mappare sono due gesti diversi: un record intero e' una sorgente valida. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon fb-tr__enter\"\r\n [attr.aria-label]=\"'Entra in ' + source.label\"\r\n (click)=\"enterSource(source.path)\"\r\n >\r\n \u203A\r\n </button>\r\n }\r\n </li>\r\n } @empty {\r\n @if (sourcePending()) {\r\n <li class=\"fb-tr__empty\">Caricamento\u2026</li>\r\n } @else if (sourceUndeclared()) {\r\n <li class=\"fb-tr__empty\">\r\n Il catalogo non dichiara cosa c\u2019e\u2019 qui dentro: non significa che non ci sia niente. Il\r\n percorso si scrive a mano nel dettaglio.\r\n </li>\r\n } @else {\r\n <li class=\"fb-tr__empty\">Nessuna sorgente con questo nome.</li>\r\n }\r\n }\r\n </ul>\r\n </aside>\r\n\r\n <!-- ========================================================= destinazione -->\r\n <section class=\"fb-tr__panel fb-tr__map\" aria-label=\"Destinazione\">\r\n <h3 class=\"fb-tr__panel-title\">\r\n Destinazione\r\n @if (slotCount()) {\r\n <span class=\"fb-tr__count\">{{ mappedCount() }} di {{ slotCount() }} mappate</span>\r\n } @else if (mappedCount()) {\r\n <span class=\"fb-tr__count\">{{ mappedCount() }} mappate</span>\r\n }\r\n </h3>\r\n\r\n <!-- Il tipo del risultato e' la forma della colonna: sta in cima, non in fondo. -->\r\n <div class=\"fb-tr__result\">\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().dataType || ''\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n {{ isStructureTarget() ? 'Classe del risultato' : 'Tipo dell\u2019oggetto' }}\r\n </label>\r\n @if (isStructureTarget()) {\r\n <!-- \u00A74.7: una classe del backend. Le destinazioni delle azioni sono i suoi membri. -->\r\n <fb-structure-picker\r\n [value]=\"transform().objectType\"\r\n label=\"Classe del risultato\"\r\n (valueChange)=\"setObjectType($event ?? '')\"\r\n />\r\n } @else if (transform().dataType === 'Enum') {\r\n <!-- Le enumerazioni sono un dizionario chiuso: non c'e' scrittura libera da concedere. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().objectType || ''\"\r\n (change)=\"setObjectType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"transform().objectType\"\r\n label=\"Tipo dell\u2019oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObjectType($event ?? '')\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n <label class=\"fb-check fb-tr__collection\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"transform().isCollection === true\"\r\n (change)=\"setIsCollection($any($event.target).checked)\"\r\n />\r\n Collection\r\n </label>\r\n </div>\r\n\r\n @if (!transform().dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio (TRANSFORM_DATA_TYPE_MISSING).</p>\r\n }\r\n @if (missingObjectType()) {\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n }\r\n @if (targetUndeclared()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il catalogo non dichiara i {{ isStructureTarget() ? 'membri' : 'campi' }} di\r\n <code>{{ transform().objectType }}</code>: le destinazioni si scrivono a mano, e nessuna di\r\n esse viene accusata di non esistere.\r\n </p>\r\n }\r\n </div>\r\n\r\n <ul class=\"fb-tr__list fb-tr__rows\">\r\n @for (row of targets(); track row.key) {\r\n <!--\r\n `data-fb-slot` e' cio' che il rilascio cerca: il bersaglio lo calcola `hitTest` dal DOM,\r\n non una drop list del CDK.\r\n -->\r\n <li\r\n class=\"fb-tr__row\"\r\n [attr.data-fb-slot]=\"row.key\"\r\n [class.fb-tr__row--selected]=\"isSelected(row)\"\r\n [class.fb-tr__row--drop]=\"dropKey() === row.key\"\r\n [class.fb-tr__row--empty]=\"row.index < 0\"\r\n >\r\n <button type=\"button\" class=\"fb-tr__row-main\" (click)=\"select(row.key)\">\r\n <span class=\"fb-tr__op\" [attr.title]=\"operationLabel(operationOf(row))\" aria-hidden=\"true\">\r\n {{ operationIcon(row) }}\r\n </span>\r\n <span class=\"fb-tr__row-text\">\r\n <span class=\"fb-tr__row-name\">\r\n {{ row.label }}\r\n @if (row.isCollection) {\r\n <span class=\"fb-tr__badge\" aria-label=\"collection\">[ ]</span>\r\n }\r\n @if (row.isUnknown) {\r\n <span class=\"fb-tr__badge fb-tr__badge--warn\">fuori catalogo</span>\r\n }\r\n </span>\r\n @if (row.index < 0) {\r\n <span class=\"fb-tr__row-src fb-tr__row-src--empty\">non mappata</span>\r\n } @else if (sourceSummary(row)) {\r\n <span class=\"fb-tr__row-src\">\u2190 {{ sourceSummary(row) }}</span>\r\n } @else {\r\n <span class=\"fb-tr__row-src fb-tr__row-src--empty\">sorgente da indicare</span>\r\n }\r\n </span>\r\n @if (row.dataType) {\r\n <span class=\"fb-tr__row-type\">{{ row.dataType }}</span>\r\n }\r\n @if (issuesOf(row).length) {\r\n <span class=\"fb-tr__row-issue\" [attr.title]=\"issuesOf(row)[0].message\">!</span>\r\n }\r\n </button>\r\n @if (row.index >= 0) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n [attr.aria-label]=\"'Togli la mappatura di ' + row.label\"\r\n (click)=\"removeAction(row)\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </li>\r\n } @empty {\r\n <li class=\"fb-empty\">\r\n Nessuna trasformazione: l\u2019elemento non produce nulla (TRANSFORM_WITHOUT_VALUES).\r\n </li>\r\n }\r\n </ul>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAction()\">\r\n {{ hasTargetCatalog() ? 'Aggiungi una destinazione fuori catalogo' : 'Aggiungi trasformazione' }}\r\n </button>\r\n\r\n <p class=\"fb-callout\">\r\n Il risultato e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento: si referenzia con\r\n <code>{{ name() }}</code>.\r\n </p>\r\n </section>\r\n\r\n <!-- ============================================================ dettaglio -->\r\n <aside class=\"fb-tr__panel fb-tr__detail\" aria-label=\"Dettaglio della mappatura\">\r\n @if (selected(); as row) {\r\n <h3 class=\"fb-tr__panel-title\">{{ row.label }}</h3>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <div class=\"fb-tr__modes\" role=\"group\" aria-label=\"Operazione\">\r\n @for (type of transformTypes(); track type.value) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-tr__mode\"\r\n [class.fb-tr__mode--active]=\"operationOf(row) === type.value\"\r\n (click)=\"setOperation(row, type.value)\"\r\n >\r\n {{ type.label }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n {{ isStructureTarget() ? 'Membro di destinazione' : 'Campo di destinazione' }}\r\n </label>\r\n @if (isStructureTarget()) {\r\n <!-- Solo i membri scrivibili: un membro calcolato e' TARGET_NOT_WRITABLE (\u00A74.7). -->\r\n <fb-structure-member-picker\r\n [value]=\"row.field\"\r\n [className]=\"transform().objectType\"\r\n usage=\"writable\"\r\n label=\"Membro di destinazione\"\r\n (valueChange)=\"setOutputField(row, $event ?? '')\"\r\n />\r\n } @else if (targetObject()) {\r\n <!-- Il risultato e' un record: le destinazioni sono i campi della sua entita' (\u00A74.4). -->\r\n <fb-field-picker\r\n [value]=\"row.field\"\r\n [object]=\"targetObject()\"\r\n label=\"Campo di destinazione\"\r\n (valueChange)=\"setOutputField(row, $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"row.field\"\r\n placeholder=\"Totale\"\r\n (input)=\"setOutputField(row, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n\r\n @if (isMap(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo di cio' che si scrive guida il controllo: su un campo o un membro Enum i valori\r\n proponibili sono quelli del suo tipo (\u00A74.6).\r\n -->\r\n <fb-value-editor\r\n [value]=\"row.action?.value ?? undefined\"\r\n label=\"Valore\"\r\n [dataType]=\"mapDataType(row)\"\r\n [objectType]=\"mapObjectType(row)\"\r\n (valueChange)=\"setMapValue(row, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationValues(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection su cui aggregare</label>\r\n <fb-reference-picker\r\n [value]=\"aggregationCollection(row.action)\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setAggregationCollection(row, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationField(row)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo da sommare</label>\r\n <!--\r\n Nel documento va il **nome** del campo (`stringValue`, \u00A75.13); dove il tipo della\r\n collection e' noto, il nome lo si sceglie da un elenco invece di ricordarlo.\r\n -->\r\n @if (aggregationObject(row); as object) {\r\n <fb-field-picker\r\n [value]=\"aggregationField(row.action)\"\r\n [object]=\"object\"\r\n label=\"Campo da sommare\"\r\n (valueChange)=\"setAggregationField(row, $event ?? '')\"\r\n />\r\n } @else if (aggregationClass(row); as className) {\r\n <fb-structure-member-picker\r\n [value]=\"aggregationField(row.action)\"\r\n [className]=\"className\"\r\n label=\"Membro da sommare\"\r\n (valueChange)=\"setAggregationField(row, $event ?? '')\"\r\n />\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"aggregationField(row.action)\"\r\n placeholder=\"Importo\"\r\n (input)=\"setAggregationField(row, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n @for (issue of issuesOf(row); track $index) {\r\n <p class=\"fb-callout fb-callout--error\">{{ issue.message }} ({{ issue.code }})</p>\r\n }\r\n } @else {\r\n <p class=\"fb-tr__hint\">\r\n Scegli il tipo del risultato: le destinazioni compaiono da l\u00EC, e la mappatura si fa\r\n trascinando una sorgente su una di esse.\r\n </p>\r\n }\r\n </aside>\r\n</div>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", styles: [":host{display:block;container-type:inline-size}.fb-tr{display:grid;grid-template-columns:240px minmax(0,1fr) 320px;gap:12px;align-items:start;margin-bottom:14px}@container (max-width: 1040px){.fb-tr{grid-template-columns:minmax(0,1fr)}}.fb-tr__panel{min-width:0;padding:10px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface-alt, #f7f8fa)}.fb-tr__map{background:var(--fb-surface, #fff)}.fb-tr__panel-title{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3);overflow-wrap:anywhere}.fb-tr__count{font-size:10px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--fb-text-muted, #6b7086)}.fb-tr__hint{margin:6px 0;font-size:11px;line-height:1.45;color:var(--fb-text-muted, #6b7086)}.fb-tr__search{margin-bottom:6px}.fb-tr__crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-bottom:6px}.fb-tr__crumb{padding:2px 4px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-tr__crumb:hover{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 10%,transparent)}.fb-tr__crumb-sep{color:var(--fb-text-subtle, #98a2b3);font-size:11px}.fb-tr__list{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;max-height:340px;overflow-y:auto;overscroll-behavior:contain;list-style:none}.fb-tr__empty{padding:8px;border:1px dashed var(--fb-border, #d6dae1);border-radius:var(--fb-radius-xs, 6px);font-size:11px;line-height:1.45;color:var(--fb-text-muted, #667085)}.fb-tr__source-row{display:flex;align-items:stretch;gap:2px}.fb-tr__source{display:flex;flex:1;flex-direction:column;gap:1px;min-width:0;padding:5px 8px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff);color:var(--fb-text, #1a1c23);font:inherit;font-size:12px;text-align:left;cursor:grab}.fb-tr__source:hover{border-color:var(--fb-accent, #4f6ef7)}.fb-tr__source--dragging{opacity:.6}.fb-tr__source-name,.fb-tr__source-detail{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-tr__source-detail{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-tr__enter{flex:none}.fb-tr__badge{margin-left:4px;padding:0 4px;border-radius:4px;background:var(--fb-surface-sunken, #eef0f4);font-size:9px;font-weight:700;color:var(--fb-text-muted, #6b7086)}.fb-tr__badge--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-tr__result{margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-tr__collection{align-self:end;padding-bottom:6px;white-space:nowrap}.fb-tr__rows{max-height:420px;margin-bottom:8px}.fb-tr__row{display:flex;align-items:stretch;gap:2px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-xs, 6px);background:var(--fb-surface, #fff)}.fb-tr__row--empty{border-style:dashed;background:var(--fb-surface-alt, #f7f8fa)}.fb-tr__row--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 1px var(--fb-accent, #4f6ef7) inset}.fb-tr__row--drop{border-color:var(--fb-accent, #4f6ef7);border-style:solid;background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent)}.fb-tr__row-main{display:flex;flex:1;align-items:center;gap:8px;min-width:0;padding:6px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:pointer}.fb-tr__op{display:grid;flex:none;place-items:center;width:20px;height:20px;border-radius:50%;background:var(--fb-surface-sunken, #eef0f4);font-size:11px;font-weight:700;color:var(--fb-text-muted, #6b7086)}.fb-tr__row-text{display:flex;flex:1;flex-direction:column;gap:1px;min-width:0}.fb-tr__row-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:600}.fb-tr__row-src{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px;color:var(--fb-text-muted, #6b7086)}.fb-tr__row-src--empty{color:var(--fb-text-subtle, #98a2b3);font-style:italic}.fb-tr__row-type{flex:none;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-tr__row-issue{display:grid;flex:none;place-items:center;width:16px;height:16px;border-radius:50%;background:color-mix(in srgb,var(--fb-error, #dc2626) 14%,transparent);font-size:10px;font-weight:700;color:var(--fb-error, #dc2626)}.fb-tr__modes{margin-top:2px}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: StructureMemberPickerComponent, selector: "fb-structure-member-picker", inputs: ["value", "className", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14374
14928
|
}
|
|
14375
14929
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: TransformInspectorComponent, decorators: [{
|
|
14376
14930
|
type: Component,
|
|
@@ -14495,7 +15049,7 @@ class WaitInspectorComponent extends NodeInspectorBase {
|
|
|
14495
15049
|
this.setField('defaultConnectorLabel', label);
|
|
14496
15050
|
}
|
|
14497
15051
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: WaitInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14498
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: WaitInspectorComponent, isStandalone: true, selector: "fb-wait-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-list\">\r\n @for (event of events(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveEvent($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveEvent($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019evento\"\r\n (click)=\"removeEvent($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"event.label || ''\"\r\n placeholder=\"Alla scadenza\"\r\n (input)=\"setEventLabel($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"event.name || ''\"\r\n (input)=\"setEventName($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"event.eventType || ''\"\r\n (change)=\"setEventType($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of eventTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n @for (entry of catalogEventOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!event.eventType) {\r\n <p class=\"fb-field__error\">Senza tipo l\u2019evento non e\u2019 valido (WAIT_EVENT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"event\"\r\n inputTitle=\"Parametri dell\u2019evento\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dall\u2019evento\"\r\n (changed)=\"onEventParametersChanged($index, $event)\"\r\n />\r\n\r\n <fb-condition-editor\r\n [holder]=\"event\"\r\n title=\"Condizioni dell\u2019evento\"\r\n (changed)=\"onEventConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un evento e\u2019 obbligatorio: un Wait senza eventi non e\u2019 eseguibile (WAIT_WITHOUT_EVENTS).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addEvent()\">Aggiungi evento</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessun evento si verifica</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"wait().defaultConnectorLabel || ''\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il ramo percorso quando le condizioni di <strong>tutti</strong> gli eventi sono false.\r\n </p>\r\n </div>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15052
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: WaitInspectorComponent, isStandalone: true, selector: "fb-wait-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-list\">\r\n @for (event of events(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveEvent($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveEvent($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019evento\"\r\n (click)=\"removeEvent($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"event.label || ''\"\r\n placeholder=\"Alla scadenza\"\r\n (input)=\"setEventLabel($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"event.name || ''\"\r\n (input)=\"setEventName($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"event.eventType || ''\"\r\n (change)=\"setEventType($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of eventTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n @for (entry of catalogEventOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!event.eventType) {\r\n <p class=\"fb-field__error\">Senza tipo l\u2019evento non e\u2019 valido (WAIT_EVENT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"event\"\r\n inputTitle=\"Parametri dell\u2019evento\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dall\u2019evento\"\r\n (changed)=\"onEventParametersChanged($index, $event)\"\r\n />\r\n\r\n <fb-condition-editor\r\n [holder]=\"event\"\r\n title=\"Condizioni dell\u2019evento\"\r\n (changed)=\"onEventConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un evento e\u2019 obbligatorio: un Wait senza eventi non e\u2019 eseguibile (WAIT_WITHOUT_EVENTS).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addEvent()\">Aggiungi evento</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessun evento si verifica</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"wait().defaultConnectorLabel || ''\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il ramo percorso quando le condizioni di <strong>tutti</strong> gli eventi sono false.\r\n </p>\r\n </div>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "preferredRoots", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason", "extraTargets", "preferredRoots"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14499
15053
|
}
|
|
14500
15054
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: WaitInspectorComponent, decorators: [{
|
|
14501
15055
|
type: Component,
|
|
@@ -14734,17 +15288,28 @@ class ElementInspectorComponent {
|
|
|
14734
15288
|
* Quanti riferimenti verrebbero riscritti dalla rinomina. Serve a rendere visibile che
|
|
14735
15289
|
* l'operazione tocca il resto del documento, non solo questo elemento.
|
|
14736
15290
|
*/
|
|
14737
|
-
|
|
15291
|
+
/**
|
|
15292
|
+
* Gli usi del nome, con la stessa funzione del pannello risorse: e' la stessa domanda, e due
|
|
15293
|
+
* numeri diversi per la stessa domanda sono un difetto — questo era una regex sul JSON
|
|
15294
|
+
* serializzato, che contava le stringhe uguali al nome in **qualunque** campo (anche dove non
|
|
15295
|
+
* sono riferimenti) e non vedeva ne' le formule ne' i merge field, cioe' sovrastimava e
|
|
15296
|
+
* sottostimava insieme.
|
|
15297
|
+
*/
|
|
15298
|
+
nameUses = computed(() => {
|
|
14738
15299
|
const name = this.selectedName();
|
|
14739
15300
|
if (!name || this.isStart()) {
|
|
14740
|
-
return 0;
|
|
14741
|
-
}
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14747
|
-
|
|
15301
|
+
return { references: 0, formulas: 0, mergeFields: 0, total: 0 };
|
|
15302
|
+
}
|
|
15303
|
+
return describeNameUses(this.store.document(), name);
|
|
15304
|
+
}, ...(ngDevMode ? [{ debugName: "nameUses" }] : []));
|
|
15305
|
+
/** Le occorrenze che la rinomina **riscrive**: riferimenti piu' merge field nei testi. */
|
|
15306
|
+
referenceCount = computed(() => this.nameUses().references + this.nameUses().mergeFields, ...(ngDevMode ? [{ debugName: "referenceCount" }] : []));
|
|
15307
|
+
/**
|
|
15308
|
+
* Le occorrenze che restano indietro: le espressioni delle formule, che l'editor non riscrive
|
|
15309
|
+
* perche' la loro sintassi e' del motore (§6.3). Dirlo qui e' l'unico modo di non farlo
|
|
15310
|
+
* scoprire dopo, quando la formula punta a un nome che non esiste piu'.
|
|
15311
|
+
*/
|
|
15312
|
+
formulaUseCount = computed(() => this.nameUses().formulas, ...(ngDevMode ? [{ debugName: "formulaUseCount" }] : []));
|
|
14748
15313
|
canRename = computed(() => {
|
|
14749
15314
|
const pending = this.pendingName();
|
|
14750
15315
|
return pending !== null && pending !== this.selectedName() && !this.nameError();
|
|
@@ -14831,7 +15396,7 @@ class ElementInspectorComponent {
|
|
|
14831
15396
|
return { x: node?.locationX ?? 0, y: node?.locationY ?? 0 };
|
|
14832
15397
|
}, ...(ngDevMode ? [{ debugName: "position" }] : []));
|
|
14833
15398
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ElementInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14834
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ElementInspectorComponent, isStandalone: true, selector: "fb-element-inspector", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, initialFieldPath: { classPropertyName: "initialFieldPath", publicName: "initialFieldPath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested", renamed: "renamed" }, viewQueries: [{ propertyName: "labelInput", first: true, predicate: ["labelInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of visibleIssues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n @if (hiddenIssueCount() > 0 || issuesExpanded()) {\r\n <!-- Sei rilievi in cima riempivano la finestra prima del primo campo: si chiedono. -->\r\n <button type=\"button\" class=\"fb-inspector__issues-more\" (click)=\"toggleIssues()\">\r\n @if (issuesExpanded()) {\r\n Mostra meno\r\n } @else {\r\n Altri {{ hiddenIssueCount() }} rilievi su questo elemento\r\n }\r\n </button>\r\n }\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n #labelInput\r\n class=\"fb-input\"\r\n [value]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n <!--\r\n Elemento appena creato: il nome non e\u2019 ancora niente, si costruisce dall\u2019etichetta e\r\n si puo\u2019 scrivere a mano. Su un elemento gi\u00E0 nominato, invece, cambiare il nome riscrive\r\n i riferimenti di tutto il documento (\u00A713.8) e sta dietro il comando \u00ABRinomina\u00BB.\r\n -->\r\n @if (isComposingName()) {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!composedNameError()\"\r\n [value]=\"composedName()\"\r\n placeholder=\"si genera dall\u2019etichetta\"\r\n aria-label=\"Nome tecnico\"\r\n (input)=\"onComposedNameInput($any($event.target).value)\"\r\n />\r\n @if (composedNameError()) {\r\n <p class=\"fb-field__error\">{{ composedNameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento: si costruisce\r\n dall\u2019etichetta finch\u00E9 non lo si scrive a mano, e svuotandolo torna a seguirla.\r\n @if (!composedName()) {\r\n Finch\u00E9 resta vuoto vale \u00AB{{ selectedName() }}\u00BB.\r\n }\r\n </p>\r\n }\r\n } @else if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\r\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('DynamicScreen') {\r\n <fb-dynamic-screen-inspector\r\n [name]=\"selectedName()!\"\r\n [node]=\"node()!\"\r\n [initialSelection]=\"initialFieldPath()\"\r\n />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__issues-more{display:block;margin:-4px 0 10px;padding:2px 0;border:0;background:none;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-inspector__issues-more:hover{text-decoration:underline}\n"], dependencies: [{ kind: "component", type: ActionCallInspectorComponent, selector: "fb-action-call-inspector" }, { kind: "component", type: AssignmentInspectorComponent, selector: "fb-assignment-inspector" }, { kind: "component", type: CollectionProcessorInspectorComponent, selector: "fb-collection-processor-inspector" }, { kind: "component", type: CustomErrorInspectorComponent, selector: "fb-custom-error-inspector" }, { kind: "component", type: DecisionInspectorComponent, selector: "fb-decision-inspector" }, { kind: "component", type: DynamicScreenInspectorComponent, selector: "fb-dynamic-screen-inspector", inputs: ["initialSelection"] }, { kind: "component", type: LoopInspectorComponent, selector: "fb-loop-inspector" }, { kind: "component", type: OrchestratedStageInspectorComponent, selector: "fb-orchestrated-stage-inspector" }, { kind: "component", type: RecordLookupInspectorComponent, selector: "fb-record-lookup-inspector" }, { kind: "component", type: RecordRollbackInspectorComponent, selector: "fb-record-rollback-inspector" }, { kind: "component", type: RecordWriteInspectorComponent, selector: "fb-record-write-inspector", inputs: ["type"] }, { kind: "component", type: ScreenInspectorComponent, selector: "fb-screen-inspector" }, { kind: "component", type: ScriptCallInspectorComponent, selector: "fb-script-call-inspector" }, { kind: "component", type: StartInspectorComponent, selector: "fb-start-inspector" }, { kind: "component", type: SubflowInspectorComponent, selector: "fb-subflow-inspector" }, { kind: "component", type: TransformInspectorComponent, selector: "fb-transform-inspector" }, { kind: "component", type: WaitInspectorComponent, selector: "fb-wait-inspector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15399
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ElementInspectorComponent, isStandalone: true, selector: "fb-element-inspector", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, initialFieldPath: { classPropertyName: "initialFieldPath", publicName: "initialFieldPath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested", renamed: "renamed" }, viewQueries: [{ propertyName: "labelInput", first: true, predicate: ["labelInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of visibleIssues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n @if (hiddenIssueCount() > 0 || issuesExpanded()) {\r\n <!-- Sei rilievi in cima riempivano la finestra prima del primo campo: si chiedono. -->\r\n <button type=\"button\" class=\"fb-inspector__issues-more\" (click)=\"toggleIssues()\">\r\n @if (issuesExpanded()) {\r\n Mostra meno\r\n } @else {\r\n Altri {{ hiddenIssueCount() }} rilievi su questo elemento\r\n }\r\n </button>\r\n }\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n #labelInput\r\n class=\"fb-input\"\r\n [value]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n <!--\r\n Elemento appena creato: il nome non e\u2019 ancora niente, si costruisce dall\u2019etichetta e\r\n si puo\u2019 scrivere a mano. Su un elemento gi\u00E0 nominato, invece, cambiare il nome riscrive\r\n i riferimenti di tutto il documento (\u00A713.8) e sta dietro il comando \u00ABRinomina\u00BB.\r\n -->\r\n @if (isComposingName()) {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!composedNameError()\"\r\n [value]=\"composedName()\"\r\n placeholder=\"si genera dall\u2019etichetta\"\r\n aria-label=\"Nome tecnico\"\r\n (input)=\"onComposedNameInput($any($event.target).value)\"\r\n />\r\n @if (composedNameError()) {\r\n <p class=\"fb-field__error\">{{ composedNameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento: si costruisce\r\n dall\u2019etichetta finch\u00E9 non lo si scrive a mano, e svuotandolo torna a seguirla.\r\n @if (!composedName()) {\r\n Finch\u00E9 resta vuoto vale \u00AB{{ selectedName() }}\u00BB.\r\n }\r\n </p>\r\n }\r\n } @else if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive i riferimenti che puntano a questo elemento e i merge field\r\n <code>{!\u2026}</code> nei testi ({{ referenceCount() }} occorrenze): nessuna\r\n primitiva del backend lo fa, lo fa l\u2019editor.\r\n <!--\r\n Le formule restano indietro di proposito: la loro sintassi e\u2019 del motore e\r\n riscriverci dentro vorrebbe dire indovinare. Il numero va detto **qui**, perche\u2019\r\n e\u2019 l\u2019unico momento in cui si puo\u2019 correggerle sapendo cosa cercare.\r\n -->\r\n @if (formulaUseCount()) {\r\n Le {{ formulaUseCount() }} occorrenze dentro le formule <strong>no</strong>: la\r\n sintassi e\u2019 del motore di regole, e vanno corrette a mano.\r\n }\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('DynamicScreen') {\r\n <fb-dynamic-screen-inspector\r\n [name]=\"selectedName()!\"\r\n [node]=\"node()!\"\r\n [initialSelection]=\"initialFieldPath()\"\r\n />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__issues-more{display:block;margin:-4px 0 10px;padding:2px 0;border:0;background:none;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-inspector__issues-more:hover{text-decoration:underline}\n"], dependencies: [{ kind: "component", type: ActionCallInspectorComponent, selector: "fb-action-call-inspector" }, { kind: "component", type: AssignmentInspectorComponent, selector: "fb-assignment-inspector" }, { kind: "component", type: CollectionProcessorInspectorComponent, selector: "fb-collection-processor-inspector" }, { kind: "component", type: CustomErrorInspectorComponent, selector: "fb-custom-error-inspector" }, { kind: "component", type: DecisionInspectorComponent, selector: "fb-decision-inspector" }, { kind: "component", type: DynamicScreenInspectorComponent, selector: "fb-dynamic-screen-inspector", inputs: ["initialSelection"] }, { kind: "component", type: LoopInspectorComponent, selector: "fb-loop-inspector" }, { kind: "component", type: OrchestratedStageInspectorComponent, selector: "fb-orchestrated-stage-inspector" }, { kind: "component", type: RecordLookupInspectorComponent, selector: "fb-record-lookup-inspector" }, { kind: "component", type: RecordRollbackInspectorComponent, selector: "fb-record-rollback-inspector" }, { kind: "component", type: RecordWriteInspectorComponent, selector: "fb-record-write-inspector", inputs: ["type"] }, { kind: "component", type: ScreenInspectorComponent, selector: "fb-screen-inspector" }, { kind: "component", type: ScriptCallInspectorComponent, selector: "fb-script-call-inspector" }, { kind: "component", type: StartInspectorComponent, selector: "fb-start-inspector" }, { kind: "component", type: SubflowInspectorComponent, selector: "fb-subflow-inspector" }, { kind: "component", type: TransformInspectorComponent, selector: "fb-transform-inspector" }, { kind: "component", type: WaitInspectorComponent, selector: "fb-wait-inspector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
14835
15400
|
}
|
|
14836
15401
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ElementInspectorComponent, decorators: [{
|
|
14837
15402
|
type: Component,
|
|
@@ -14853,7 +15418,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
14853
15418
|
SubflowInspectorComponent,
|
|
14854
15419
|
TransformInspectorComponent,
|
|
14855
15420
|
WaitInspectorComponent,
|
|
14856
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of visibleIssues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n @if (hiddenIssueCount() > 0 || issuesExpanded()) {\r\n <!-- Sei rilievi in cima riempivano la finestra prima del primo campo: si chiedono. -->\r\n <button type=\"button\" class=\"fb-inspector__issues-more\" (click)=\"toggleIssues()\">\r\n @if (issuesExpanded()) {\r\n Mostra meno\r\n } @else {\r\n Altri {{ hiddenIssueCount() }} rilievi su questo elemento\r\n }\r\n </button>\r\n }\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n #labelInput\r\n class=\"fb-input\"\r\n [value]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n <!--\r\n Elemento appena creato: il nome non e\u2019 ancora niente, si costruisce dall\u2019etichetta e\r\n si puo\u2019 scrivere a mano. Su un elemento gi\u00E0 nominato, invece, cambiare il nome riscrive\r\n i riferimenti di tutto il documento (\u00A713.8) e sta dietro il comando \u00ABRinomina\u00BB.\r\n -->\r\n @if (isComposingName()) {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!composedNameError()\"\r\n [value]=\"composedName()\"\r\n placeholder=\"si genera dall\u2019etichetta\"\r\n aria-label=\"Nome tecnico\"\r\n (input)=\"onComposedNameInput($any($event.target).value)\"\r\n />\r\n @if (composedNameError()) {\r\n <p class=\"fb-field__error\">{{ composedNameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento: si costruisce\r\n dall\u2019etichetta finch\u00E9 non lo si scrive a mano, e svuotandolo torna a seguirla.\r\n @if (!composedName()) {\r\n Finch\u00E9 resta vuoto vale \u00AB{{ selectedName() }}\u00BB.\r\n }\r\n </p>\r\n }\r\n } @else if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\r\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('DynamicScreen') {\r\n <fb-dynamic-screen-inspector\r\n [name]=\"selectedName()!\"\r\n [node]=\"node()!\"\r\n [initialSelection]=\"initialFieldPath()\"\r\n />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__issues-more{display:block;margin:-4px 0 10px;padding:2px 0;border:0;background:none;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-inspector__issues-more:hover{text-decoration:underline}\n"] }]
|
|
15421
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of visibleIssues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n @if (hiddenIssueCount() > 0 || issuesExpanded()) {\r\n <!-- Sei rilievi in cima riempivano la finestra prima del primo campo: si chiedono. -->\r\n <button type=\"button\" class=\"fb-inspector__issues-more\" (click)=\"toggleIssues()\">\r\n @if (issuesExpanded()) {\r\n Mostra meno\r\n } @else {\r\n Altri {{ hiddenIssueCount() }} rilievi su questo elemento\r\n }\r\n </button>\r\n }\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n #labelInput\r\n class=\"fb-input\"\r\n [value]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n <!--\r\n Elemento appena creato: il nome non e\u2019 ancora niente, si costruisce dall\u2019etichetta e\r\n si puo\u2019 scrivere a mano. Su un elemento gi\u00E0 nominato, invece, cambiare il nome riscrive\r\n i riferimenti di tutto il documento (\u00A713.8) e sta dietro il comando \u00ABRinomina\u00BB.\r\n -->\r\n @if (isComposingName()) {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!composedNameError()\"\r\n [value]=\"composedName()\"\r\n placeholder=\"si genera dall\u2019etichetta\"\r\n aria-label=\"Nome tecnico\"\r\n (input)=\"onComposedNameInput($any($event.target).value)\"\r\n />\r\n @if (composedNameError()) {\r\n <p class=\"fb-field__error\">{{ composedNameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento: si costruisce\r\n dall\u2019etichetta finch\u00E9 non lo si scrive a mano, e svuotandolo torna a seguirla.\r\n @if (!composedName()) {\r\n Finch\u00E9 resta vuoto vale \u00AB{{ selectedName() }}\u00BB.\r\n }\r\n </p>\r\n }\r\n } @else if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive i riferimenti che puntano a questo elemento e i merge field\r\n <code>{!\u2026}</code> nei testi ({{ referenceCount() }} occorrenze): nessuna\r\n primitiva del backend lo fa, lo fa l\u2019editor.\r\n <!--\r\n Le formule restano indietro di proposito: la loro sintassi e\u2019 del motore e\r\n riscriverci dentro vorrebbe dire indovinare. Il numero va detto **qui**, perche\u2019\r\n e\u2019 l\u2019unico momento in cui si puo\u2019 correggerle sapendo cosa cercare.\r\n -->\r\n @if (formulaUseCount()) {\r\n Le {{ formulaUseCount() }} occorrenze dentro le formule <strong>no</strong>: la\r\n sintassi e\u2019 del motore di regole, e vanno corrette a mano.\r\n }\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('DynamicScreen') {\r\n <fb-dynamic-screen-inspector\r\n [name]=\"selectedName()!\"\r\n [node]=\"node()!\"\r\n [initialSelection]=\"initialFieldPath()\"\r\n />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__issues-more{display:block;margin:-4px 0 10px;padding:2px 0;border:0;background:none;color:var(--fb-accent, #4f6ef7);font:inherit;font-size:11px;cursor:pointer}.fb-inspector__issues-more:hover{text-decoration:underline}\n"] }]
|
|
14857
15422
|
}], ctorParameters: () => [], propDecorators: { labelInput: [{ type: i0.ViewChild, args: ['labelInput', { isSignal: true }] }], selectedName: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedName", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], initialFieldPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFieldPath", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], removeRequested: [{ type: i0.Output, args: ["removeRequested"] }], duplicateRequested: [{ type: i0.Output, args: ["duplicateRequested"] }], renamed: [{ type: i0.Output, args: ["renamed"] }] } });
|
|
14858
15423
|
|
|
14859
15424
|
/**
|
|
@@ -15080,100 +15645,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
15080
15645
|
args: [{ selector: 'fb-paste-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-dialog__backdrop\" (click)=\"cancel()\"></div>\r\n\r\n<div\r\n class=\"fb-dialog__panel fb-paste\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label=\"Incolla elementi\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown.escape)=\"cancel()\"\r\n>\r\n <header class=\"fb-dialog__head\">\r\n <div class=\"fb-dialog__identity\">\r\n <span class=\"fb-dialog__type\">Appunti</span>\r\n <h2 class=\"fb-dialog__title\">Incolla {{ elementLabel() }}</h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"cancel()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"fb-dialog__body fb-paste__body\">\r\n @if (payload().flowName) {\r\n <p class=\"fb-paste__origin\">Copiati da <strong>{{ payload().flowName }}</strong>.</p>\r\n }\r\n\r\n <ul class=\"fb-paste__elements\">\r\n @for (entry of payload().nodes; track $index) {\r\n <li>{{ entry.node.label || entry.node.name }} <span class=\"fb-paste__tech\"> {{ entry.node.name }}</span></li>\r\n }\r\n </ul>\r\n\r\n @if (plan().processTypeMismatch) {\r\n <!--\r\n Le globali dipendono dal `processType` con cui le si chiede: `$Record` non esiste in uno\r\n screen flow (\u00A74.1). Non blocca \u2014 il documento e' comunque salvabile \u2014 ma un riferimento\r\n che di la' era valido puo' non esserlo qui, e dirlo prima evita la caccia al rilievo.\r\n -->\r\n <p class=\"fb-paste__warn\">\r\n Il flow di origine e\u2019 di tipo <strong>{{ plan().processTypeMismatch }}</strong>: le variabili globali\r\n disponibili qui sono altre, e i riferimenti che le usano potrebbero non essere validi.\r\n </p>\r\n }\r\n\r\n @if (nodeRenames().length || innerRenames().length) {\r\n <section class=\"fb-paste__section\">\r\n <h3 class=\"fb-paste__title\">Nomi gi\u00E0 usati, quindi cambiati</h3>\r\n <p class=\"fb-field__hint\">\r\n Elementi, risorse, step, campi e screen action condividono un unico spazio di nomi: un omonimo\r\n sarebbe <code>NAME_DUPLICATED</code>. I riferimenti interni al blocco seguono il nome nuovo.\r\n </p>\r\n <ul class=\"fb-paste__list\">\r\n @for (rename of nodeRenames(); track $index) {\r\n <li><code>{{ rename.from }}</code> \u2192 <code>{{ rename.to }}</code></li>\r\n }\r\n @for (rename of innerRenames(); track $index) {\r\n <li><code>{{ rename.from }}</code> \u2192 <code>{{ rename.to }}</code> <span class=\"fb-paste__tech\"> interno</span></li>\r\n }\r\n </ul>\r\n </section>\r\n }\r\n\r\n @if (newResources().length) {\r\n <section class=\"fb-paste__section\">\r\n <h3 class=\"fb-paste__title\">Risorse che qui non esistono</h3>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"createMissingResources()\"\r\n (change)=\"setCreateMissingResources($any($event.target).checked)\"\r\n />\r\n <span>Crearle insieme agli elementi</span>\r\n </label>\r\n <ul class=\"fb-paste__list\">\r\n @for (resource of newResources(); track $index) {\r\n <li><code>{{ resource.name }}</code> <span class=\"fb-paste__tech\"> {{ resourceLabel(resource.collection) }}</span></li>\r\n }\r\n </ul>\r\n @if (!createMissingResources()) {\r\n <p class=\"fb-paste__warn\">\r\n Senza di esse i riferimenti resteranno da ricollegare: il pannello dei problemi li segnalera\u2019\r\n come <code>REFERENCE_UNKNOWN</code>.\r\n </p>\r\n }\r\n </section>\r\n }\r\n\r\n @if (reusedResources().length) {\r\n <section class=\"fb-paste__section\">\r\n <h3 class=\"fb-paste__title\">Risorse gi\u00E0 presenti</h3>\r\n <p class=\"fb-field__hint\">\r\n Un nome uguale esiste gi\u00E0 in questo flow: si usa quello, non se ne crea una copia. Controlla che\r\n sia davvero la stessa cosa \u2014 il confronto e\u2019 sul nome, non sul contenuto.\r\n </p>\r\n <ul class=\"fb-paste__list\">\r\n @for (resource of reusedResources(); track $index) {\r\n <li><code>{{ resource.name }}</code></li>\r\n }\r\n </ul>\r\n </section>\r\n }\r\n\r\n @if (plan().unresolved.length) {\r\n <section class=\"fb-paste__section\">\r\n <h3 class=\"fb-paste__title\">Riferimenti che restano da sistemare</h3>\r\n <p class=\"fb-field__hint\">\r\n Puntano a qualcosa che in questo flow non c\u2019e\u2019 \u2014 quasi sempre un elemento rimasto nel flow di\r\n origine. L\u2019incolla non puo\u2019 portarseli dietro: vanno ricollegati a mano.\r\n </p>\r\n <ul class=\"fb-paste__list\">\r\n @for (name of plan().unresolved; track $index) {\r\n <li><code>{{ name }}</code></li>\r\n }\r\n </ul>\r\n </section>\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-dialog__foot\">\r\n <span class=\"fb-dialog__note\">Un solo passo di annulla riporta tutto com\u2019era.</span>\r\n <div class=\"fb-paste__actions\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"cancel()\">Annulla</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"confirm()\">Incolla</button>\r\n </div>\r\n </footer>\r\n</div>\r\n", styles: [":host{position:absolute;inset:0;z-index:40;display:grid;place-items:center;padding:24px}.fb-dialog__backdrop{position:absolute;inset:0;background:#10182852;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.fb-dialog__panel{position:relative;display:flex;flex-direction:column;width:min(560px,100%);max-height:min(720px,100%);border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-lg, 0 18px 44px rgb(16 24 40 / 18%));outline:none;overflow:hidden}.fb-dialog__head,.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px}.fb-dialog__head{border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__foot{border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__type{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-dialog__title{margin:0;font-size:15px;font-weight:600}.fb-dialog__note{color:var(--fb-text-muted, #6b7086);font-size:12px}.fb-dialog__body{display:flex;flex-direction:column;gap:14px;padding:14px;overflow:auto}.fb-paste__origin{margin:0;color:var(--fb-text-muted, #6b7086);font-size:12px}.fb-paste__elements,.fb-paste__list{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:4px;font-size:13px}.fb-paste__section{display:flex;flex-direction:column;gap:6px;padding-top:12px;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-paste__title{margin:0;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-paste__tech{color:var(--fb-text-muted, #6b7086);font-size:11px}.fb-paste__warn{margin:0;padding:8px 10px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-warning, #b7791f);font-size:12px}.fb-paste__actions{display:flex;gap:8px}\n"] }]
|
|
15081
15646
|
}], propDecorators: { payload: [{ type: i0.Input, args: [{ isSignal: true, alias: "payload", required: true }] }], plan: [{ type: i0.Input, args: [{ isSignal: true, alias: "plan", required: true }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
|
|
15082
15647
|
|
|
15083
|
-
/**
|
|
15084
|
-
* Quante volte un nome e' **usato** nel documento.
|
|
15085
|
-
*
|
|
15086
|
-
* Serve dove togliere qualcosa rompe il resto: cancellare una risorsa non e' come cancellare
|
|
15087
|
-
* un elemento — nessun arco sparisce dal canvas, nessun rilievo compare finche' non si valida,
|
|
15088
|
-
* e i riferimenti orfani si scoprono dopo. Il conteggio e' cio' che rende visibile il danno
|
|
15089
|
-
* **prima**.
|
|
15090
|
-
*
|
|
15091
|
-
* Due regole, e sono le stesse della rinomina:
|
|
15092
|
-
*
|
|
15093
|
-
* - i riferimenti stanno nei campi di {@link FLOW_REFERENCE_FIELDS} — l'elenco e' **uno**, e
|
|
15094
|
-
* questo modulo lo importa invece di riscriverlo: due elenchi diventano due elenchi diversi
|
|
15095
|
-
* al primo campo aggiunto (§13.8);
|
|
15096
|
-
* - un riferimento vale anche **navigato**: `Ordine.Numero` e' un uso di `Ordine`.
|
|
15097
|
-
*
|
|
15098
|
-
* Piu' una che la rinomina non ha: le **formule**. Il motore di regole cita le risorse per
|
|
15099
|
-
* nome dentro un'espressione (`GetStreetToponym(Residenza_Street)`), quindi un conteggio che
|
|
15100
|
-
* guardasse solo i campi di riferimento direbbe «non usata» di una variabile che tre formule
|
|
15101
|
-
* leggono. L'espressione non la interpretiamo — la sintassi e' del motore (§6.3) — quindi si
|
|
15102
|
-
* spezza in identificatori e si confronta la radice: e' un'approssimazione, ed e' per questo
|
|
15103
|
-
* che il risultato serve ad **avvertire**, non a decidere al posto dell'utente.
|
|
15104
|
-
*/
|
|
15105
|
-
/**
|
|
15106
|
-
* I campi che contengono un'espressione del motore di regole. `expression` e' quello di una
|
|
15107
|
-
* risorsa `Formula` (§4.6), `formulaExpression` quello di un valore e di una validation rule
|
|
15108
|
-
* (§5.2), `filterFormula` quello di un filtro di record (§5.8).
|
|
15109
|
-
*/
|
|
15110
|
-
const FLOW_FORMULA_FIELDS = new Set([
|
|
15111
|
-
'expression',
|
|
15112
|
-
'formulaExpression',
|
|
15113
|
-
'filterFormula',
|
|
15114
|
-
]);
|
|
15115
|
-
/** `true` se la stringa e' quel nome, o un percorso che parte da quel nome. */
|
|
15116
|
-
function isUseOf(value, name) {
|
|
15117
|
-
return value === name || value.startsWith(`${name}.`);
|
|
15118
|
-
}
|
|
15119
|
-
/**
|
|
15120
|
-
* Gli identificatori citati da un'espressione, ridotti alla loro **radice**: di
|
|
15121
|
-
* `OCRCIE.TaxCode` conta `OCRCIE`, perche' e' quella la risorsa. Senza la riduzione, un
|
|
15122
|
-
* membro chiamato come una variabile la farebbe sembrare usata.
|
|
15123
|
-
*/
|
|
15124
|
-
function rootsIn(expression) {
|
|
15125
|
-
return expression
|
|
15126
|
-
.split(/[^A-Za-z0-9_.$]+/)
|
|
15127
|
-
.filter(Boolean)
|
|
15128
|
-
.map((token) => token.split('.')[0])
|
|
15129
|
-
.filter(Boolean);
|
|
15130
|
-
}
|
|
15131
|
-
/**
|
|
15132
|
-
* Quante volte `name` compare come riferimento nel documento (o in un frammento).
|
|
15133
|
-
*
|
|
15134
|
-
* Non conta la **dichiarazione**: il campo `name` della risorsa o dell'elemento non e' un
|
|
15135
|
-
* campo di riferimento, quindi resta fuori da se'. E' la differenza con il conteggio della
|
|
15136
|
-
* rinomina, che serializza il documento e cerca la stringa: lì un elemento mai referenziato
|
|
15137
|
-
* «compare 1 volta».
|
|
15138
|
-
*/
|
|
15139
|
-
function countNameUses(root, name) {
|
|
15140
|
-
if (!name) {
|
|
15141
|
-
return 0;
|
|
15142
|
-
}
|
|
15143
|
-
let count = 0;
|
|
15144
|
-
const walk = (value) => {
|
|
15145
|
-
if (Array.isArray(value)) {
|
|
15146
|
-
value.forEach(walk);
|
|
15147
|
-
return;
|
|
15148
|
-
}
|
|
15149
|
-
if (!value || typeof value !== 'object') {
|
|
15150
|
-
return;
|
|
15151
|
-
}
|
|
15152
|
-
for (const [key, child] of Object.entries(value)) {
|
|
15153
|
-
if (typeof child === 'string') {
|
|
15154
|
-
if (FLOW_REFERENCE_FIELDS.has(key)) {
|
|
15155
|
-
if (isUseOf(child, name)) {
|
|
15156
|
-
count++;
|
|
15157
|
-
}
|
|
15158
|
-
}
|
|
15159
|
-
else if (FLOW_FORMULA_FIELDS.has(key)) {
|
|
15160
|
-
count += rootsIn(child).filter((root) => root === name).length;
|
|
15161
|
-
}
|
|
15162
|
-
continue;
|
|
15163
|
-
}
|
|
15164
|
-
if (FLOW_REFERENCE_FIELDS.has(key) && Array.isArray(child) && child.every((item) => typeof item === 'string')) {
|
|
15165
|
-
// `choiceReferences` e' un array di **nomi**: scendendoci dentro non si troverebbe
|
|
15166
|
-
// nessuna stringa da confrontare, perche' le stringhe sono gli elementi.
|
|
15167
|
-
count += child.filter((item) => isUseOf(item, name)).length;
|
|
15168
|
-
continue;
|
|
15169
|
-
}
|
|
15170
|
-
walk(child);
|
|
15171
|
-
}
|
|
15172
|
-
};
|
|
15173
|
-
walk(root);
|
|
15174
|
-
return count;
|
|
15175
|
-
}
|
|
15176
|
-
|
|
15177
15648
|
/**
|
|
15178
15649
|
* I controlli locali su una risorsa, condivisi fra l'elenco e la dialog di modifica.
|
|
15179
15650
|
*
|
|
@@ -15406,6 +15877,8 @@ function resourceKindOf(collection) {
|
|
|
15406
15877
|
* (`editing`), invece di tenerne una copia sua che si scollerebbe alla prima chiusura fatta
|
|
15407
15878
|
* dalla finestra.
|
|
15408
15879
|
*/
|
|
15880
|
+
/** Nessun uso: il valore per una risorsa che la mappa non conosce ancora. */
|
|
15881
|
+
const EMPTY_USES = { references: 0, formulas: 0, mergeFields: 0, total: 0 };
|
|
15409
15882
|
class ResourcePanelComponent {
|
|
15410
15883
|
store = inject(FlowDocumentStore);
|
|
15411
15884
|
closed = output();
|
|
@@ -15635,7 +16108,33 @@ class ResourcePanelComponent {
|
|
|
15635
16108
|
* senza, tre formule che leggono una variabile la direbbero «non usata».
|
|
15636
16109
|
*/
|
|
15637
16110
|
usesOf(reference) {
|
|
15638
|
-
return this.
|
|
16111
|
+
return this.usesDetailOf(reference).total;
|
|
16112
|
+
}
|
|
16113
|
+
/**
|
|
16114
|
+
* Il dettaglio degli usi: quanti riferimenti, quante formule, quanti merge field.
|
|
16115
|
+
*
|
|
16116
|
+
* Serve al `title` della riga, e non e' un vezzo: `{!Cliente.Nome}` in un messaggio e' un uso
|
|
16117
|
+
* che non si vede da nessuna parte nel documento, quindi «13 usi» senza dire **dove** manda a
|
|
16118
|
+
* cercarli nei posti sbagliati. E le tre categorie non si comportano allo stesso modo davanti a
|
|
16119
|
+
* una rinomina, che e' l'altra cosa che si fa con questo numero.
|
|
16120
|
+
*/
|
|
16121
|
+
usesDetailOf(reference) {
|
|
16122
|
+
return this.useCounts().get(reference.name) ?? EMPTY_USES;
|
|
16123
|
+
}
|
|
16124
|
+
/** La riga del `title`: si scrivono le sole categorie presenti, altrimenti sono sempre tre. */
|
|
16125
|
+
usesDetailLabel(reference) {
|
|
16126
|
+
const uses = this.usesDetailOf(reference);
|
|
16127
|
+
const parts = [];
|
|
16128
|
+
if (uses.references) {
|
|
16129
|
+
parts.push(uses.references === 1 ? '1 come riferimento' : `${uses.references} come riferimenti`);
|
|
16130
|
+
}
|
|
16131
|
+
if (uses.formulas) {
|
|
16132
|
+
parts.push(`${uses.formulas} in formule`);
|
|
16133
|
+
}
|
|
16134
|
+
if (uses.mergeFields) {
|
|
16135
|
+
parts.push(`${uses.mergeFields} nei testi con {!…}`);
|
|
16136
|
+
}
|
|
16137
|
+
return parts.join(', ');
|
|
15639
16138
|
}
|
|
15640
16139
|
/**
|
|
15641
16140
|
* Il conteggio di **tutte** le risorse, calcolato una volta per documento.
|
|
@@ -15644,17 +16143,17 @@ class ResourcePanelComponent {
|
|
|
15644
16143
|
* chiede per ogni risorsa di ogni collection — i numeri delle schede, il chip «non usate» —
|
|
15645
16144
|
* chiamarlo dal template significherebbe cinquanta walk per giro di change detection.
|
|
15646
16145
|
*
|
|
15647
|
-
* La chiave e' il nome **esatto** e non quello minuscolo:
|
|
15648
|
-
*
|
|
15649
|
-
*
|
|
15650
|
-
*
|
|
16146
|
+
* La chiave e' il nome **esatto** e non quello minuscolo: il conteggio confronta le stringhe
|
|
16147
|
+
* come sono, quindi due nomi che differiscono solo per il case — che sono un `NAME_DUPLICATED`,
|
|
16148
|
+
* non un caso da normalizzare qui — hanno due conteggi diversi, ed e' quello che il pannello
|
|
16149
|
+
* mostrava anche prima della memoizzazione.
|
|
15651
16150
|
*/
|
|
15652
16151
|
useCounts = computed(() => {
|
|
15653
16152
|
const document = this.store.document();
|
|
15654
16153
|
const counts = new Map();
|
|
15655
16154
|
for (const reference of this.store.resources()) {
|
|
15656
16155
|
if (!counts.has(reference.name)) {
|
|
15657
|
-
counts.set(reference.name,
|
|
16156
|
+
counts.set(reference.name, describeNameUses(document, reference.name));
|
|
15658
16157
|
}
|
|
15659
16158
|
}
|
|
15660
16159
|
return counts;
|
|
@@ -15701,11 +16200,11 @@ class ResourcePanelComponent {
|
|
|
15701
16200
|
this.closed.emit();
|
|
15702
16201
|
}
|
|
15703
16202
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ResourcePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
15704
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ResourcePanelComponent, isStandalone: true, selector: "fb-resource-panel", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", editRequested: "editRequested" }, ngImport: i0, template: "<header class=\"fb-res__header\">\r\n <h2 class=\"fb-res__title\">Risorse</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-res__tabs\" aria-label=\"Tipi di risorsa\">\r\n @for (kind of kinds; track kind.collection) {\r\n <!--\r\n Con un filtro acceso il numero della scheda e\u2019 quello delle **corrispondenze**: e\u2019 cio\u2019\r\n che dice dove sta la risorsa che si cerca, che il piu\u2019 delle volte e\u2019 in un\u2019altra\r\n collection. La scheda a zero si sbiadisce invece di sparire \u2014 nasconderla farebbe\r\n ballare la barra a ogni lettera digitata.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__tab\"\r\n [class.fb-res__tab--active]=\"activeCollection() === kind.collection\"\r\n [class.fb-res__tab--dim]=\"hasFilter() && countOf(kind.collection) === 0\"\r\n [title]=\"\r\n hasFilter()\r\n ? countOf(kind.collection) + ' su ' + totalOf(kind.collection) + ' con il filtro attivo'\r\n : kind.label\r\n \"\r\n (click)=\"select(kind.collection)\"\r\n >\r\n {{ kind.label }}\r\n <span class=\"fb-res__count\" [class.fb-res__count--match]=\"hasFilter()\">\r\n {{ countOf(kind.collection) }}\r\n </span>\r\n </button>\r\n }\r\n</nav>\r\n\r\n<!--\r\n Il filtro sta fra le schede e l\u2019elenco, e non scorre con esso: su una collection lunga\r\n scorrere per raggiungere la casella con cui accorciarla e\u2019 il gesto che non deve servire.\r\n-->\r\n<div class=\"fb-res__filter\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-res__search\"\r\n placeholder=\"Filtra per nome o tipo\u2026\"\r\n aria-label=\"Filtra le risorse per nome o tipo\"\r\n title=\"Cerca nel nome, nell\u2019etichetta, nel tipo e nei flag (input, output, collection). Non guarda dentro i valori e le formule: a dire chi cita una risorsa e\u2019 il conteggio degli usi\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <div class=\"fb-res__chips\" role=\"group\" aria-label=\"Filtra per stato\">\r\n @let counts = stateCounts();\r\n <!--\r\n I due stati sono le domande che un nome non sa fare, e sono quelle che si fanno davvero\r\n su un flow grande: \u00ABquali variabili sono morte\u00BB e \u00ABquali righe hanno qualcosa che non\r\n va\u00BB. A zero il chip si disabilita: un filtro che darebbe certamente un elenco vuoto non\r\n restringe niente, nasconde tutto.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'unused'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'unused'\"\r\n [disabled]=\"counts.unused === 0 && stateFilter() !== 'unused'\"\r\n title=\"Solo le risorse che nessun riferimento e nessuna formula usa\"\r\n (click)=\"setStateFilter('unused')\"\r\n >\r\n Non usate <span class=\"fb-res__count\">{{ counts.unused }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--issues\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'issues'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'issues'\"\r\n [disabled]=\"counts.issues === 0 && stateFilter() !== 'issues'\"\r\n title=\"Solo le risorse con un rilievo locale: nome duplicato, costante che referenzia, ordine di stage doppio\"\r\n (click)=\"setStateFilter('issues')\"\r\n >\r\n Con rilievi <span class=\"fb-res__count\">{{ counts.issues }}</span>\r\n </button>\r\n @if (hasFilter()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--reset\"\r\n title=\"Mostra tutte le risorse della scheda\"\r\n (click)=\"resetFilter()\"\r\n >\r\n Azzera\r\n </button>\r\n }\r\n </div>\r\n</div>\r\n\r\n<div class=\"fb-res__body\">\r\n <p class=\"fb-section__note\">{{ activeKind().note }}</p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track item.collection + ':' + item.index) {\r\n <div class=\"fb-list__item\" [class.fb-list__item--active]=\"isEditing(item)\">\r\n <div class=\"fb-list__header\">\r\n <!--\r\n Il nome apre la finestra di modifica: qui l\u2019elenco resta un elenco, e cio\u2019 che si\r\n configura non lo copre. \u00C8 la stessa scelta della dialog del dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__name\"\r\n [title]=\"'Configura ' + (item.name || 'la risorsa')\"\r\n (click)=\"edit(item)\"\r\n >\r\n <span class=\"fb-res__name-text\">{{ item.name || '(senza nome)' }}</span>\r\n <!--\r\n L\u2019`objectType` sta accanto al tipo perche\u2019 su una `Structure` il tipo da solo non\r\n dice niente: sei variabili su ventuno dicevano \u00ABStructure\u00BB e la classe si leggeva\r\n solo aprendole. E\u2019 anche cio\u2019 che rende spiegabile il filtro, che l\u2019`objectType`\r\n lo guarda: senza, cercare \u00ABdto\u00BB faceva comparire tre righe che \u00ABdto\u00BB non lo\r\n mostrano da nessuna parte \u2014 un filtro che sembra sbagliato.\r\n -->\r\n <span class=\"fb-res__meta\">\r\n {{ string(item, 'dataType') }}{{ boolean(item, 'isCollection') ? '[]' : '' }}\r\n @if (string(item, 'objectType')) {\r\n \u00B7 {{ string(item, 'objectType') }}\r\n }\r\n @if (boolean(item, 'isInput')) {\r\n \u00B7 input\r\n }\r\n @if (boolean(item, 'isOutput')) {\r\n \u00B7 output\r\n }\r\n </span>\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <!--\r\n Il numero di usi sta accanto al nome perche\u2019 e\u2019 cio\u2019 che decide se si puo\u2019 togliere:\r\n \u00ABnon usata\u00BB e\u2019 un invito a fare pulizia, \u00ABusata 15 volte\u00BB e\u2019 un avvertimento.\r\n -->\r\n @let uses = usesOf(item);\r\n @if (uses === 0) {\r\n <span class=\"fb-res__uses fb-res__uses--none\" title=\"Nessun riferimento la usa: si puo\u2019 togliere\">\r\n non usata\r\n </span>\r\n } @else {\r\n <span\r\n class=\"fb-res__uses\"\r\n [title]=\"'Referenziata ' + uses + ' volte nel documento, formule comprese'\"\r\n >\r\n {{ uses }} usi\r\n </span>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la risorsa\"\r\n [title]=\"uses ? 'Rimuovi: e\u2019 usata ' + uses + ' volte, verra\u2019 chiesta conferma' : 'Rimuovi'\"\r\n (click)=\"requestRemove(item)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isPendingRemoval(item)) {\r\n <!--\r\n La conferma sta qui e non in una finestra: la riga che si sta togliendo resta sotto\r\n gli occhi, e il numero di riferimenti che si rompono e\u2019 il dato della decisione.\r\n -->\r\n <p class=\"fb-callout fb-callout--warn fb-res__confirm\">\r\n <span>\r\n \u00AB{{ item.name }}\u00BB e\u2019 usata {{ usesOf(item) }} volte: togliendola quei riferimenti\r\n restano orfani e la validazione li segnalera\u2019.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"remove(item)\">Rimuovi comunque</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelRemove()\">\r\n Annulla\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n I rilievi locali restano nell\u2019elenco anche ora che il form sta altrove: senza, per\r\n sapere quali risorse hanno qualcosa che non va bisognerebbe aprirle una per una.\r\n -->\r\n @if (nameError(item); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n }\r\n @if (constantReferencesResource(item)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n }\r\n @if (duplicateStageOrder(item)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n }\r\n @if (ignoredChoiceSetFilters(item); as ignored) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Le opzioni vengono da una collection: {{ ignored }} (CHOICE_SET_FILTERS_IGNORED).\r\n </p>\r\n }\r\n </div>\r\n } @empty {\r\n <!--\r\n Tre casi e tre rimedi: la collection e\u2019 vuota (si aggiunge), il filtro non trova\r\n niente qui ma trova altrove (si cambia scheda), il filtro non trova niente da nessuna\r\n parte (si azzera). Un solo \u00ABnessuna risorsa\u00BB per tutti e tre manda a cercare il\r\n difetto dove non c\u2019e\u2019.\r\n -->\r\n @if (!hasFilter()) {\r\n <p class=\"fb-empty\">Nessuna risorsa di questo tipo.</p>\r\n } @else if (matchesElsewhere() > 0) {\r\n <p class=\"fb-empty\">\r\n Nessuna corrispondenza in questa scheda, ma {{ matchesElsewhere() }} in altre: il\r\n numero accanto a ognuna dice dove.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessuna risorsa corrisponde al filtro.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"resetFilter()\">\r\n Azzera il filtro\r\n </button>\r\n </p>\r\n }\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"add()\">\r\n Aggiungi {{ activeKind().singular }}\r\n </button>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-res__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-res__tabs{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__tab{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__tab:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-res__tab--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-res__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-res__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-res__name{flex:1;min-width:0;display:flex;flex-direction:column;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-res__name-text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__meta{font-size:10px;color:var(--fb-text-muted, #667085);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__uses{flex:none;padding:1px 6px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap}.fb-res__uses--none{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-res__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.fb-list__item--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,var(--fb-surface-alt, #f8f9fb))}.fb-list__item .fb-list__header{margin-bottom:0}.fb-list__item .fb-list__header+*{margin-top:6px}.fb-res__filter{display:flex;flex-direction:column;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__search{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-res__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-res__chips{display:flex;flex-wrap:wrap;gap:4px}.fb-res__chip{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__chip:hover:not(:disabled){background:var(--fb-border, #d6dae1)}.fb-res__chip:disabled{opacity:.45;cursor:default}.fb-res__chip--active{background:var(--fb-accent, #2f6feb);color:#fff;font-weight:600}.fb-res__chip--active .fb-res__count{background:color-mix(in srgb,#fff 30%,transparent);color:#fff}.fb-res__chip--reset{margin-left:auto}.fb-res__tab--dim{opacity:.4}.fb-res__count--match{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 20%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-empty .fb-btn{margin-left:6px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
16203
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ResourcePanelComponent, isStandalone: true, selector: "fb-resource-panel", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", editRequested: "editRequested" }, ngImport: i0, template: "<header class=\"fb-res__header\">\r\n <h2 class=\"fb-res__title\">Risorse</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-res__tabs\" aria-label=\"Tipi di risorsa\">\r\n @for (kind of kinds; track kind.collection) {\r\n <!--\r\n Con un filtro acceso il numero della scheda e\u2019 quello delle **corrispondenze**: e\u2019 cio\u2019\r\n che dice dove sta la risorsa che si cerca, che il piu\u2019 delle volte e\u2019 in un\u2019altra\r\n collection. La scheda a zero si sbiadisce invece di sparire \u2014 nasconderla farebbe\r\n ballare la barra a ogni lettera digitata.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__tab\"\r\n [class.fb-res__tab--active]=\"activeCollection() === kind.collection\"\r\n [class.fb-res__tab--dim]=\"hasFilter() && countOf(kind.collection) === 0\"\r\n [title]=\"\r\n hasFilter()\r\n ? countOf(kind.collection) + ' su ' + totalOf(kind.collection) + ' con il filtro attivo'\r\n : kind.label\r\n \"\r\n (click)=\"select(kind.collection)\"\r\n >\r\n {{ kind.label }}\r\n <span class=\"fb-res__count\" [class.fb-res__count--match]=\"hasFilter()\">\r\n {{ countOf(kind.collection) }}\r\n </span>\r\n </button>\r\n }\r\n</nav>\r\n\r\n<!--\r\n Il filtro sta fra le schede e l\u2019elenco, e non scorre con esso: su una collection lunga\r\n scorrere per raggiungere la casella con cui accorciarla e\u2019 il gesto che non deve servire.\r\n-->\r\n<div class=\"fb-res__filter\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-res__search\"\r\n placeholder=\"Filtra per nome o tipo\u2026\"\r\n aria-label=\"Filtra le risorse per nome o tipo\"\r\n title=\"Cerca nel nome, nell\u2019etichetta, nel tipo e nei flag (input, output, collection). Non guarda dentro i valori e le formule: a dire chi cita una risorsa e\u2019 il conteggio degli usi\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <div class=\"fb-res__chips\" role=\"group\" aria-label=\"Filtra per stato\">\r\n @let counts = stateCounts();\r\n <!--\r\n I due stati sono le domande che un nome non sa fare, e sono quelle che si fanno davvero\r\n su un flow grande: \u00ABquali variabili sono morte\u00BB e \u00ABquali righe hanno qualcosa che non\r\n va\u00BB. A zero il chip si disabilita: un filtro che darebbe certamente un elenco vuoto non\r\n restringe niente, nasconde tutto.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'unused'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'unused'\"\r\n [disabled]=\"counts.unused === 0 && stateFilter() !== 'unused'\"\r\n title=\"Solo le risorse che nessun riferimento e nessuna formula usa\"\r\n (click)=\"setStateFilter('unused')\"\r\n >\r\n Non usate <span class=\"fb-res__count\">{{ counts.unused }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--issues\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'issues'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'issues'\"\r\n [disabled]=\"counts.issues === 0 && stateFilter() !== 'issues'\"\r\n title=\"Solo le risorse con un rilievo locale: nome duplicato, costante che referenzia, ordine di stage doppio\"\r\n (click)=\"setStateFilter('issues')\"\r\n >\r\n Con rilievi <span class=\"fb-res__count\">{{ counts.issues }}</span>\r\n </button>\r\n @if (hasFilter()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--reset\"\r\n title=\"Mostra tutte le risorse della scheda\"\r\n (click)=\"resetFilter()\"\r\n >\r\n Azzera\r\n </button>\r\n }\r\n </div>\r\n</div>\r\n\r\n<div class=\"fb-res__body\">\r\n <p class=\"fb-section__note\">{{ activeKind().note }}</p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track item.collection + ':' + item.index) {\r\n <div class=\"fb-list__item\" [class.fb-list__item--active]=\"isEditing(item)\">\r\n <div class=\"fb-list__header\">\r\n <!--\r\n Il nome apre la finestra di modifica: qui l\u2019elenco resta un elenco, e cio\u2019 che si\r\n configura non lo copre. \u00C8 la stessa scelta della dialog del dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__name\"\r\n [title]=\"'Configura ' + (item.name || 'la risorsa')\"\r\n (click)=\"edit(item)\"\r\n >\r\n <span class=\"fb-res__name-text\">{{ item.name || '(senza nome)' }}</span>\r\n <!--\r\n L\u2019`objectType` sta accanto al tipo perche\u2019 su una `Structure` il tipo da solo non\r\n dice niente: sei variabili su ventuno dicevano \u00ABStructure\u00BB e la classe si leggeva\r\n solo aprendole. E\u2019 anche cio\u2019 che rende spiegabile il filtro, che l\u2019`objectType`\r\n lo guarda: senza, cercare \u00ABdto\u00BB faceva comparire tre righe che \u00ABdto\u00BB non lo\r\n mostrano da nessuna parte \u2014 un filtro che sembra sbagliato.\r\n -->\r\n <span class=\"fb-res__meta\">\r\n {{ string(item, 'dataType') }}{{ boolean(item, 'isCollection') ? '[]' : '' }}\r\n @if (string(item, 'objectType')) {\r\n \u00B7 {{ string(item, 'objectType') }}\r\n }\r\n @if (boolean(item, 'isInput')) {\r\n \u00B7 input\r\n }\r\n @if (boolean(item, 'isOutput')) {\r\n \u00B7 output\r\n }\r\n </span>\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <!--\r\n Il numero di usi sta accanto al nome perche\u2019 e\u2019 cio\u2019 che decide se si puo\u2019 togliere:\r\n \u00ABnon usata\u00BB e\u2019 un invito a fare pulizia, \u00ABusata 15 volte\u00BB e\u2019 un avvertimento.\r\n -->\r\n @let uses = usesOf(item);\r\n @if (uses === 0) {\r\n <span class=\"fb-res__uses fb-res__uses--none\" title=\"Nessun riferimento la usa: si puo\u2019 togliere\">\r\n non usata\r\n </span>\r\n } @else {\r\n <!--\r\n Il dettaglio sta nel `title` e non nella riga: in 340px non ci sta, e serve a una\r\n domanda che si fa **dopo** (\u00AB13 dove?\u00BB). Un merge field e\u2019 l\u2019uso che non si vede\r\n scorrendo il documento, quindi dirlo cambia dove si va a cercare.\r\n -->\r\n <span\r\n class=\"fb-res__uses\"\r\n [title]=\"\r\n (uses === 1 ? 'Usata una volta nel documento: ' : 'Usata ' + uses + ' volte nel documento: ') +\r\n usesDetailLabel(item)\r\n \"\r\n >\r\n {{ uses }} {{ uses === 1 ? 'uso' : 'usi' }}\r\n </span>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la risorsa\"\r\n [title]=\"uses ? 'Rimuovi: e\u2019 usata ' + uses + ' volte, verra\u2019 chiesta conferma' : 'Rimuovi'\"\r\n (click)=\"requestRemove(item)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isPendingRemoval(item)) {\r\n <!--\r\n La conferma sta qui e non in una finestra: la riga che si sta togliendo resta sotto\r\n gli occhi, e il numero di riferimenti che si rompono e\u2019 il dato della decisione.\r\n -->\r\n <p class=\"fb-callout fb-callout--warn fb-res__confirm\">\r\n <span>\r\n \u00AB{{ item.name }}\u00BB e\u2019 usata {{ usesOf(item) }} volte: togliendola quei riferimenti\r\n restano orfani e la validazione li segnalera\u2019.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"remove(item)\">Rimuovi comunque</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelRemove()\">\r\n Annulla\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n I rilievi locali restano nell\u2019elenco anche ora che il form sta altrove: senza, per\r\n sapere quali risorse hanno qualcosa che non va bisognerebbe aprirle una per una.\r\n -->\r\n @if (nameError(item); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n }\r\n @if (constantReferencesResource(item)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n }\r\n @if (duplicateStageOrder(item)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n }\r\n @if (ignoredChoiceSetFilters(item); as ignored) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Le opzioni vengono da una collection: {{ ignored }} (CHOICE_SET_FILTERS_IGNORED).\r\n </p>\r\n }\r\n </div>\r\n } @empty {\r\n <!--\r\n Tre casi e tre rimedi: la collection e\u2019 vuota (si aggiunge), il filtro non trova\r\n niente qui ma trova altrove (si cambia scheda), il filtro non trova niente da nessuna\r\n parte (si azzera). Un solo \u00ABnessuna risorsa\u00BB per tutti e tre manda a cercare il\r\n difetto dove non c\u2019e\u2019.\r\n -->\r\n @if (!hasFilter()) {\r\n <p class=\"fb-empty\">Nessuna risorsa di questo tipo.</p>\r\n } @else if (matchesElsewhere() > 0) {\r\n <p class=\"fb-empty\">\r\n Nessuna corrispondenza in questa scheda, ma {{ matchesElsewhere() }} in altre: il\r\n numero accanto a ognuna dice dove.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessuna risorsa corrisponde al filtro.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"resetFilter()\">\r\n Azzera il filtro\r\n </button>\r\n </p>\r\n }\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"add()\">\r\n Aggiungi {{ activeKind().singular }}\r\n </button>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-res__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-res__tabs{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__tab{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__tab:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-res__tab--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-res__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-res__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-res__name{flex:1;min-width:0;display:flex;flex-direction:column;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-res__name-text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__meta{font-size:10px;color:var(--fb-text-muted, #667085);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__uses{flex:none;padding:1px 6px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap}.fb-res__uses--none{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-res__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.fb-list__item--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,var(--fb-surface-alt, #f8f9fb))}.fb-list__item .fb-list__header{margin-bottom:0}.fb-list__item .fb-list__header+*{margin-top:6px}.fb-res__filter{display:flex;flex-direction:column;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__search{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-res__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-res__chips{display:flex;flex-wrap:wrap;gap:4px}.fb-res__chip{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__chip:hover:not(:disabled){background:var(--fb-border, #d6dae1)}.fb-res__chip:disabled{opacity:.45;cursor:default}.fb-res__chip--active{background:var(--fb-accent, #2f6feb);color:#fff;font-weight:600}.fb-res__chip--active .fb-res__count{background:color-mix(in srgb,#fff 30%,transparent);color:#fff}.fb-res__chip--reset{margin-left:auto}.fb-res__tab--dim{opacity:.4}.fb-res__count--match{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 20%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-empty .fb-btn{margin-left:6px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15705
16204
|
}
|
|
15706
16205
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ResourcePanelComponent, decorators: [{
|
|
15707
16206
|
type: Component,
|
|
15708
|
-
args: [{ selector: 'fb-resource-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-res__header\">\r\n <h2 class=\"fb-res__title\">Risorse</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-res__tabs\" aria-label=\"Tipi di risorsa\">\r\n @for (kind of kinds; track kind.collection) {\r\n <!--\r\n Con un filtro acceso il numero della scheda e\u2019 quello delle **corrispondenze**: e\u2019 cio\u2019\r\n che dice dove sta la risorsa che si cerca, che il piu\u2019 delle volte e\u2019 in un\u2019altra\r\n collection. La scheda a zero si sbiadisce invece di sparire \u2014 nasconderla farebbe\r\n ballare la barra a ogni lettera digitata.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__tab\"\r\n [class.fb-res__tab--active]=\"activeCollection() === kind.collection\"\r\n [class.fb-res__tab--dim]=\"hasFilter() && countOf(kind.collection) === 0\"\r\n [title]=\"\r\n hasFilter()\r\n ? countOf(kind.collection) + ' su ' + totalOf(kind.collection) + ' con il filtro attivo'\r\n : kind.label\r\n \"\r\n (click)=\"select(kind.collection)\"\r\n >\r\n {{ kind.label }}\r\n <span class=\"fb-res__count\" [class.fb-res__count--match]=\"hasFilter()\">\r\n {{ countOf(kind.collection) }}\r\n </span>\r\n </button>\r\n }\r\n</nav>\r\n\r\n<!--\r\n Il filtro sta fra le schede e l\u2019elenco, e non scorre con esso: su una collection lunga\r\n scorrere per raggiungere la casella con cui accorciarla e\u2019 il gesto che non deve servire.\r\n-->\r\n<div class=\"fb-res__filter\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-res__search\"\r\n placeholder=\"Filtra per nome o tipo\u2026\"\r\n aria-label=\"Filtra le risorse per nome o tipo\"\r\n title=\"Cerca nel nome, nell\u2019etichetta, nel tipo e nei flag (input, output, collection). Non guarda dentro i valori e le formule: a dire chi cita una risorsa e\u2019 il conteggio degli usi\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <div class=\"fb-res__chips\" role=\"group\" aria-label=\"Filtra per stato\">\r\n @let counts = stateCounts();\r\n <!--\r\n I due stati sono le domande che un nome non sa fare, e sono quelle che si fanno davvero\r\n su un flow grande: \u00ABquali variabili sono morte\u00BB e \u00ABquali righe hanno qualcosa che non\r\n va\u00BB. A zero il chip si disabilita: un filtro che darebbe certamente un elenco vuoto non\r\n restringe niente, nasconde tutto.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'unused'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'unused'\"\r\n [disabled]=\"counts.unused === 0 && stateFilter() !== 'unused'\"\r\n title=\"Solo le risorse che nessun riferimento e nessuna formula usa\"\r\n (click)=\"setStateFilter('unused')\"\r\n >\r\n Non usate <span class=\"fb-res__count\">{{ counts.unused }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--issues\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'issues'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'issues'\"\r\n [disabled]=\"counts.issues === 0 && stateFilter() !== 'issues'\"\r\n title=\"Solo le risorse con un rilievo locale: nome duplicato, costante che referenzia, ordine di stage doppio\"\r\n (click)=\"setStateFilter('issues')\"\r\n >\r\n Con rilievi <span class=\"fb-res__count\">{{ counts.issues }}</span>\r\n </button>\r\n @if (hasFilter()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--reset\"\r\n title=\"Mostra tutte le risorse della scheda\"\r\n (click)=\"resetFilter()\"\r\n >\r\n Azzera\r\n </button>\r\n }\r\n </div>\r\n</div>\r\n\r\n<div class=\"fb-res__body\">\r\n <p class=\"fb-section__note\">{{ activeKind().note }}</p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track item.collection + ':' + item.index) {\r\n <div class=\"fb-list__item\" [class.fb-list__item--active]=\"isEditing(item)\">\r\n <div class=\"fb-list__header\">\r\n <!--\r\n Il nome apre la finestra di modifica: qui l\u2019elenco resta un elenco, e cio\u2019 che si\r\n configura non lo copre. \u00C8 la stessa scelta della dialog del dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__name\"\r\n [title]=\"'Configura ' + (item.name || 'la risorsa')\"\r\n (click)=\"edit(item)\"\r\n >\r\n <span class=\"fb-res__name-text\">{{ item.name || '(senza nome)' }}</span>\r\n <!--\r\n L\u2019`objectType` sta accanto al tipo perche\u2019 su una `Structure` il tipo da solo non\r\n dice niente: sei variabili su ventuno dicevano \u00ABStructure\u00BB e la classe si leggeva\r\n solo aprendole. E\u2019 anche cio\u2019 che rende spiegabile il filtro, che l\u2019`objectType`\r\n lo guarda: senza, cercare \u00ABdto\u00BB faceva comparire tre righe che \u00ABdto\u00BB non lo\r\n mostrano da nessuna parte \u2014 un filtro che sembra sbagliato.\r\n -->\r\n <span class=\"fb-res__meta\">\r\n {{ string(item, 'dataType') }}{{ boolean(item, 'isCollection') ? '[]' : '' }}\r\n @if (string(item, 'objectType')) {\r\n \u00B7 {{ string(item, 'objectType') }}\r\n }\r\n @if (boolean(item, 'isInput')) {\r\n \u00B7 input\r\n }\r\n @if (boolean(item, 'isOutput')) {\r\n \u00B7 output\r\n }\r\n </span>\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <!--\r\n Il numero di usi sta accanto al nome perche\u2019 e\u2019 cio\u2019 che decide se si puo\u2019 togliere:\r\n \u00ABnon usata\u00BB e\u2019 un invito a fare pulizia, \u00ABusata 15 volte\u00BB e\u2019 un avvertimento.\r\n -->\r\n @let uses = usesOf(item);\r\n @if (uses === 0) {\r\n <span class=\"fb-res__uses fb-res__uses--none\" title=\"Nessun riferimento la usa: si puo\u2019 togliere\">\r\n non usata\r\n </span>\r\n } @else {\r\n <span\r\n class=\"fb-res__uses\"\r\n [title]=\"'Referenziata ' + uses + ' volte nel documento, formule comprese'\"\r\n >\r\n {{ uses }} usi\r\n </span>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la risorsa\"\r\n [title]=\"uses ? 'Rimuovi: e\u2019 usata ' + uses + ' volte, verra\u2019 chiesta conferma' : 'Rimuovi'\"\r\n (click)=\"requestRemove(item)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isPendingRemoval(item)) {\r\n <!--\r\n La conferma sta qui e non in una finestra: la riga che si sta togliendo resta sotto\r\n gli occhi, e il numero di riferimenti che si rompono e\u2019 il dato della decisione.\r\n -->\r\n <p class=\"fb-callout fb-callout--warn fb-res__confirm\">\r\n <span>\r\n \u00AB{{ item.name }}\u00BB e\u2019 usata {{ usesOf(item) }} volte: togliendola quei riferimenti\r\n restano orfani e la validazione li segnalera\u2019.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"remove(item)\">Rimuovi comunque</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelRemove()\">\r\n Annulla\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n I rilievi locali restano nell\u2019elenco anche ora che il form sta altrove: senza, per\r\n sapere quali risorse hanno qualcosa che non va bisognerebbe aprirle una per una.\r\n -->\r\n @if (nameError(item); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n }\r\n @if (constantReferencesResource(item)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n }\r\n @if (duplicateStageOrder(item)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n }\r\n @if (ignoredChoiceSetFilters(item); as ignored) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Le opzioni vengono da una collection: {{ ignored }} (CHOICE_SET_FILTERS_IGNORED).\r\n </p>\r\n }\r\n </div>\r\n } @empty {\r\n <!--\r\n Tre casi e tre rimedi: la collection e\u2019 vuota (si aggiunge), il filtro non trova\r\n niente qui ma trova altrove (si cambia scheda), il filtro non trova niente da nessuna\r\n parte (si azzera). Un solo \u00ABnessuna risorsa\u00BB per tutti e tre manda a cercare il\r\n difetto dove non c\u2019e\u2019.\r\n -->\r\n @if (!hasFilter()) {\r\n <p class=\"fb-empty\">Nessuna risorsa di questo tipo.</p>\r\n } @else if (matchesElsewhere() > 0) {\r\n <p class=\"fb-empty\">\r\n Nessuna corrispondenza in questa scheda, ma {{ matchesElsewhere() }} in altre: il\r\n numero accanto a ognuna dice dove.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessuna risorsa corrisponde al filtro.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"resetFilter()\">\r\n Azzera il filtro\r\n </button>\r\n </p>\r\n }\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"add()\">\r\n Aggiungi {{ activeKind().singular }}\r\n </button>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-res__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-res__tabs{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__tab{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__tab:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-res__tab--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-res__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-res__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-res__name{flex:1;min-width:0;display:flex;flex-direction:column;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-res__name-text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__meta{font-size:10px;color:var(--fb-text-muted, #667085);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__uses{flex:none;padding:1px 6px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap}.fb-res__uses--none{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-res__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.fb-list__item--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,var(--fb-surface-alt, #f8f9fb))}.fb-list__item .fb-list__header{margin-bottom:0}.fb-list__item .fb-list__header+*{margin-top:6px}.fb-res__filter{display:flex;flex-direction:column;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__search{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-res__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-res__chips{display:flex;flex-wrap:wrap;gap:4px}.fb-res__chip{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__chip:hover:not(:disabled){background:var(--fb-border, #d6dae1)}.fb-res__chip:disabled{opacity:.45;cursor:default}.fb-res__chip--active{background:var(--fb-accent, #2f6feb);color:#fff;font-weight:600}.fb-res__chip--active .fb-res__count{background:color-mix(in srgb,#fff 30%,transparent);color:#fff}.fb-res__chip--reset{margin-left:auto}.fb-res__tab--dim{opacity:.4}.fb-res__count--match{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 20%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-empty .fb-btn{margin-left:6px}\n"] }]
|
|
16207
|
+
args: [{ selector: 'fb-resource-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-res__header\">\r\n <h2 class=\"fb-res__title\">Risorse</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-res__tabs\" aria-label=\"Tipi di risorsa\">\r\n @for (kind of kinds; track kind.collection) {\r\n <!--\r\n Con un filtro acceso il numero della scheda e\u2019 quello delle **corrispondenze**: e\u2019 cio\u2019\r\n che dice dove sta la risorsa che si cerca, che il piu\u2019 delle volte e\u2019 in un\u2019altra\r\n collection. La scheda a zero si sbiadisce invece di sparire \u2014 nasconderla farebbe\r\n ballare la barra a ogni lettera digitata.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__tab\"\r\n [class.fb-res__tab--active]=\"activeCollection() === kind.collection\"\r\n [class.fb-res__tab--dim]=\"hasFilter() && countOf(kind.collection) === 0\"\r\n [title]=\"\r\n hasFilter()\r\n ? countOf(kind.collection) + ' su ' + totalOf(kind.collection) + ' con il filtro attivo'\r\n : kind.label\r\n \"\r\n (click)=\"select(kind.collection)\"\r\n >\r\n {{ kind.label }}\r\n <span class=\"fb-res__count\" [class.fb-res__count--match]=\"hasFilter()\">\r\n {{ countOf(kind.collection) }}\r\n </span>\r\n </button>\r\n }\r\n</nav>\r\n\r\n<!--\r\n Il filtro sta fra le schede e l\u2019elenco, e non scorre con esso: su una collection lunga\r\n scorrere per raggiungere la casella con cui accorciarla e\u2019 il gesto che non deve servire.\r\n-->\r\n<div class=\"fb-res__filter\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-res__search\"\r\n placeholder=\"Filtra per nome o tipo\u2026\"\r\n aria-label=\"Filtra le risorse per nome o tipo\"\r\n title=\"Cerca nel nome, nell\u2019etichetta, nel tipo e nei flag (input, output, collection). Non guarda dentro i valori e le formule: a dire chi cita una risorsa e\u2019 il conteggio degli usi\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <div class=\"fb-res__chips\" role=\"group\" aria-label=\"Filtra per stato\">\r\n @let counts = stateCounts();\r\n <!--\r\n I due stati sono le domande che un nome non sa fare, e sono quelle che si fanno davvero\r\n su un flow grande: \u00ABquali variabili sono morte\u00BB e \u00ABquali righe hanno qualcosa che non\r\n va\u00BB. A zero il chip si disabilita: un filtro che darebbe certamente un elenco vuoto non\r\n restringe niente, nasconde tutto.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'unused'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'unused'\"\r\n [disabled]=\"counts.unused === 0 && stateFilter() !== 'unused'\"\r\n title=\"Solo le risorse che nessun riferimento e nessuna formula usa\"\r\n (click)=\"setStateFilter('unused')\"\r\n >\r\n Non usate <span class=\"fb-res__count\">{{ counts.unused }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--issues\"\r\n [class.fb-res__chip--active]=\"stateFilter() === 'issues'\"\r\n [attr.aria-pressed]=\"stateFilter() === 'issues'\"\r\n [disabled]=\"counts.issues === 0 && stateFilter() !== 'issues'\"\r\n title=\"Solo le risorse con un rilievo locale: nome duplicato, costante che referenzia, ordine di stage doppio\"\r\n (click)=\"setStateFilter('issues')\"\r\n >\r\n Con rilievi <span class=\"fb-res__count\">{{ counts.issues }}</span>\r\n </button>\r\n @if (hasFilter()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__chip fb-res__chip--reset\"\r\n title=\"Mostra tutte le risorse della scheda\"\r\n (click)=\"resetFilter()\"\r\n >\r\n Azzera\r\n </button>\r\n }\r\n </div>\r\n</div>\r\n\r\n<div class=\"fb-res__body\">\r\n <p class=\"fb-section__note\">{{ activeKind().note }}</p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track item.collection + ':' + item.index) {\r\n <div class=\"fb-list__item\" [class.fb-list__item--active]=\"isEditing(item)\">\r\n <div class=\"fb-list__header\">\r\n <!--\r\n Il nome apre la finestra di modifica: qui l\u2019elenco resta un elenco, e cio\u2019 che si\r\n configura non lo copre. \u00C8 la stessa scelta della dialog del dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-res__name\"\r\n [title]=\"'Configura ' + (item.name || 'la risorsa')\"\r\n (click)=\"edit(item)\"\r\n >\r\n <span class=\"fb-res__name-text\">{{ item.name || '(senza nome)' }}</span>\r\n <!--\r\n L\u2019`objectType` sta accanto al tipo perche\u2019 su una `Structure` il tipo da solo non\r\n dice niente: sei variabili su ventuno dicevano \u00ABStructure\u00BB e la classe si leggeva\r\n solo aprendole. E\u2019 anche cio\u2019 che rende spiegabile il filtro, che l\u2019`objectType`\r\n lo guarda: senza, cercare \u00ABdto\u00BB faceva comparire tre righe che \u00ABdto\u00BB non lo\r\n mostrano da nessuna parte \u2014 un filtro che sembra sbagliato.\r\n -->\r\n <span class=\"fb-res__meta\">\r\n {{ string(item, 'dataType') }}{{ boolean(item, 'isCollection') ? '[]' : '' }}\r\n @if (string(item, 'objectType')) {\r\n \u00B7 {{ string(item, 'objectType') }}\r\n }\r\n @if (boolean(item, 'isInput')) {\r\n \u00B7 input\r\n }\r\n @if (boolean(item, 'isOutput')) {\r\n \u00B7 output\r\n }\r\n </span>\r\n </button>\r\n <span class=\"fb-list__spacer\"></span>\r\n <!--\r\n Il numero di usi sta accanto al nome perche\u2019 e\u2019 cio\u2019 che decide se si puo\u2019 togliere:\r\n \u00ABnon usata\u00BB e\u2019 un invito a fare pulizia, \u00ABusata 15 volte\u00BB e\u2019 un avvertimento.\r\n -->\r\n @let uses = usesOf(item);\r\n @if (uses === 0) {\r\n <span class=\"fb-res__uses fb-res__uses--none\" title=\"Nessun riferimento la usa: si puo\u2019 togliere\">\r\n non usata\r\n </span>\r\n } @else {\r\n <!--\r\n Il dettaglio sta nel `title` e non nella riga: in 340px non ci sta, e serve a una\r\n domanda che si fa **dopo** (\u00AB13 dove?\u00BB). Un merge field e\u2019 l\u2019uso che non si vede\r\n scorrendo il documento, quindi dirlo cambia dove si va a cercare.\r\n -->\r\n <span\r\n class=\"fb-res__uses\"\r\n [title]=\"\r\n (uses === 1 ? 'Usata una volta nel documento: ' : 'Usata ' + uses + ' volte nel documento: ') +\r\n usesDetailLabel(item)\r\n \"\r\n >\r\n {{ uses }} {{ uses === 1 ? 'uso' : 'usi' }}\r\n </span>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la risorsa\"\r\n [title]=\"uses ? 'Rimuovi: e\u2019 usata ' + uses + ' volte, verra\u2019 chiesta conferma' : 'Rimuovi'\"\r\n (click)=\"requestRemove(item)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isPendingRemoval(item)) {\r\n <!--\r\n La conferma sta qui e non in una finestra: la riga che si sta togliendo resta sotto\r\n gli occhi, e il numero di riferimenti che si rompono e\u2019 il dato della decisione.\r\n -->\r\n <p class=\"fb-callout fb-callout--warn fb-res__confirm\">\r\n <span>\r\n \u00AB{{ item.name }}\u00BB e\u2019 usata {{ usesOf(item) }} volte: togliendola quei riferimenti\r\n restano orfani e la validazione li segnalera\u2019.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"remove(item)\">Rimuovi comunque</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelRemove()\">\r\n Annulla\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n I rilievi locali restano nell\u2019elenco anche ora che il form sta altrove: senza, per\r\n sapere quali risorse hanno qualcosa che non va bisognerebbe aprirle una per una.\r\n -->\r\n @if (nameError(item); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n }\r\n @if (constantReferencesResource(item)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n }\r\n @if (duplicateStageOrder(item)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n }\r\n @if (ignoredChoiceSetFilters(item); as ignored) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Le opzioni vengono da una collection: {{ ignored }} (CHOICE_SET_FILTERS_IGNORED).\r\n </p>\r\n }\r\n </div>\r\n } @empty {\r\n <!--\r\n Tre casi e tre rimedi: la collection e\u2019 vuota (si aggiunge), il filtro non trova\r\n niente qui ma trova altrove (si cambia scheda), il filtro non trova niente da nessuna\r\n parte (si azzera). Un solo \u00ABnessuna risorsa\u00BB per tutti e tre manda a cercare il\r\n difetto dove non c\u2019e\u2019.\r\n -->\r\n @if (!hasFilter()) {\r\n <p class=\"fb-empty\">Nessuna risorsa di questo tipo.</p>\r\n } @else if (matchesElsewhere() > 0) {\r\n <p class=\"fb-empty\">\r\n Nessuna corrispondenza in questa scheda, ma {{ matchesElsewhere() }} in altre: il\r\n numero accanto a ognuna dice dove.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessuna risorsa corrisponde al filtro.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"resetFilter()\">\r\n Azzera il filtro\r\n </button>\r\n </p>\r\n }\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"add()\">\r\n Aggiungi {{ activeKind().singular }}\r\n </button>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-res__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-res__tabs{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__tab{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__tab:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-res__tab--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-res__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-res__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-res__name{flex:1;min-width:0;display:flex;flex-direction:column;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-res__name-text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__meta{font-size:10px;color:var(--fb-text-muted, #667085);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-res__uses{flex:none;padding:1px 6px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap}.fb-res__uses--none{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-res__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.fb-list__item--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,var(--fb-surface-alt, #f8f9fb))}.fb-list__item .fb-list__header{margin-bottom:0}.fb-list__item .fb-list__header+*{margin-top:6px}.fb-res__filter{display:flex;flex-direction:column;gap:6px;padding:8px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-res__search{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-res__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-res__chips{display:flex;flex-wrap:wrap;gap:4px}.fb-res__chip{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-res__chip:hover:not(:disabled){background:var(--fb-border, #d6dae1)}.fb-res__chip:disabled{opacity:.45;cursor:default}.fb-res__chip--active{background:var(--fb-accent, #2f6feb);color:#fff;font-weight:600}.fb-res__chip--active .fb-res__count{background:color-mix(in srgb,#fff 30%,transparent);color:#fff}.fb-res__chip--reset{margin-left:auto}.fb-res__tab--dim{opacity:.4}.fb-res__count--match{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 20%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-empty .fb-btn{margin-left:6px}\n"] }]
|
|
15709
16208
|
}], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], editRequested: [{ type: i0.Output, args: ["editRequested"] }] } });
|
|
15710
16209
|
|
|
15711
16210
|
/**
|
|
@@ -15895,6 +16394,14 @@ class ResourceFormComponent {
|
|
|
15895
16394
|
delete resource['sortOrder'];
|
|
15896
16395
|
});
|
|
15897
16396
|
}
|
|
16397
|
+
/**
|
|
16398
|
+
* Quante volte il nome di questa risorsa compare dentro un'**espressione**.
|
|
16399
|
+
*
|
|
16400
|
+
* E' l'unica delle tre categorie che la rinomina non tocca (§6.3: la sintassi e' del motore), e
|
|
16401
|
+
* va detta dove si rinomina: il conteggio della riga lo dice già nel suo `title`, ma chi sta
|
|
16402
|
+
* cambiando il nome sta guardando questo campo.
|
|
16403
|
+
*/
|
|
16404
|
+
formulaUseCount = computed(() => describeNameUses(this.store.document(), this.reference().name).formulas, ...(ngDevMode ? [{ debugName: "formulaUseCount" }] : []));
|
|
15898
16405
|
/** Su un choice set da enum il tipo e' `enumType`: il campo generico sarebbe un doppione. */
|
|
15899
16406
|
hidesObjectType() {
|
|
15900
16407
|
return this.collection() === 'dynamicChoiceSets' && this.choiceSetSource() === 'enum';
|
|
@@ -15982,7 +16489,7 @@ class ResourceFormComponent {
|
|
|
15982
16489
|
return this.resource()['value'];
|
|
15983
16490
|
}
|
|
15984
16491
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ResourceFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
15985
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ResourceFormComponent, isStandalone: true, selector: "fb-resource-form", inputs: { reference: { classPropertyName: "reference", publicName: "reference", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!--\r\n Il form di **una** risorsa: e' cio' che prima stava inline sotto la riga dell'elenco, e ora\r\n vive nella dialog. I rilievi locali stanno in cima e non accanto al campo che li produce\r\n perche' qui il form si apre gi\u00E0 puntato sulla risorsa: la domanda \u00ABcosa non va\u00BB viene prima\r\n di \u00ABdove\u00BB.\r\n-->\r\n@if (nameError(); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n}\r\n@if (constantReferencesResource()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n}\r\n@if (duplicateStageOrder()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"reference().name\"\r\n (change)=\"rename($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il nome vive nello stesso spazio dei nomi degli elementi. Rinominare riscrive i riferimenti.\r\n </p>\r\n</div>\r\n\r\n@if (collection() === 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('label')\"\r\n (input)=\"setField('label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Ordine</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('stageOrder')\"\r\n (input)=\"setNumberField('stageOrder', $any($event.target).value)\"\r\n />\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isActive')\"\r\n (change)=\"setBooleanField('isActive', $any($event.target).checked)\"\r\n />\r\n Attivo all\u2019avvio\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n All\u2019avvio lo stage corrente e\u2019 il primo attivo per ordine. Si avanza con un Assignment su\r\n <code>$Flow.CurrentStage</code>.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'textTemplates') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"string('text')\"\r\n placeholder=\"Gentile {!Cliente.Nome},\"\r\n (input)=\"setField('text', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isViewedAsPlainText')\"\r\n (change)=\"setBooleanField('isViewedAsPlainText', $any($event.target).checked)\"\r\n />\r\n Testo semplice\r\n </label>\r\n}\r\n\r\n@if (collection() !== 'textTemplates' && collection() !== 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('dataType')\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 il valore di un'opzione **generata** non lo scrive l'autore del flow: lo produce\r\n la sorgente, nel tipo in cui il sistema ospite lo tiene, e `dataType` decide il tipo in\r\n cui viaggia. La regola e' generale \u2014 vale per tutte e tre le sorgenti \u2014 e si vede su un\r\n oggetto la cui chiave e' un enum, dove nella tendina serve di norma il numero.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Decide il tipo in cui viaggia il valore dell\u2019opzione, con qualunque sorgente:\r\n <code>String</code> il nome come testo, <code>Integer</code> o <code>Number</code> il\r\n numero sottostante, <code>Enum</code> il valore tipizzato \u2014 che e\u2019 cio\u2019 che serve in un\r\n parametro o in un membro di classe <code>Enum</code>. Una conversione impossibile non fa\r\n fallire niente: resta il valore prodotto dalla sorgente.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType() && !hidesObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n @if (isEnum()) {\r\n Tipo di enumerazione\r\n } @else if (isStructure()) {\r\n Classe\r\n } @else {\r\n Oggetto\r\n }\r\n </label>\r\n @if (isEnum()) {\r\n <!-- Dizionario chiuso: qui la scrittura libera non serve. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('objectType')\"\r\n (change)=\"setField('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else if (isStructure()) {\r\n <!-- \u00A74.7: una classe del backend, non un'entita' del modello dati. -->\r\n <fb-structure-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Classe\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n }\r\n @if (missingObjectType()) {\r\n <!-- Su una Structure non e' un avviso: senza classe non c'e' nulla da istanziare. -->\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n } @else if (isStructure()) {\r\n <p class=\"fb-field__hint\">\r\n Il flow ne legge e scrive i <strong>membri</strong> (<code>Nome.Membro</code>): non si\r\n interroga con Get Records ne\u2019 si salva con Create/Update.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Obbligatorio per Object ed Enum (OBJECT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (supportsScale()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"string('scale')\"\r\n (input)=\"setNumberField('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n}\r\n\r\n@if (collection() === 'variables') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isCollection')\"\r\n (change)=\"setBooleanField('isCollection', $any($event.target).checked)\"\r\n />\r\n \u00C8 una collection\r\n </label>\r\n <p class=\"fb-field__hint\">Solo una collection puo\u2019 essere iterata da un Loop.</p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isInput')\"\r\n (change)=\"setBooleanField('isInput', $any($event.target).checked)\"\r\n />\r\n Valorizzabile all\u2019avvio (input)\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isOutput')\"\r\n (change)=\"setBooleanField('isOutput', $any($event.target).checked)\"\r\n />\r\n Leggibile alla fine (output)\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Input e output sono il contratto del flow verso chi lo invoca, subflow compresi.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'formulas') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Espressione</label>\r\n <!--\r\n Il tipo atteso e' quello che la risorsa dichiara, e `scale` va con lui: sono cio'\r\n che permette al motore di dire che l'espressione produce altro (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"string('expression')\"\r\n usage=\"Resource\"\r\n [expectedDataType]=\"$any(resource()['dataType'])\"\r\n [scale]=\"$any(resource()['scale'])\"\r\n placeholder=\"Importo * 1.22\"\r\n ariaLabel=\"Espressione della formula\"\r\n (expressionChange)=\"setField('expression', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Passata verbatim al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n}\r\n\r\n@if (collection() === 'choices') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo mostrato</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('choiceText')\"\r\n (input)=\"setField('choiceText', $any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 le sorgenti sono tre e si escludono a vicenda: i pulsanti le rendono\r\n mutuamente esclusive nel documento, non solo nel form.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Da dove arrivano le opzioni</label>\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Sorgente delle opzioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('collection')\"\r\n (click)=\"setChoiceSetSource('collection')\"\r\n >\r\n Collection del flow\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('object')\"\r\n (click)=\"setChoiceSetSource('object')\"\r\n >\r\n Query su un oggetto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('enum')\"\r\n (click)=\"setChoiceSetSource('enum')\"\r\n >\r\n Valori di un enum\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (ambiguousChoiceSetSource()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo choice set dichiara piu\u2019 di una sorgente: il runtime ne usa una sola\r\n (CHOICE_SET_SOURCE_AMBIGUOUS). Scegli quella giusta qui sopra: le altre vengono tolte.\r\n </p>\r\n }\r\n\r\n @if (isChoiceSetSource('collection')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection</label>\r\n <fb-reference-picker\r\n [value]=\"string('collectionReference')\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setField('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Tipicamente il risultato di un Get Records: le opzioni sono i record che il flow ha gi\u00E0\r\n letto, <strong>nell\u2019ordine in cui ci sono</strong>. Filtri e ordinamento non si applicano\r\n qui: per sceglierne una parte, filtra la collection con un elemento Filter prima dello\r\n screen.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('object')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"string('object') || undefined\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('object', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di enumerazione</label>\r\n <!-- Dizionario chiuso, e qui e' anche **autorevole**: un tipo fuori elenco e' un errore. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('enumType')\"\r\n (change)=\"setField('enumType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumType()) {\r\n <p class=\"fb-field__error\">\r\n Il tipo \u00AB{{ string('enumType') }}\u00BB non e\u2019 fra quelli utilizzabili\r\n (ENUM_TYPE_UNKNOWN).\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Le opzioni sono i valori dichiarati dal tipo: nessuna choice da tenere allineata a mano.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <!--\r\n \u00A75.2 \u2014 qui i campi citabili sono le tre proprieta' di un valore di enum, non i campi\r\n di un'entita': l'elenco viene dal dizionario, e i default rendono superfluo dichiararli.\r\n -->\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo mostrato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('displayField')\"\r\n (change)=\"setField('displayField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultDisplayField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('displayField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('displayField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del valore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('valueField')\"\r\n (change)=\"setField('valueField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultValueField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('valueField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('valueField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Dichiarare <code>Integer</code> come tipo della risorsa e\u2019 la stessa cosa che scegliere qui\r\n \u00ABValore numerico\u00BB: il valore dell\u2019opzione e\u2019 il numero sottostante.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo mostrato</label>\r\n <fb-field-picker\r\n [value]=\"string('displayField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo mostrato\"\r\n (valueChange)=\"setField('displayField', $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo del valore</label>\r\n <fb-field-picker\r\n [value]=\"string('valueField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo del valore\"\r\n (valueChange)=\"setField('valueField', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n <!--\r\n \u00A75.2 \u2014 `filters`, `filterLogic`, `sortField` e `sortOrder` valgono sulle **sole** sorgenti\r\n `object` (dove diventano la query) ed `enumType` (valutati in memoria). Su una collection le\r\n opzioni sono i suoi elementi nell\u2019ordine in cui ci sono: mostrare i controlli l\u00EC\r\n inviterebbe a dichiarare cio\u2019 che il runtime ignora, ed e\u2019 `CHOICE_SET_FILTERS_IGNORED`.\r\n -->\r\n @if (!isChoiceSetSource('collection')) {\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n @if (isChoiceSetSource('enum')) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortField')\"\r\n (change)=\"setField('sortField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Ordine di dichiarazione</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('sortField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('sortField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"string('sortField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setField('sortField', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortOrder')\"\r\n (change)=\"setField('sortOrder', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- `limit` e' l'unico dei quattro che vale su tutt'e tre le sorgenti (\u00A75.2). -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('limit')\"\r\n (input)=\"setNumberField('limit', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (!isChoiceSetSource('collection')) {\r\n <!--\r\n `supportsLogic` a true: dalla revisione del contratto `filterLogic` **esiste** anche qui, con\r\n le stesse forme e gli stessi rilievi degli elementi che interrogano il database (\u00A74.4).\r\n Nessun `emptyWarning`: senza filtri il set legge tutti i record dell\u2019oggetto, che qui e\u2019 un\r\n uso legittimo. Su un enum i filtri li valuta il runtime **in memoria**, e i campi sono le tre\r\n proprieta\u2019 di un valore: `fieldOptions` e\u2019 cio\u2019 che sostituisce il catalogo (\u00A75.2).\r\n -->\r\n <fb-record-filter-editor\r\n [holder]=\"$any(resource())\"\r\n [object]=\"isChoiceSetSource('enum') ? undefined : string('object') || undefined\"\r\n [fieldOptions]=\"isChoiceSetSource('enum') ? enumChoiceSetFields() : []\"\r\n [title]=\"\r\n isChoiceSetSource('enum') ? 'Quali valori diventano opzioni' : 'Quali record diventano opzioni'\r\n \"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n } @else if (ignoredChoiceSetFilters(); as ignored) {\r\n <!--\r\n Il documento dichiara cio' che su una collection il runtime ignora. E\u2019 un **avviso**, e la\r\n ripulitura e\u2019 un comando: cancellare da se\u2019 i filtri di un flow scritto altrove sarebbe una\r\n modifica che nessuno ha chiesto, e farebbe sparire l\u2019unico indizio di cosa quel flow\r\n voleva selezionare. E\u2019 la stessa scelta dei membri di un riquadro (\u00A73.6).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Su una collection {{ ignored }}: le opzioni sono gli elementi della collection nell\u2019ordine in\r\n cui ci sono (CHOICE_SET_FILTERS_IGNORED).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"clearIgnoredChoiceSetFilters()\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isStructure() && collection() === 'variables') {\r\n <p class=\"fb-field__hint\">\r\n Non serve un valore iniziale: la variabile parte con un\u2019istanza vuota, e il flow ne assegna i\r\n membri uno alla volta con un Assignment.\r\n </p>\r\n}\r\n\r\n@if (\r\n supportsInitialValue() &&\r\n (collection() === 'variables' || collection() === 'constants' || collection() === 'choices')\r\n) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ collection() === 'variables' ? 'Valore iniziale' : 'Valore' }}\r\n </label>\r\n <fb-value-editor\r\n [value]=\"value()\"\r\n [dataType]=\"$any(resource()['dataType'])\"\r\n [objectType]=\"$any(resource()['objectType'])\"\r\n [isCollection]=\"boolean('isCollection')\"\r\n [allowFormula]=\"collection() !== 'constants'\"\r\n label=\"Valore\"\r\n (valueChange)=\"setValue($event)\"\r\n />\r\n </div>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('description')\"\r\n (input)=\"setField('description', $any($event.target).value)\"\r\n />\r\n</div>\r\n", dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
16492
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ResourceFormComponent, isStandalone: true, selector: "fb-resource-form", inputs: { reference: { classPropertyName: "reference", publicName: "reference", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!--\r\n Il form di **una** risorsa: e' cio' che prima stava inline sotto la riga dell'elenco, e ora\r\n vive nella dialog. I rilievi locali stanno in cima e non accanto al campo che li produce\r\n perche' qui il form si apre gi\u00E0 puntato sulla risorsa: la domanda \u00ABcosa non va\u00BB viene prima\r\n di \u00ABdove\u00BB.\r\n-->\r\n@if (nameError(); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n}\r\n@if (constantReferencesResource()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n}\r\n@if (duplicateStageOrder()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"reference().name\"\r\n (change)=\"rename($any($event.target).value)\"\r\n />\r\n <!--\r\n Le tre categorie non si comportano allo stesso modo davanti a una rinomina, e questo e' il\r\n punto in cui il gesto avviene: i riferimenti e i merge field li riscrive l\u2019editor, le\r\n espressioni delle formule no \u2014 la loro sintassi e\u2019 del motore. Dirlo altrove vorrebbe dire\r\n dirlo a chi non sta rinominando.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Il nome vive nello stesso spazio dei nomi degli elementi. Rinominare riscrive i riferimenti e i\r\n merge field <code>{!\u2026}</code> nei testi.\r\n </p>\r\n @if (formulaUseCount()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Attenzione: {{ formulaUseCount() }}\r\n {{ formulaUseCount() === 1 ? 'occorrenza sta' : 'occorrenze stanno' }} dentro\r\n {{ formulaUseCount() === 1 ? 'una formula' : 'delle formule' }} e\r\n <strong>non {{ formulaUseCount() === 1 ? 'viene' : 'vengono' }} riscritt{{ formulaUseCount() === 1 ? 'a' : 'e' }}</strong>:\r\n la sintassi e\u2019 del motore di regole, quindi\r\n {{ formulaUseCount() === 1 ? 'va corretta' : 'vanno corrette' }} a mano.\r\n </p>\r\n }\r\n</div>\r\n\r\n@if (collection() === 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('label')\"\r\n (input)=\"setField('label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Ordine</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('stageOrder')\"\r\n (input)=\"setNumberField('stageOrder', $any($event.target).value)\"\r\n />\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isActive')\"\r\n (change)=\"setBooleanField('isActive', $any($event.target).checked)\"\r\n />\r\n Attivo all\u2019avvio\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n All\u2019avvio lo stage corrente e\u2019 il primo attivo per ordine. Si avanza con un Assignment su\r\n <code>$Flow.CurrentStage</code>.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'textTemplates') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"string('text')\"\r\n placeholder=\"Gentile {!Cliente.Nome},\"\r\n (input)=\"setField('text', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isViewedAsPlainText')\"\r\n (change)=\"setBooleanField('isViewedAsPlainText', $any($event.target).checked)\"\r\n />\r\n Testo semplice\r\n </label>\r\n}\r\n\r\n@if (collection() !== 'textTemplates' && collection() !== 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('dataType')\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 il valore di un'opzione **generata** non lo scrive l'autore del flow: lo produce\r\n la sorgente, nel tipo in cui il sistema ospite lo tiene, e `dataType` decide il tipo in\r\n cui viaggia. La regola e' generale \u2014 vale per tutte e tre le sorgenti \u2014 e si vede su un\r\n oggetto la cui chiave e' un enum, dove nella tendina serve di norma il numero.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Decide il tipo in cui viaggia il valore dell\u2019opzione, con qualunque sorgente:\r\n <code>String</code> il nome come testo, <code>Integer</code> o <code>Number</code> il\r\n numero sottostante, <code>Enum</code> il valore tipizzato \u2014 che e\u2019 cio\u2019 che serve in un\r\n parametro o in un membro di classe <code>Enum</code>. Una conversione impossibile non fa\r\n fallire niente: resta il valore prodotto dalla sorgente.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType() && !hidesObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n @if (isEnum()) {\r\n Tipo di enumerazione\r\n } @else if (isStructure()) {\r\n Classe\r\n } @else {\r\n Oggetto\r\n }\r\n </label>\r\n @if (isEnum()) {\r\n <!-- Dizionario chiuso: qui la scrittura libera non serve. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('objectType')\"\r\n (change)=\"setField('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else if (isStructure()) {\r\n <!-- \u00A74.7: una classe del backend, non un'entita' del modello dati. -->\r\n <fb-structure-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Classe\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n }\r\n @if (missingObjectType()) {\r\n <!-- Su una Structure non e' un avviso: senza classe non c'e' nulla da istanziare. -->\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n } @else if (isStructure()) {\r\n <p class=\"fb-field__hint\">\r\n Il flow ne legge e scrive i <strong>membri</strong> (<code>Nome.Membro</code>): non si\r\n interroga con Get Records ne\u2019 si salva con Create/Update.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Obbligatorio per Object ed Enum (OBJECT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (supportsScale()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"string('scale')\"\r\n (input)=\"setNumberField('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n}\r\n\r\n@if (collection() === 'variables') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isCollection')\"\r\n (change)=\"setBooleanField('isCollection', $any($event.target).checked)\"\r\n />\r\n \u00C8 una collection\r\n </label>\r\n <p class=\"fb-field__hint\">Solo una collection puo\u2019 essere iterata da un Loop.</p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isInput')\"\r\n (change)=\"setBooleanField('isInput', $any($event.target).checked)\"\r\n />\r\n Valorizzabile all\u2019avvio (input)\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isOutput')\"\r\n (change)=\"setBooleanField('isOutput', $any($event.target).checked)\"\r\n />\r\n Leggibile alla fine (output)\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Input e output sono il contratto del flow verso chi lo invoca, subflow compresi.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'formulas') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Espressione</label>\r\n <!--\r\n Il tipo atteso e' quello che la risorsa dichiara, e `scale` va con lui: sono cio'\r\n che permette al motore di dire che l'espressione produce altro (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"string('expression')\"\r\n usage=\"Resource\"\r\n [expectedDataType]=\"$any(resource()['dataType'])\"\r\n [scale]=\"$any(resource()['scale'])\"\r\n placeholder=\"Importo * 1.22\"\r\n ariaLabel=\"Espressione della formula\"\r\n (expressionChange)=\"setField('expression', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Passata verbatim al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n}\r\n\r\n@if (collection() === 'choices') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo mostrato</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('choiceText')\"\r\n (input)=\"setField('choiceText', $any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 le sorgenti sono tre e si escludono a vicenda: i pulsanti le rendono\r\n mutuamente esclusive nel documento, non solo nel form.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Da dove arrivano le opzioni</label>\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Sorgente delle opzioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('collection')\"\r\n (click)=\"setChoiceSetSource('collection')\"\r\n >\r\n Collection del flow\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('object')\"\r\n (click)=\"setChoiceSetSource('object')\"\r\n >\r\n Query su un oggetto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('enum')\"\r\n (click)=\"setChoiceSetSource('enum')\"\r\n >\r\n Valori di un enum\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (ambiguousChoiceSetSource()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo choice set dichiara piu\u2019 di una sorgente: il runtime ne usa una sola\r\n (CHOICE_SET_SOURCE_AMBIGUOUS). Scegli quella giusta qui sopra: le altre vengono tolte.\r\n </p>\r\n }\r\n\r\n @if (isChoiceSetSource('collection')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection</label>\r\n <fb-reference-picker\r\n [value]=\"string('collectionReference')\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setField('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Tipicamente il risultato di un Get Records: le opzioni sono i record che il flow ha gi\u00E0\r\n letto, <strong>nell\u2019ordine in cui ci sono</strong>. Filtri e ordinamento non si applicano\r\n qui: per sceglierne una parte, filtra la collection con un elemento Filter prima dello\r\n screen.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('object')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"string('object') || undefined\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('object', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di enumerazione</label>\r\n <!-- Dizionario chiuso, e qui e' anche **autorevole**: un tipo fuori elenco e' un errore. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('enumType')\"\r\n (change)=\"setField('enumType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumType()) {\r\n <p class=\"fb-field__error\">\r\n Il tipo \u00AB{{ string('enumType') }}\u00BB non e\u2019 fra quelli utilizzabili\r\n (ENUM_TYPE_UNKNOWN).\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Le opzioni sono i valori dichiarati dal tipo: nessuna choice da tenere allineata a mano.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <!--\r\n \u00A75.2 \u2014 qui i campi citabili sono le tre proprieta' di un valore di enum, non i campi\r\n di un'entita': l'elenco viene dal dizionario, e i default rendono superfluo dichiararli.\r\n -->\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo mostrato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('displayField')\"\r\n (change)=\"setField('displayField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultDisplayField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('displayField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('displayField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del valore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('valueField')\"\r\n (change)=\"setField('valueField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultValueField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('valueField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('valueField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Dichiarare <code>Integer</code> come tipo della risorsa e\u2019 la stessa cosa che scegliere qui\r\n \u00ABValore numerico\u00BB: il valore dell\u2019opzione e\u2019 il numero sottostante.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo mostrato</label>\r\n <fb-field-picker\r\n [value]=\"string('displayField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo mostrato\"\r\n (valueChange)=\"setField('displayField', $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo del valore</label>\r\n <fb-field-picker\r\n [value]=\"string('valueField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo del valore\"\r\n (valueChange)=\"setField('valueField', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n <!--\r\n \u00A75.2 \u2014 `filters`, `filterLogic`, `sortField` e `sortOrder` valgono sulle **sole** sorgenti\r\n `object` (dove diventano la query) ed `enumType` (valutati in memoria). Su una collection le\r\n opzioni sono i suoi elementi nell\u2019ordine in cui ci sono: mostrare i controlli l\u00EC\r\n inviterebbe a dichiarare cio\u2019 che il runtime ignora, ed e\u2019 `CHOICE_SET_FILTERS_IGNORED`.\r\n -->\r\n @if (!isChoiceSetSource('collection')) {\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n @if (isChoiceSetSource('enum')) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortField')\"\r\n (change)=\"setField('sortField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Ordine di dichiarazione</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('sortField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('sortField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"string('sortField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setField('sortField', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortOrder')\"\r\n (change)=\"setField('sortOrder', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- `limit` e' l'unico dei quattro che vale su tutt'e tre le sorgenti (\u00A75.2). -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('limit')\"\r\n (input)=\"setNumberField('limit', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (!isChoiceSetSource('collection')) {\r\n <!--\r\n `supportsLogic` a true: dalla revisione del contratto `filterLogic` **esiste** anche qui, con\r\n le stesse forme e gli stessi rilievi degli elementi che interrogano il database (\u00A74.4).\r\n Nessun `emptyWarning`: senza filtri il set legge tutti i record dell\u2019oggetto, che qui e\u2019 un\r\n uso legittimo. Su un enum i filtri li valuta il runtime **in memoria**, e i campi sono le tre\r\n proprieta\u2019 di un valore: `fieldOptions` e\u2019 cio\u2019 che sostituisce il catalogo (\u00A75.2).\r\n -->\r\n <fb-record-filter-editor\r\n [holder]=\"$any(resource())\"\r\n [object]=\"isChoiceSetSource('enum') ? undefined : string('object') || undefined\"\r\n [fieldOptions]=\"isChoiceSetSource('enum') ? enumChoiceSetFields() : []\"\r\n [title]=\"\r\n isChoiceSetSource('enum') ? 'Quali valori diventano opzioni' : 'Quali record diventano opzioni'\r\n \"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n } @else if (ignoredChoiceSetFilters(); as ignored) {\r\n <!--\r\n Il documento dichiara cio' che su una collection il runtime ignora. E\u2019 un **avviso**, e la\r\n ripulitura e\u2019 un comando: cancellare da se\u2019 i filtri di un flow scritto altrove sarebbe una\r\n modifica che nessuno ha chiesto, e farebbe sparire l\u2019unico indizio di cosa quel flow\r\n voleva selezionare. E\u2019 la stessa scelta dei membri di un riquadro (\u00A73.6).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Su una collection {{ ignored }}: le opzioni sono gli elementi della collection nell\u2019ordine in\r\n cui ci sono (CHOICE_SET_FILTERS_IGNORED).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"clearIgnoredChoiceSetFilters()\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isStructure() && collection() === 'variables') {\r\n <p class=\"fb-field__hint\">\r\n Non serve un valore iniziale: la variabile parte con un\u2019istanza vuota, e il flow ne assegna i\r\n membri uno alla volta con un Assignment.\r\n </p>\r\n}\r\n\r\n@if (\r\n supportsInitialValue() &&\r\n (collection() === 'variables' || collection() === 'constants' || collection() === 'choices')\r\n) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ collection() === 'variables' ? 'Valore iniziale' : 'Valore' }}\r\n </label>\r\n <fb-value-editor\r\n [value]=\"value()\"\r\n [dataType]=\"$any(resource()['dataType'])\"\r\n [objectType]=\"$any(resource()['objectType'])\"\r\n [isCollection]=\"boolean('isCollection')\"\r\n [allowFormula]=\"collection() !== 'constants'\"\r\n label=\"Valore\"\r\n (valueChange)=\"setValue($event)\"\r\n />\r\n </div>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('description')\"\r\n (input)=\"setField('description', $any($event.target).value)\"\r\n />\r\n</div>\r\n", dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: StructurePickerComponent, selector: "fb-structure-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15986
16493
|
}
|
|
15987
16494
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ResourceFormComponent, decorators: [{
|
|
15988
16495
|
type: Component,
|
|
@@ -15995,7 +16502,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
15995
16502
|
StructurePickerComponent,
|
|
15996
16503
|
ValueEditorComponent,
|
|
15997
16504
|
SelectValueDirective,
|
|
15998
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\r\n Il form di **una** risorsa: e' cio' che prima stava inline sotto la riga dell'elenco, e ora\r\n vive nella dialog. I rilievi locali stanno in cima e non accanto al campo che li produce\r\n perche' qui il form si apre gi\u00E0 puntato sulla risorsa: la domanda \u00ABcosa non va\u00BB viene prima\r\n di \u00ABdove\u00BB.\r\n-->\r\n@if (nameError(); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n}\r\n@if (constantReferencesResource()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n}\r\n@if (duplicateStageOrder()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"reference().name\"\r\n (change)=\"rename($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il nome vive nello stesso spazio dei nomi degli elementi. Rinominare riscrive i riferimenti.\r\n </p>\r\n</div>\r\n\r\n@if (collection() === 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('label')\"\r\n (input)=\"setField('label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Ordine</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('stageOrder')\"\r\n (input)=\"setNumberField('stageOrder', $any($event.target).value)\"\r\n />\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isActive')\"\r\n (change)=\"setBooleanField('isActive', $any($event.target).checked)\"\r\n />\r\n Attivo all\u2019avvio\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n All\u2019avvio lo stage corrente e\u2019 il primo attivo per ordine. Si avanza con un Assignment su\r\n <code>$Flow.CurrentStage</code>.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'textTemplates') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"string('text')\"\r\n placeholder=\"Gentile {!Cliente.Nome},\"\r\n (input)=\"setField('text', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isViewedAsPlainText')\"\r\n (change)=\"setBooleanField('isViewedAsPlainText', $any($event.target).checked)\"\r\n />\r\n Testo semplice\r\n </label>\r\n}\r\n\r\n@if (collection() !== 'textTemplates' && collection() !== 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('dataType')\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 il valore di un'opzione **generata** non lo scrive l'autore del flow: lo produce\r\n la sorgente, nel tipo in cui il sistema ospite lo tiene, e `dataType` decide il tipo in\r\n cui viaggia. La regola e' generale \u2014 vale per tutte e tre le sorgenti \u2014 e si vede su un\r\n oggetto la cui chiave e' un enum, dove nella tendina serve di norma il numero.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Decide il tipo in cui viaggia il valore dell\u2019opzione, con qualunque sorgente:\r\n <code>String</code> il nome come testo, <code>Integer</code> o <code>Number</code> il\r\n numero sottostante, <code>Enum</code> il valore tipizzato \u2014 che e\u2019 cio\u2019 che serve in un\r\n parametro o in un membro di classe <code>Enum</code>. Una conversione impossibile non fa\r\n fallire niente: resta il valore prodotto dalla sorgente.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType() && !hidesObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n @if (isEnum()) {\r\n Tipo di enumerazione\r\n } @else if (isStructure()) {\r\n Classe\r\n } @else {\r\n Oggetto\r\n }\r\n </label>\r\n @if (isEnum()) {\r\n <!-- Dizionario chiuso: qui la scrittura libera non serve. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('objectType')\"\r\n (change)=\"setField('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else if (isStructure()) {\r\n <!-- \u00A74.7: una classe del backend, non un'entita' del modello dati. -->\r\n <fb-structure-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Classe\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n }\r\n @if (missingObjectType()) {\r\n <!-- Su una Structure non e' un avviso: senza classe non c'e' nulla da istanziare. -->\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n } @else if (isStructure()) {\r\n <p class=\"fb-field__hint\">\r\n Il flow ne legge e scrive i <strong>membri</strong> (<code>Nome.Membro</code>): non si\r\n interroga con Get Records ne\u2019 si salva con Create/Update.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Obbligatorio per Object ed Enum (OBJECT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (supportsScale()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"string('scale')\"\r\n (input)=\"setNumberField('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n}\r\n\r\n@if (collection() === 'variables') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isCollection')\"\r\n (change)=\"setBooleanField('isCollection', $any($event.target).checked)\"\r\n />\r\n \u00C8 una collection\r\n </label>\r\n <p class=\"fb-field__hint\">Solo una collection puo\u2019 essere iterata da un Loop.</p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isInput')\"\r\n (change)=\"setBooleanField('isInput', $any($event.target).checked)\"\r\n />\r\n Valorizzabile all\u2019avvio (input)\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isOutput')\"\r\n (change)=\"setBooleanField('isOutput', $any($event.target).checked)\"\r\n />\r\n Leggibile alla fine (output)\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Input e output sono il contratto del flow verso chi lo invoca, subflow compresi.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'formulas') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Espressione</label>\r\n <!--\r\n Il tipo atteso e' quello che la risorsa dichiara, e `scale` va con lui: sono cio'\r\n che permette al motore di dire che l'espressione produce altro (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"string('expression')\"\r\n usage=\"Resource\"\r\n [expectedDataType]=\"$any(resource()['dataType'])\"\r\n [scale]=\"$any(resource()['scale'])\"\r\n placeholder=\"Importo * 1.22\"\r\n ariaLabel=\"Espressione della formula\"\r\n (expressionChange)=\"setField('expression', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Passata verbatim al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n}\r\n\r\n@if (collection() === 'choices') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo mostrato</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('choiceText')\"\r\n (input)=\"setField('choiceText', $any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 le sorgenti sono tre e si escludono a vicenda: i pulsanti le rendono\r\n mutuamente esclusive nel documento, non solo nel form.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Da dove arrivano le opzioni</label>\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Sorgente delle opzioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('collection')\"\r\n (click)=\"setChoiceSetSource('collection')\"\r\n >\r\n Collection del flow\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('object')\"\r\n (click)=\"setChoiceSetSource('object')\"\r\n >\r\n Query su un oggetto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('enum')\"\r\n (click)=\"setChoiceSetSource('enum')\"\r\n >\r\n Valori di un enum\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (ambiguousChoiceSetSource()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo choice set dichiara piu\u2019 di una sorgente: il runtime ne usa una sola\r\n (CHOICE_SET_SOURCE_AMBIGUOUS). Scegli quella giusta qui sopra: le altre vengono tolte.\r\n </p>\r\n }\r\n\r\n @if (isChoiceSetSource('collection')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection</label>\r\n <fb-reference-picker\r\n [value]=\"string('collectionReference')\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setField('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Tipicamente il risultato di un Get Records: le opzioni sono i record che il flow ha gi\u00E0\r\n letto, <strong>nell\u2019ordine in cui ci sono</strong>. Filtri e ordinamento non si applicano\r\n qui: per sceglierne una parte, filtra la collection con un elemento Filter prima dello\r\n screen.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('object')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"string('object') || undefined\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('object', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di enumerazione</label>\r\n <!-- Dizionario chiuso, e qui e' anche **autorevole**: un tipo fuori elenco e' un errore. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('enumType')\"\r\n (change)=\"setField('enumType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumType()) {\r\n <p class=\"fb-field__error\">\r\n Il tipo \u00AB{{ string('enumType') }}\u00BB non e\u2019 fra quelli utilizzabili\r\n (ENUM_TYPE_UNKNOWN).\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Le opzioni sono i valori dichiarati dal tipo: nessuna choice da tenere allineata a mano.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <!--\r\n \u00A75.2 \u2014 qui i campi citabili sono le tre proprieta' di un valore di enum, non i campi\r\n di un'entita': l'elenco viene dal dizionario, e i default rendono superfluo dichiararli.\r\n -->\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo mostrato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('displayField')\"\r\n (change)=\"setField('displayField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultDisplayField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('displayField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('displayField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del valore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('valueField')\"\r\n (change)=\"setField('valueField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultValueField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('valueField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('valueField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Dichiarare <code>Integer</code> come tipo della risorsa e\u2019 la stessa cosa che scegliere qui\r\n \u00ABValore numerico\u00BB: il valore dell\u2019opzione e\u2019 il numero sottostante.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo mostrato</label>\r\n <fb-field-picker\r\n [value]=\"string('displayField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo mostrato\"\r\n (valueChange)=\"setField('displayField', $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo del valore</label>\r\n <fb-field-picker\r\n [value]=\"string('valueField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo del valore\"\r\n (valueChange)=\"setField('valueField', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n <!--\r\n \u00A75.2 \u2014 `filters`, `filterLogic`, `sortField` e `sortOrder` valgono sulle **sole** sorgenti\r\n `object` (dove diventano la query) ed `enumType` (valutati in memoria). Su una collection le\r\n opzioni sono i suoi elementi nell\u2019ordine in cui ci sono: mostrare i controlli l\u00EC\r\n inviterebbe a dichiarare cio\u2019 che il runtime ignora, ed e\u2019 `CHOICE_SET_FILTERS_IGNORED`.\r\n -->\r\n @if (!isChoiceSetSource('collection')) {\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n @if (isChoiceSetSource('enum')) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortField')\"\r\n (change)=\"setField('sortField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Ordine di dichiarazione</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('sortField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('sortField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"string('sortField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setField('sortField', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortOrder')\"\r\n (change)=\"setField('sortOrder', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- `limit` e' l'unico dei quattro che vale su tutt'e tre le sorgenti (\u00A75.2). -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('limit')\"\r\n (input)=\"setNumberField('limit', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (!isChoiceSetSource('collection')) {\r\n <!--\r\n `supportsLogic` a true: dalla revisione del contratto `filterLogic` **esiste** anche qui, con\r\n le stesse forme e gli stessi rilievi degli elementi che interrogano il database (\u00A74.4).\r\n Nessun `emptyWarning`: senza filtri il set legge tutti i record dell\u2019oggetto, che qui e\u2019 un\r\n uso legittimo. Su un enum i filtri li valuta il runtime **in memoria**, e i campi sono le tre\r\n proprieta\u2019 di un valore: `fieldOptions` e\u2019 cio\u2019 che sostituisce il catalogo (\u00A75.2).\r\n -->\r\n <fb-record-filter-editor\r\n [holder]=\"$any(resource())\"\r\n [object]=\"isChoiceSetSource('enum') ? undefined : string('object') || undefined\"\r\n [fieldOptions]=\"isChoiceSetSource('enum') ? enumChoiceSetFields() : []\"\r\n [title]=\"\r\n isChoiceSetSource('enum') ? 'Quali valori diventano opzioni' : 'Quali record diventano opzioni'\r\n \"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n } @else if (ignoredChoiceSetFilters(); as ignored) {\r\n <!--\r\n Il documento dichiara cio' che su una collection il runtime ignora. E\u2019 un **avviso**, e la\r\n ripulitura e\u2019 un comando: cancellare da se\u2019 i filtri di un flow scritto altrove sarebbe una\r\n modifica che nessuno ha chiesto, e farebbe sparire l\u2019unico indizio di cosa quel flow\r\n voleva selezionare. E\u2019 la stessa scelta dei membri di un riquadro (\u00A73.6).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Su una collection {{ ignored }}: le opzioni sono gli elementi della collection nell\u2019ordine in\r\n cui ci sono (CHOICE_SET_FILTERS_IGNORED).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"clearIgnoredChoiceSetFilters()\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isStructure() && collection() === 'variables') {\r\n <p class=\"fb-field__hint\">\r\n Non serve un valore iniziale: la variabile parte con un\u2019istanza vuota, e il flow ne assegna i\r\n membri uno alla volta con un Assignment.\r\n </p>\r\n}\r\n\r\n@if (\r\n supportsInitialValue() &&\r\n (collection() === 'variables' || collection() === 'constants' || collection() === 'choices')\r\n) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ collection() === 'variables' ? 'Valore iniziale' : 'Valore' }}\r\n </label>\r\n <fb-value-editor\r\n [value]=\"value()\"\r\n [dataType]=\"$any(resource()['dataType'])\"\r\n [objectType]=\"$any(resource()['objectType'])\"\r\n [isCollection]=\"boolean('isCollection')\"\r\n [allowFormula]=\"collection() !== 'constants'\"\r\n label=\"Valore\"\r\n (valueChange)=\"setValue($event)\"\r\n />\r\n </div>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('description')\"\r\n (input)=\"setField('description', $any($event.target).value)\"\r\n />\r\n</div>\r\n" }]
|
|
16505
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\r\n Il form di **una** risorsa: e' cio' che prima stava inline sotto la riga dell'elenco, e ora\r\n vive nella dialog. I rilievi locali stanno in cima e non accanto al campo che li produce\r\n perche' qui il form si apre gi\u00E0 puntato sulla risorsa: la domanda \u00ABcosa non va\u00BB viene prima\r\n di \u00ABdove\u00BB.\r\n-->\r\n@if (nameError(); as message) {\r\n <p class=\"fb-field__error\">{{ message }}</p>\r\n}\r\n@if (constantReferencesResource()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una costante deve essere un valore fisso: non puo\u2019 referenziare altre risorse\r\n (CONSTANT_REFERENCES_RESOURCE).\r\n </p>\r\n}\r\n@if (duplicateStageOrder()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Due stage con lo stesso ordine: quale sia il corrente all\u2019avvio diventa arbitrario\r\n (STAGE_ORDER_DUPLICATED).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"reference().name\"\r\n (change)=\"rename($any($event.target).value)\"\r\n />\r\n <!--\r\n Le tre categorie non si comportano allo stesso modo davanti a una rinomina, e questo e' il\r\n punto in cui il gesto avviene: i riferimenti e i merge field li riscrive l\u2019editor, le\r\n espressioni delle formule no \u2014 la loro sintassi e\u2019 del motore. Dirlo altrove vorrebbe dire\r\n dirlo a chi non sta rinominando.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Il nome vive nello stesso spazio dei nomi degli elementi. Rinominare riscrive i riferimenti e i\r\n merge field <code>{!\u2026}</code> nei testi.\r\n </p>\r\n @if (formulaUseCount()) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Attenzione: {{ formulaUseCount() }}\r\n {{ formulaUseCount() === 1 ? 'occorrenza sta' : 'occorrenze stanno' }} dentro\r\n {{ formulaUseCount() === 1 ? 'una formula' : 'delle formule' }} e\r\n <strong>non {{ formulaUseCount() === 1 ? 'viene' : 'vengono' }} riscritt{{ formulaUseCount() === 1 ? 'a' : 'e' }}</strong>:\r\n la sintassi e\u2019 del motore di regole, quindi\r\n {{ formulaUseCount() === 1 ? 'va corretta' : 'vanno corrette' }} a mano.\r\n </p>\r\n }\r\n</div>\r\n\r\n@if (collection() === 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('label')\"\r\n (input)=\"setField('label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Ordine</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('stageOrder')\"\r\n (input)=\"setNumberField('stageOrder', $any($event.target).value)\"\r\n />\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isActive')\"\r\n (change)=\"setBooleanField('isActive', $any($event.target).checked)\"\r\n />\r\n Attivo all\u2019avvio\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n All\u2019avvio lo stage corrente e\u2019 il primo attivo per ordine. Si avanza con un Assignment su\r\n <code>$Flow.CurrentStage</code>.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'textTemplates') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"string('text')\"\r\n placeholder=\"Gentile {!Cliente.Nome},\"\r\n (input)=\"setField('text', $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isViewedAsPlainText')\"\r\n (change)=\"setBooleanField('isViewedAsPlainText', $any($event.target).checked)\"\r\n />\r\n Testo semplice\r\n </label>\r\n}\r\n\r\n@if (collection() !== 'textTemplates' && collection() !== 'stages') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('dataType')\"\r\n (change)=\"setDataType($any($event.target).value)\"\r\n >\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 il valore di un'opzione **generata** non lo scrive l'autore del flow: lo produce\r\n la sorgente, nel tipo in cui il sistema ospite lo tiene, e `dataType` decide il tipo in\r\n cui viaggia. La regola e' generale \u2014 vale per tutte e tre le sorgenti \u2014 e si vede su un\r\n oggetto la cui chiave e' un enum, dove nella tendina serve di norma il numero.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Decide il tipo in cui viaggia il valore dell\u2019opzione, con qualunque sorgente:\r\n <code>String</code> il nome come testo, <code>Integer</code> o <code>Number</code> il\r\n numero sottostante, <code>Enum</code> il valore tipizzato \u2014 che e\u2019 cio\u2019 che serve in un\r\n parametro o in un membro di classe <code>Enum</code>. Una conversione impossibile non fa\r\n fallire niente: resta il valore prodotto dalla sorgente.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (requiresObjectType() && !hidesObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">\r\n @if (isEnum()) {\r\n Tipo di enumerazione\r\n } @else if (isStructure()) {\r\n Classe\r\n } @else {\r\n Oggetto\r\n }\r\n </label>\r\n @if (isEnum()) {\r\n <!-- Dizionario chiuso: qui la scrittura libera non serve. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('objectType')\"\r\n (change)=\"setField('objectType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else if (isStructure()) {\r\n <!-- \u00A74.7: una classe del backend, non un'entita' del modello dati. -->\r\n <fb-structure-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Classe\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n } @else {\r\n <fb-object-picker\r\n [value]=\"string('objectType') || undefined\"\r\n label=\"Oggetto\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('objectType', $event ?? '')\"\r\n />\r\n }\r\n @if (missingObjectType()) {\r\n <!-- Su una Structure non e' un avviso: senza classe non c'e' nulla da istanziare. -->\r\n <p class=\"fb-field__error\">\r\n La classe e\u2019 obbligatoria: senza, l\u2019attivazione e\u2019 bloccata (OBJECT_TYPE_MISSING).\r\n </p>\r\n } @else if (isStructure()) {\r\n <p class=\"fb-field__hint\">\r\n Il flow ne legge e scrive i <strong>membri</strong> (<code>Nome.Membro</code>): non si\r\n interroga con Get Records ne\u2019 si salva con Create/Update.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Obbligatorio per Object ed Enum (OBJECT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (supportsScale()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Decimali</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"string('scale')\"\r\n (input)=\"setNumberField('scale', $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n}\r\n\r\n@if (collection() === 'variables') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isCollection')\"\r\n (change)=\"setBooleanField('isCollection', $any($event.target).checked)\"\r\n />\r\n \u00C8 una collection\r\n </label>\r\n <p class=\"fb-field__hint\">Solo una collection puo\u2019 essere iterata da un Loop.</p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isInput')\"\r\n (change)=\"setBooleanField('isInput', $any($event.target).checked)\"\r\n />\r\n Valorizzabile all\u2019avvio (input)\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"boolean('isOutput')\"\r\n (change)=\"setBooleanField('isOutput', $any($event.target).checked)\"\r\n />\r\n Leggibile alla fine (output)\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n Input e output sono il contratto del flow verso chi lo invoca, subflow compresi.\r\n </p>\r\n}\r\n\r\n@if (collection() === 'formulas') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Espressione</label>\r\n <!--\r\n Il tipo atteso e' quello che la risorsa dichiara, e `scale` va con lui: sono cio'\r\n che permette al motore di dire che l'espressione produce altro (\u00A76.3).\r\n -->\r\n <fb-formula-editor\r\n [expression]=\"string('expression')\"\r\n usage=\"Resource\"\r\n [expectedDataType]=\"$any(resource()['dataType'])\"\r\n [scale]=\"$any(resource()['scale'])\"\r\n placeholder=\"Importo * 1.22\"\r\n ariaLabel=\"Espressione della formula\"\r\n (expressionChange)=\"setField('expression', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Passata verbatim al motore di regole: la sintassi delle funzioni e\u2019 del motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n}\r\n\r\n@if (collection() === 'choices') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Testo mostrato</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('choiceText')\"\r\n (input)=\"setField('choiceText', $any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (collection() === 'dynamicChoiceSets') {\r\n <!--\r\n \u00A75.2 \u2014 le sorgenti sono tre e si escludono a vicenda: i pulsanti le rendono\r\n mutuamente esclusive nel documento, non solo nel form.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Da dove arrivano le opzioni</label>\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Sorgente delle opzioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('collection')\"\r\n (click)=\"setChoiceSetSource('collection')\"\r\n >\r\n Collection del flow\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('object')\"\r\n (click)=\"setChoiceSetSource('object')\"\r\n >\r\n Query su un oggetto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"isChoiceSetSource('enum')\"\r\n (click)=\"setChoiceSetSource('enum')\"\r\n >\r\n Valori di un enum\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (ambiguousChoiceSetSource()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo choice set dichiara piu\u2019 di una sorgente: il runtime ne usa una sola\r\n (CHOICE_SET_SOURCE_AMBIGUOUS). Scegli quella giusta qui sopra: le altre vengono tolte.\r\n </p>\r\n }\r\n\r\n @if (isChoiceSetSource('collection')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection</label>\r\n <fb-reference-picker\r\n [value]=\"string('collectionReference')\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setField('collectionReference', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Tipicamente il risultato di un Get Records: le opzioni sono i record che il flow ha gi\u00E0\r\n letto, <strong>nell\u2019ordine in cui ci sono</strong>. Filtri e ordinamento non si applicano\r\n qui: per sceglierne una parte, filtra la collection con un elemento Filter prima dello\r\n screen.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('object')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n <fb-object-picker\r\n [value]=\"string('object') || undefined\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setField('object', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di enumerazione</label>\r\n <!-- Dizionario chiuso, e qui e' anche **autorevole**: un tipo fuori elenco e' un errore. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('enumType')\"\r\n (change)=\"setField('enumType', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumType()) {\r\n <p class=\"fb-field__error\">\r\n Il tipo \u00AB{{ string('enumType') }}\u00BB non e\u2019 fra quelli utilizzabili\r\n (ENUM_TYPE_UNKNOWN).\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Le opzioni sono i valori dichiarati dal tipo: nessuna choice da tenere allineata a mano.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (isChoiceSetSource('enum')) {\r\n <!--\r\n \u00A75.2 \u2014 qui i campi citabili sono le tre proprieta' di un valore di enum, non i campi\r\n di un'entita': l'elenco viene dal dizionario, e i default rendono superfluo dichiararli.\r\n -->\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo mostrato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('displayField')\"\r\n (change)=\"setField('displayField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultDisplayField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('displayField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('displayField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del valore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('valueField')\"\r\n (change)=\"setField('valueField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Predefinito ({{ defaultValueField }})</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('valueField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('valueField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n </div>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Dichiarare <code>Integer</code> come tipo della risorsa e\u2019 la stessa cosa che scegliere qui\r\n \u00ABValore numerico\u00BB: il valore dell\u2019opzione e\u2019 il numero sottostante.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo mostrato</label>\r\n <fb-field-picker\r\n [value]=\"string('displayField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo mostrato\"\r\n (valueChange)=\"setField('displayField', $event ?? '')\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo del valore</label>\r\n <fb-field-picker\r\n [value]=\"string('valueField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"any\"\r\n label=\"Campo del valore\"\r\n (valueChange)=\"setField('valueField', $event ?? '')\"\r\n />\r\n </div>\r\n }\r\n <!--\r\n \u00A75.2 \u2014 `filters`, `filterLogic`, `sortField` e `sortOrder` valgono sulle **sole** sorgenti\r\n `object` (dove diventano la query) ed `enumType` (valutati in memoria). Su una collection le\r\n opzioni sono i suoi elementi nell\u2019ordine in cui ci sono: mostrare i controlli l\u00EC\r\n inviterebbe a dichiarare cio\u2019 che il runtime ignora, ed e\u2019 `CHOICE_SET_FILTERS_IGNORED`.\r\n -->\r\n @if (!isChoiceSetSource('collection')) {\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n @if (isChoiceSetSource('enum')) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortField')\"\r\n (change)=\"setField('sortField', $any($event.target).value)\"\r\n >\r\n <option value=\"\">Ordine di dichiarazione</option>\r\n @for (field of enumChoiceSetFields(); track field.value) {\r\n <option [value]=\"field.value\">{{ field.label }}</option>\r\n }\r\n </select>\r\n @if (unknownEnumChoiceSetField('sortField')) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ string('sortField') }}\u00BB non e\u2019 una proprieta\u2019 di un valore di enum\r\n (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"string('sortField') || undefined\"\r\n [object]=\"string('object') || undefined\"\r\n usage=\"sortable\"\r\n label=\"Campo di ordinamento\"\r\n placeholder=\"Nessun ordinamento\"\r\n (valueChange)=\"setField('sortField', $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"string('sortOrder')\"\r\n (change)=\"setField('sortOrder', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- `limit` e' l'unico dei quattro che vale su tutt'e tre le sorgenti (\u00A75.2). -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"string('limit')\"\r\n (input)=\"setNumberField('limit', $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (!isChoiceSetSource('collection')) {\r\n <!--\r\n `supportsLogic` a true: dalla revisione del contratto `filterLogic` **esiste** anche qui, con\r\n le stesse forme e gli stessi rilievi degli elementi che interrogano il database (\u00A74.4).\r\n Nessun `emptyWarning`: senza filtri il set legge tutti i record dell\u2019oggetto, che qui e\u2019 un\r\n uso legittimo. Su un enum i filtri li valuta il runtime **in memoria**, e i campi sono le tre\r\n proprieta\u2019 di un valore: `fieldOptions` e\u2019 cio\u2019 che sostituisce il catalogo (\u00A75.2).\r\n -->\r\n <fb-record-filter-editor\r\n [holder]=\"$any(resource())\"\r\n [object]=\"isChoiceSetSource('enum') ? undefined : string('object') || undefined\"\r\n [fieldOptions]=\"isChoiceSetSource('enum') ? enumChoiceSetFields() : []\"\r\n [title]=\"\r\n isChoiceSetSource('enum') ? 'Quali valori diventano opzioni' : 'Quali record diventano opzioni'\r\n \"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n } @else if (ignoredChoiceSetFilters(); as ignored) {\r\n <!--\r\n Il documento dichiara cio' che su una collection il runtime ignora. E\u2019 un **avviso**, e la\r\n ripulitura e\u2019 un comando: cancellare da se\u2019 i filtri di un flow scritto altrove sarebbe una\r\n modifica che nessuno ha chiesto, e farebbe sparire l\u2019unico indizio di cosa quel flow\r\n voleva selezionare. E\u2019 la stessa scelta dei membri di un riquadro (\u00A73.6).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Su una collection {{ ignored }}: le opzioni sono gli elementi della collection nell\u2019ordine in\r\n cui ci sono (CHOICE_SET_FILTERS_IGNORED).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"clearIgnoredChoiceSetFilters()\">\r\n Togli\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isStructure() && collection() === 'variables') {\r\n <p class=\"fb-field__hint\">\r\n Non serve un valore iniziale: la variabile parte con un\u2019istanza vuota, e il flow ne assegna i\r\n membri uno alla volta con un Assignment.\r\n </p>\r\n}\r\n\r\n@if (\r\n supportsInitialValue() &&\r\n (collection() === 'variables' || collection() === 'constants' || collection() === 'choices')\r\n) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ collection() === 'variables' ? 'Valore iniziale' : 'Valore' }}\r\n </label>\r\n <fb-value-editor\r\n [value]=\"value()\"\r\n [dataType]=\"$any(resource()['dataType'])\"\r\n [objectType]=\"$any(resource()['objectType'])\"\r\n [isCollection]=\"boolean('isCollection')\"\r\n [allowFormula]=\"collection() !== 'constants'\"\r\n label=\"Valore\"\r\n (valueChange)=\"setValue($event)\"\r\n />\r\n </div>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"string('description')\"\r\n (input)=\"setField('description', $any($event.target).value)\"\r\n />\r\n</div>\r\n" }]
|
|
15999
16506
|
}], ctorParameters: () => [], propDecorators: { reference: [{ type: i0.Input, args: [{ isSignal: true, alias: "reference", required: true }] }] } });
|
|
16000
16507
|
|
|
16001
16508
|
/**
|
|
@@ -16247,6 +16754,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
16247
16754
|
* raggruppare e per decidere il comportamento; `message` e' testo per l'utente e non va
|
|
16248
16755
|
* interpretato. `elementName` e' l'ancora per portare l'utente sull'elemento con un clic.
|
|
16249
16756
|
*
|
|
16757
|
+
* Su un flow di produzione l'elenco e' lungo — quello della demo ne ha 64 righe — e a quella
|
|
16758
|
+
* misura serve **restringerlo**: la casella filtra su cio' che la riga mostra (messaggio, codice,
|
|
16759
|
+
* elemento, percorso) e la tendina dei codici e' una selezione **multipla**, perche' «fammi vedere
|
|
16760
|
+
* i tre codici che vengono dai cataloghi non popolati» non e' una gravita' e non e' un nome. I
|
|
16761
|
+
* codici che elenca sono quelli **presenti**, non quelli della §7: l'elenco del contratto e'
|
|
16762
|
+
* dichiarato aperto, e proporre codici che questo flow non ha significa proporre filtri che danno
|
|
16763
|
+
* zero righe.
|
|
16764
|
+
*
|
|
16250
16765
|
* Tre effetti che l'interfaccia deve rispettare:
|
|
16251
16766
|
* - `Error` blocca l'**attivazione**, non il salvataggio;
|
|
16252
16767
|
* - `Warning` non blocca nulla, ma dice che qualcosa non fara' cio' che sembra;
|
|
@@ -16275,31 +16790,152 @@ class ProblemsPanelComponent {
|
|
|
16275
16790
|
elementOpened = output();
|
|
16276
16791
|
closed = output();
|
|
16277
16792
|
filter = signal('all', ...(ngDevMode ? [{ debugName: "filter" }] : []));
|
|
16793
|
+
/** Il testo del filtro: cerca in cio' che la riga mostra. */
|
|
16794
|
+
query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : []));
|
|
16795
|
+
/**
|
|
16796
|
+
* I codici selezionati. Vuoto significa «tutti», non «nessuno»: e' l'unico modo di far
|
|
16797
|
+
* coincidere lo stato iniziale con l'assenza di filtro.
|
|
16798
|
+
*/
|
|
16799
|
+
selectedCodes = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedCodes" }] : []));
|
|
16800
|
+
isCodeMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "isCodeMenuOpen" }] : []));
|
|
16801
|
+
/** Il confine della tendina: il controllo, non il pannello. Vedi il commento nel costruttore. */
|
|
16802
|
+
codesRef = viewChild('codes', ...(ngDevMode ? [{ debugName: "codesRef" }] : []));
|
|
16803
|
+
constructor() {
|
|
16804
|
+
// La tendina si chiude con un gesto fuori o con `Esc`, come i picker: la regola sta in un
|
|
16805
|
+
// posto solo (`close-on-outside`). Il confine e' **il controllo** e non l'host, perche' qui
|
|
16806
|
+
// l'host e' tutto il pannello: un clic su una riga dei rilievi e' «altrove» per chi guarda,
|
|
16807
|
+
// e con l'host la tendina restava aperta sopra l'elenco che si stava leggendo.
|
|
16808
|
+
closeOnOutside(() => this.isCodeMenuOpen(), () => this.isCodeMenuOpen.set(false), () => this.codesRef()?.nativeElement);
|
|
16809
|
+
}
|
|
16278
16810
|
isRunning = computed(() => this.validation.isRunning(), ...(ngDevMode ? [{ debugName: "isRunning" }] : []));
|
|
16279
16811
|
hasResult = computed(() => this.validation.hasResult(), ...(ngDevMode ? [{ debugName: "hasResult" }] : []));
|
|
16280
16812
|
lastError = computed(() => this.validation.lastError(), ...(ngDevMode ? [{ debugName: "lastError" }] : []));
|
|
16813
|
+
/**
|
|
16814
|
+
* Il testo su cui la casella confronta, **una stringa per rilievo** e allineata per indice a
|
|
16815
|
+
* `validation.issues()`.
|
|
16816
|
+
*
|
|
16817
|
+
* Ci sta anche il percorso **tradotto per nome** (`pathLabel`), che e' quello che la riga
|
|
16818
|
+
* mostra: su uno screen dinamico il path arriva a indici, e cercare il nome del campo che si
|
|
16819
|
+
* legge sullo schermo non troverebbe niente. Si calcola una volta per risultato invece che a
|
|
16820
|
+
* ogni battuta, perche' tradurre un path significa camminare l'albero della schermata.
|
|
16821
|
+
*/
|
|
16822
|
+
haystacks = computed(() => this.validation
|
|
16823
|
+
.issues()
|
|
16824
|
+
.map((issue) => `${issue.message ?? ''} ${issue.code ?? ''} ${issue.elementName ?? ''} ${issue.path ?? ''} ${this.pathLabel(issue)}`.toLowerCase()), ...(ngDevMode ? [{ debugName: "haystacks" }] : []));
|
|
16825
|
+
needle = computed(() => this.query().trim().toLowerCase(), ...(ngDevMode ? [{ debugName: "needle" }] : []));
|
|
16826
|
+
/** Testo e codici: i due filtri che non sono la gravita'. */
|
|
16827
|
+
matchesText(index) {
|
|
16828
|
+
const needle = this.needle();
|
|
16829
|
+
return !needle || (this.haystacks()[index] ?? '').includes(needle);
|
|
16830
|
+
}
|
|
16831
|
+
matchesCode(issue) {
|
|
16832
|
+
const selected = this.selectedCodes();
|
|
16833
|
+
return selected.size === 0 || selected.has(issue.code ?? '');
|
|
16834
|
+
}
|
|
16281
16835
|
/**
|
|
16282
16836
|
* I conteggi dei chip vengono dai rilievi, non dal risultato: il numero sul filtro deve
|
|
16283
|
-
* essere quello che il filtro apre (vedi `FlowValidationStore.issueCounts`).
|
|
16837
|
+
* essere quello che il filtro apre (vedi `FlowValidationStore.issueCounts`). E' anche il
|
|
16838
|
+
* motivo per cui **non** si leggono piu' da `issueCounts` quando gli altri filtri sono attivi:
|
|
16839
|
+
* «Errori 47» accanto a una casella che ne lascia passare tre e' un numero che promette
|
|
16840
|
+
* quarantaquattro righe che non compariranno.
|
|
16284
16841
|
*/
|
|
16285
|
-
counts = computed(() =>
|
|
16842
|
+
counts = computed(() => {
|
|
16843
|
+
const counts = { Error: 0, Warning: 0, Info: 0 };
|
|
16844
|
+
this.validation.issues().forEach((issue, index) => {
|
|
16845
|
+
if (this.matchesText(index) && this.matchesCode(issue)) {
|
|
16846
|
+
counts[severityBucket(issue.severity)] += 1;
|
|
16847
|
+
}
|
|
16848
|
+
});
|
|
16849
|
+
return counts;
|
|
16850
|
+
}, ...(ngDevMode ? [{ debugName: "counts" }] : []));
|
|
16286
16851
|
errorCount = computed(() => this.counts().Error, ...(ngDevMode ? [{ debugName: "errorCount" }] : []));
|
|
16287
16852
|
warningCount = computed(() => this.counts().Warning, ...(ngDevMode ? [{ debugName: "warningCount" }] : []));
|
|
16288
16853
|
infoCount = computed(() => this.counts().Info, ...(ngDevMode ? [{ debugName: "infoCount" }] : []));
|
|
16289
|
-
|
|
16854
|
+
/** Quante righe aprirebbe «Tutti»: il totale **dopo** testo e codici. */
|
|
16855
|
+
totalCount = computed(() => this.counts().Error + this.counts().Warning + this.counts().Info, ...(ngDevMode ? [{ debugName: "totalCount" }] : []));
|
|
16856
|
+
/**
|
|
16857
|
+
* Il totale assoluto, che non guarda nessun filtro. Serve a due cose diverse dai chip: dire
|
|
16858
|
+
* che il flow **non** e' senza rilievi quando i filtri li nascondono tutti, e tenere in fondo
|
|
16859
|
+
* la nota sugli errori che bloccano l'attivazione — filtrando per «Note» quella nota spariva
|
|
16860
|
+
* pur essendoci quarantasette errori, cioe' proprio quando serve.
|
|
16861
|
+
*/
|
|
16862
|
+
absoluteCount = computed(() => this.validation.issues().length, ...(ngDevMode ? [{ debugName: "absoluteCount" }] : []));
|
|
16863
|
+
hasErrors = computed(() => this.validation.issueCounts().Error > 0, ...(ngDevMode ? [{ debugName: "hasErrors" }] : []));
|
|
16290
16864
|
issues = computed(() => {
|
|
16291
16865
|
const filter = this.filter();
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
|
|
16866
|
+
return this.validation.issues().filter((issue, index) => {
|
|
16867
|
+
if (!this.matchesText(index) || !this.matchesCode(issue)) {
|
|
16868
|
+
return false;
|
|
16869
|
+
}
|
|
16870
|
+
// Il secchio, non la stringa: `severity` e' un tipo aperto e un `error` minuscolo o un
|
|
16871
|
+
// `Log` non corrisponderebbero a niente, svuotando la lista senza dire perche'.
|
|
16872
|
+
return filter === 'all' || severityBucket(issue.severity) === filter;
|
|
16873
|
+
});
|
|
16299
16874
|
}, ...(ngDevMode ? [{ debugName: "issues" }] : []));
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16875
|
+
/**
|
|
16876
|
+
* I codici della tendina: quelli **presenti** dopo gravita' e testo, col loro conteggio.
|
|
16877
|
+
*
|
|
16878
|
+
* Piu' quelli **selezionati** anche se non compaiono piu', a zero: una selezione che sparisce
|
|
16879
|
+
* dall'elenco quando si cambia gravita' e' una selezione che continua a filtrare senza che si
|
|
16880
|
+
* possa vedere ne' togliere. Ordinati per frequenza e poi per nome, perche' su sessanta righe
|
|
16881
|
+
* il codice che conta e' quello che si ripete.
|
|
16882
|
+
*/
|
|
16883
|
+
codeFacets = computed(() => {
|
|
16884
|
+
const filter = this.filter();
|
|
16885
|
+
const selected = this.selectedCodes();
|
|
16886
|
+
const counts = new Map();
|
|
16887
|
+
this.validation.issues().forEach((issue, index) => {
|
|
16888
|
+
if (!this.matchesText(index)) {
|
|
16889
|
+
return;
|
|
16890
|
+
}
|
|
16891
|
+
if (filter !== 'all' && severityBucket(issue.severity) !== filter) {
|
|
16892
|
+
return;
|
|
16893
|
+
}
|
|
16894
|
+
const code = issue.code ?? '';
|
|
16895
|
+
counts.set(code, (counts.get(code) ?? 0) + 1);
|
|
16896
|
+
});
|
|
16897
|
+
for (const code of selected) {
|
|
16898
|
+
if (!counts.has(code)) {
|
|
16899
|
+
counts.set(code, 0);
|
|
16900
|
+
}
|
|
16901
|
+
}
|
|
16902
|
+
return [...counts.entries()]
|
|
16903
|
+
.map(([code, count]) => ({ code, count, selected: selected.has(code) }))
|
|
16904
|
+
.sort((a, b) => b.count - a.count || a.code.localeCompare(b.code));
|
|
16905
|
+
}, ...(ngDevMode ? [{ debugName: "codeFacets" }] : []));
|
|
16906
|
+
selectedCodeCount = computed(() => this.selectedCodes().size, ...(ngDevMode ? [{ debugName: "selectedCodeCount" }] : []));
|
|
16907
|
+
/** `true` se c'e' un filtro **oltre** la gravita': decide il messaggio dell'elenco vuoto. */
|
|
16908
|
+
hasTextOrCodeFilter = computed(() => this.needle() !== '' || this.selectedCodes().size > 0, ...(ngDevMode ? [{ debugName: "hasTextOrCodeFilter" }] : []));
|
|
16909
|
+
hasAnyFilter = computed(() => this.hasTextOrCodeFilter() || this.filter() !== 'all', ...(ngDevMode ? [{ debugName: "hasAnyFilter" }] : []));
|
|
16910
|
+
onQuery(value) {
|
|
16911
|
+
this.query.set(value);
|
|
16912
|
+
}
|
|
16913
|
+
toggleCodeMenu() {
|
|
16914
|
+
this.isCodeMenuOpen.update((open) => !open);
|
|
16915
|
+
}
|
|
16916
|
+
/** La selezione e' un insieme nuovo a ogni gesto: un `Set` mutato non fa scattare il signal. */
|
|
16917
|
+
toggleCode(code) {
|
|
16918
|
+
this.selectedCodes.update((selected) => {
|
|
16919
|
+
const next = new Set(selected);
|
|
16920
|
+
if (!next.delete(code)) {
|
|
16921
|
+
next.add(code);
|
|
16922
|
+
}
|
|
16923
|
+
return next;
|
|
16924
|
+
});
|
|
16925
|
+
}
|
|
16926
|
+
clearCodes() {
|
|
16927
|
+
this.selectedCodes.set(new Set());
|
|
16928
|
+
}
|
|
16929
|
+
resetFilters() {
|
|
16930
|
+
this.filter.set('all');
|
|
16931
|
+
this.query.set('');
|
|
16932
|
+
this.clearCodes();
|
|
16933
|
+
this.isCodeMenuOpen.set(false);
|
|
16934
|
+
}
|
|
16935
|
+
/** Nessun rilievo **nel flow**: e' un'altra cosa dal «nessuno che passi i filtri». */
|
|
16936
|
+
isClean = computed(() => this.hasResult() && this.absoluteCount() === 0, ...(ngDevMode ? [{ debugName: "isClean" }] : []));
|
|
16937
|
+
/** Ci sono rilievi, ma i filtri attivi non ne mostrano nessuno: va detto, non lasciato in bianco. */
|
|
16938
|
+
isFilteredEmpty = computed(() => this.absoluteCount() > 0 && this.issues().length === 0, ...(ngDevMode ? [{ debugName: "isFilteredEmpty" }] : []));
|
|
16303
16939
|
activeFilterLabel = computed(() => {
|
|
16304
16940
|
const filter = this.filter();
|
|
16305
16941
|
return filter === 'all' ? '' : SEVERITY_LABEL[filter];
|
|
@@ -16352,12 +16988,12 @@ class ProblemsPanelComponent {
|
|
|
16352
16988
|
this.closed.emit();
|
|
16353
16989
|
}
|
|
16354
16990
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ProblemsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
16355
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ProblemsPanelComponent, isStandalone: true, selector: "fb-problems-panel", outputs: { elementFocused: "elementFocused", elementOpened: "elementOpened", closed: "closed" }, ngImport: i0, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti <span class=\"fb-prob__count\">{{ totalCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n [attr.aria-pressed]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--error\" aria-hidden=\"true\">\u2715</span>\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n [attr.aria-pressed]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--warning\" aria-hidden=\"true\">!</span>\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--info\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n [attr.aria-pressed]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--info\" aria-hidden=\"true\">i</span>\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n @if (isFilteredEmpty()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo di tipo \u00AB{{ activeFilterLabel() }}\u00BB. Ce ne sono altri: scegli\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"setFilter('all')\">Tutti</button>.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <!-- Il secchio si calcola una volta: `severity` e' una stringa aperta e serve\r\n normalizzata a classe, icona ed etichetta. -->\r\n @let bucket = bucketOf(issue);\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__item--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__item--info]=\"bucket === 'Info'\"\r\n >\r\n <!--\r\n Un clic **mostra** (seleziona e centra), il doppio clic apre: e\u2019 la stessa coppia di\r\n gesti del canvas, dove un clic seleziona il node e il doppio ne apre il dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__link\"\r\n (click)=\"focus(issue)\"\r\n (dblclick)=\"open(issue, $event)\"\r\n >\r\n <!-- Il glifo porta il colore: `role=\"img\"` + `aria-label` perche' la gravita\u2019 non\r\n sia leggibile solo a vista. -->\r\n <span\r\n class=\"fb-prob__icon\"\r\n [class.fb-prob__icon--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__icon--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__icon--info]=\"bucket === 'Info'\"\r\n role=\"img\"\r\n [attr.aria-label]=\"labelText(bucket)\"\r\n [title]=\"labelText(bucket)\"\r\n >{{ iconText(bucket) }}</span\r\n >\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n <!-- Il path per nome, non a indici: vedi `pathLabel`. -->\r\n \u00B7 {{ pathLabel(issue) }}\r\n }\r\n </span>\r\n </button>\r\n <!--\r\n Il comando per aprire il form. Sta accanto alla riga e non **e\u2019** la riga: scorrere\r\n l\u2019elenco dei problemi e aprire il form di ognuno sono due intenzioni diverse, e finche\u2019\r\n erano lo stesso clic la seconda accadeva per forza.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__open\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + (issue.elementName || 'questo rilievo')\"\r\n title=\"Apri il dettaglio (anche con un doppio clic sulla riga)\"\r\n (click)=\"open(issue, $event)\"\r\n >\r\n \u270E\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (errorCount() > 0) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__icon{display:inline-grid;place-items:center;flex:0 0 auto;width:13px;height:13px;border-radius:50%;background:var(--fb-text-subtle, #98a2b3);color:var(--fb-surface, #fff);font-size:9px;font-weight:700;line-height:1}.fb-prob__icon--error{background:var(--fb-error, #c9372c)}.fb-prob__icon--warning{background:var(--fb-warning, #b7791f)}.fb-prob__icon--info{background:var(--fb-accent, #2f6feb)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{display:flex;align-items:stretch;border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__item--info{border-left-color:var(--fb-accent, #2f6feb)}.fb-prob__link{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:7px;align-items:start;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__link>.fb-prob__icon{grid-column:1;grid-row:1 / span 2;margin-top:2px}.fb-prob__message{grid-column:2;grid-row:1;font-size:12px;line-height:1.4}.fb-prob__meta{grid-column:2;grid-row:2;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__inline-link{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;text-decoration:underline;cursor:pointer}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__open{flex:none;align-self:flex-start;width:22px;height:22px;margin:2px 4px 0 0;padding:0;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;line-height:1;cursor:pointer}.fb-prob__open:hover,.fb-prob__open:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
16991
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: ProblemsPanelComponent, isStandalone: true, selector: "fb-problems-panel", outputs: { elementFocused: "elementFocused", elementOpened: "elementOpened", closed: "closed" }, viewQueries: [{ propertyName: "codesRef", first: true, predicate: ["codes"], descendants: true, isSignal: true }], ngImport: i0, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti <span class=\"fb-prob__count\">{{ totalCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n [attr.aria-pressed]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--error\" aria-hidden=\"true\">\u2715</span>\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n [attr.aria-pressed]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--warning\" aria-hidden=\"true\">!</span>\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--info\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n [attr.aria-pressed]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--info\" aria-hidden=\"true\">i</span>\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<!--\r\n La seconda barra sta **fuori** dal corpo che scorre, come i chip della gravita\u2019: su sessanta\r\n righe scorrere per raggiungere il controllo che le accorcia e\u2019 il gesto che il filtro esiste\r\n per togliere.\r\n-->\r\n<div class=\"fb-prob__tools\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-prob__search\"\r\n placeholder=\"Filtra i rilievi\u2026\"\r\n aria-label=\"Filtra i rilievi per messaggio, codice, elemento o percorso\"\r\n title=\"Cerca nel messaggio, nel codice, nel nome dell\u2019elemento e nel percorso \u2014 cioe\u2019 in cio\u2019 che la riga mostra\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <!--\r\n La tendina dei codici. E\u2019 **multipla** perche\u2019 la domanda vera e\u2019 \u00ABfammi vedere questi tre\r\n codici\u00BB, e i codici che elenca sono quelli **presenti** nel risultato con il loro conteggio:\r\n l\u2019elenco della \u00A77 e\u2019 aperto e proporre codici che questo flow non ha vorrebbe dire proporre\r\n filtri che danno zero righe.\r\n -->\r\n <div class=\"fb-prob__codes\" #codes>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__codes-toggle\"\r\n [class.fb-prob__codes-toggle--active]=\"selectedCodeCount() > 0\"\r\n [attr.aria-expanded]=\"isCodeMenuOpen()\"\r\n aria-haspopup=\"true\"\r\n [title]=\"\r\n selectedCodeCount()\r\n ? 'Filtra per codice: ' + selectedCodeCount() + ' selezionati'\r\n : 'Filtra per uno o piu\u2019 codici presenti nel risultato'\r\n \"\r\n (click)=\"toggleCodeMenu()\"\r\n >\r\n @if (selectedCodeCount()) {\r\n {{ selectedCodeCount() }} codici\r\n } @else {\r\n Codici\r\n }\r\n <span class=\"fb-prob__caret\" aria-hidden=\"true\">\u25BE</span>\r\n </button>\r\n\r\n @if (isCodeMenuOpen()) {\r\n <div class=\"fb-prob__menu\" role=\"group\" aria-label=\"Codici dei rilievi\">\r\n @if (selectedCodeCount()) {\r\n <button type=\"button\" class=\"fb-prob__menu-clear\" (click)=\"clearCodes()\">\r\n Deseleziona tutti\r\n </button>\r\n }\r\n @for (facet of codeFacets(); track facet.code) {\r\n <!--\r\n Un codice **selezionato** resta nell\u2019elenco anche quando la gravita\u2019 scelta non ne\r\n contiene piu\u2019 nessuno: si vede a zero. Senza, continuerebbe a filtrare senza che si\r\n possa vederlo ne\u2019 togliere.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__menu-item\"\r\n role=\"menuitemcheckbox\"\r\n [attr.aria-checked]=\"facet.selected\"\r\n [class.fb-prob__menu-item--selected]=\"facet.selected\"\r\n (click)=\"toggleCode(facet.code)\"\r\n >\r\n <span class=\"fb-prob__check\" aria-hidden=\"true\">{{ facet.selected ? '\u2611' : '\u2610' }}</span>\r\n <code class=\"fb-prob__menu-code\">{{ facet.code || '(senza codice)' }}</code>\r\n <span class=\"fb-prob__menu-count\">{{ facet.count }}</span>\r\n </button>\r\n } @empty {\r\n <p class=\"fb-prob__menu-empty\">Nessun codice da filtrare.</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (hasAnyFilter()) {\r\n <button type=\"button\" class=\"fb-prob__reset\" (click)=\"resetFilters()\">Azzera i filtri</button>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n @if (isFilteredEmpty()) {\r\n <!--\r\n Due messaggi perche\u2019 i rimedi sono due: con la sola gravita\u2019 attiva basta tornare a\r\n \u00ABTutti\u00BB, con testo o codici attivi il rimedio e\u2019 azzerarli \u2014 e dirlo con la frase della\r\n gravita\u2019 manderebbe a cercare il filtro sbagliato.\r\n -->\r\n @if (hasTextOrCodeFilter()) {\r\n <p class=\"fb-empty\">\r\n Nessuno dei {{ absoluteCount() }} rilievi corrisponde ai filtri attivi.\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"resetFilters()\">Azzerali</button>.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo di tipo \u00AB{{ activeFilterLabel() }}\u00BB. Ce ne sono altri: scegli\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"setFilter('all')\">Tutti</button>.\r\n </p>\r\n }\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <!-- Il secchio si calcola una volta: `severity` e' una stringa aperta e serve\r\n normalizzata a classe, icona ed etichetta. -->\r\n @let bucket = bucketOf(issue);\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__item--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__item--info]=\"bucket === 'Info'\"\r\n >\r\n <!--\r\n Un clic **mostra** (seleziona e centra), il doppio clic apre: e\u2019 la stessa coppia di\r\n gesti del canvas, dove un clic seleziona il node e il doppio ne apre il dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__link\"\r\n (click)=\"focus(issue)\"\r\n (dblclick)=\"open(issue, $event)\"\r\n >\r\n <!-- Il glifo porta il colore: `role=\"img\"` + `aria-label` perche' la gravita\u2019 non\r\n sia leggibile solo a vista. -->\r\n <span\r\n class=\"fb-prob__icon\"\r\n [class.fb-prob__icon--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__icon--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__icon--info]=\"bucket === 'Info'\"\r\n role=\"img\"\r\n [attr.aria-label]=\"labelText(bucket)\"\r\n [title]=\"labelText(bucket)\"\r\n >{{ iconText(bucket) }}</span\r\n >\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n <!-- Il path per nome, non a indici: vedi `pathLabel`. -->\r\n \u00B7 {{ pathLabel(issue) }}\r\n }\r\n </span>\r\n </button>\r\n <!--\r\n Il comando per aprire il form. Sta accanto alla riga e non **e\u2019** la riga: scorrere\r\n l\u2019elenco dei problemi e aprire il form di ognuno sono due intenzioni diverse, e finche\u2019\r\n erano lo stesso clic la seconda accadeva per forza.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__open\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + (issue.elementName || 'questo rilievo')\"\r\n title=\"Apri il dettaglio (anche con un doppio clic sulla riga)\"\r\n (click)=\"open(issue, $event)\"\r\n >\r\n \u270E\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n <!--\r\n `hasErrors` e non `errorCount`: quello segue i filtri, e filtrando per \u00ABNote\u00BB la nota\r\n spariva pur essendoci quarantasette errori \u2014 cioe' proprio quando serve.\r\n -->\r\n @if (hasErrors()) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__icon{display:inline-grid;place-items:center;flex:0 0 auto;width:13px;height:13px;border-radius:50%;background:var(--fb-text-subtle, #98a2b3);color:var(--fb-surface, #fff);font-size:9px;font-weight:700;line-height:1}.fb-prob__icon--error{background:var(--fb-error, #c9372c)}.fb-prob__icon--warning{background:var(--fb-warning, #b7791f)}.fb-prob__icon--info{background:var(--fb-accent, #2f6feb)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{display:flex;align-items:stretch;border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__item--info{border-left-color:var(--fb-accent, #2f6feb)}.fb-prob__link{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:7px;align-items:start;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__link>.fb-prob__icon{grid-column:1;grid-row:1 / span 2;margin-top:2px}.fb-prob__message{grid-column:2;grid-row:1;font-size:12px;line-height:1.4}.fb-prob__meta{grid-column:2;grid-row:2;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__inline-link{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;text-decoration:underline;cursor:pointer}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__open{flex:none;align-self:flex-start;width:22px;height:22px;margin:2px 4px 0 0;padding:0;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;line-height:1;cursor:pointer}.fb-prob__open:hover,.fb-prob__open:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939)}.fb-prob__tools{display:flex;align-items:center;gap:6px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__search{box-sizing:border-box;flex:1 1 260px;max-width:420px;padding:4px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-prob__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-prob__codes{position:relative;flex:none}.fb-prob__codes-toggle{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__codes-toggle:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__codes-toggle--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__caret{font-size:9px}.fb-prob__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:30;min-width:260px;max-height:280px;overflow-y:auto;padding:4px;border:1px solid var(--fb-border, #d6dae1);border-radius:8px;background:var(--fb-surface, #fff);box-shadow:0 8px 24px #10182824}.fb-prob__menu-clear{width:100%;padding:4px 6px;border:0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);margin-bottom:4px;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:11px;text-align:left;cursor:pointer}.fb-prob__menu-item{display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border:0;border-radius:6px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:11px;text-align:left;cursor:pointer}.fb-prob__menu-item:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__menu-item--selected{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 8%,transparent)}.fb-prob__check{flex:none;font-size:12px;color:var(--fb-accent, #2f6feb)}.fb-prob__menu-code{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}.fb-prob__menu-count{flex:none;padding:0 5px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font-size:10px;font-variant-numeric:tabular-nums}.fb-prob__menu-empty{margin:6px;color:var(--fb-text-muted, #667085);font-size:11px}.fb-prob__reset{flex:none;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__reset:hover{background:var(--fb-border, #d6dae1)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
16356
16992
|
}
|
|
16357
16993
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: ProblemsPanelComponent, decorators: [{
|
|
16358
16994
|
type: Component,
|
|
16359
|
-
args: [{ selector: 'fb-problems-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti <span class=\"fb-prob__count\">{{ totalCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n [attr.aria-pressed]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--error\" aria-hidden=\"true\">\u2715</span>\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n [attr.aria-pressed]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--warning\" aria-hidden=\"true\">!</span>\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--info\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n [attr.aria-pressed]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--info\" aria-hidden=\"true\">i</span>\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n @if (isFilteredEmpty()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo di tipo \u00AB{{ activeFilterLabel() }}\u00BB. Ce ne sono altri: scegli\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"setFilter('all')\">Tutti</button>.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <!-- Il secchio si calcola una volta: `severity` e' una stringa aperta e serve\r\n normalizzata a classe, icona ed etichetta. -->\r\n @let bucket = bucketOf(issue);\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__item--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__item--info]=\"bucket === 'Info'\"\r\n >\r\n <!--\r\n Un clic **mostra** (seleziona e centra), il doppio clic apre: e\u2019 la stessa coppia di\r\n gesti del canvas, dove un clic seleziona il node e il doppio ne apre il dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__link\"\r\n (click)=\"focus(issue)\"\r\n (dblclick)=\"open(issue, $event)\"\r\n >\r\n <!-- Il glifo porta il colore: `role=\"img\"` + `aria-label` perche' la gravita\u2019 non\r\n sia leggibile solo a vista. -->\r\n <span\r\n class=\"fb-prob__icon\"\r\n [class.fb-prob__icon--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__icon--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__icon--info]=\"bucket === 'Info'\"\r\n role=\"img\"\r\n [attr.aria-label]=\"labelText(bucket)\"\r\n [title]=\"labelText(bucket)\"\r\n >{{ iconText(bucket) }}</span\r\n >\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n <!-- Il path per nome, non a indici: vedi `pathLabel`. -->\r\n \u00B7 {{ pathLabel(issue) }}\r\n }\r\n </span>\r\n </button>\r\n <!--\r\n Il comando per aprire il form. Sta accanto alla riga e non **e\u2019** la riga: scorrere\r\n l\u2019elenco dei problemi e aprire il form di ognuno sono due intenzioni diverse, e finche\u2019\r\n erano lo stesso clic la seconda accadeva per forza.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__open\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + (issue.elementName || 'questo rilievo')\"\r\n title=\"Apri il dettaglio (anche con un doppio clic sulla riga)\"\r\n (click)=\"open(issue, $event)\"\r\n >\r\n \u270E\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (errorCount() > 0) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__icon{display:inline-grid;place-items:center;flex:0 0 auto;width:13px;height:13px;border-radius:50%;background:var(--fb-text-subtle, #98a2b3);color:var(--fb-surface, #fff);font-size:9px;font-weight:700;line-height:1}.fb-prob__icon--error{background:var(--fb-error, #c9372c)}.fb-prob__icon--warning{background:var(--fb-warning, #b7791f)}.fb-prob__icon--info{background:var(--fb-accent, #2f6feb)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{display:flex;align-items:stretch;border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__item--info{border-left-color:var(--fb-accent, #2f6feb)}.fb-prob__link{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:7px;align-items:start;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__link>.fb-prob__icon{grid-column:1;grid-row:1 / span 2;margin-top:2px}.fb-prob__message{grid-column:2;grid-row:1;font-size:12px;line-height:1.4}.fb-prob__meta{grid-column:2;grid-row:2;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__inline-link{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;text-decoration:underline;cursor:pointer}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__open{flex:none;align-self:flex-start;width:22px;height:22px;margin:2px 4px 0 0;padding:0;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;line-height:1;cursor:pointer}.fb-prob__open:hover,.fb-prob__open:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939)}\n"] }]
|
|
16360
|
-
}], propDecorators: { elementFocused: [{ type: i0.Output, args: ["elementFocused"] }], elementOpened: [{ type: i0.Output, args: ["elementOpened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
16995
|
+
args: [{ selector: 'fb-problems-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti <span class=\"fb-prob__count\">{{ totalCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n [attr.aria-pressed]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--error\" aria-hidden=\"true\">\u2715</span>\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n [attr.aria-pressed]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--warning\" aria-hidden=\"true\">!</span>\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--info\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n [attr.aria-pressed]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n <span class=\"fb-prob__icon fb-prob__icon--info\" aria-hidden=\"true\">i</span>\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<!--\r\n La seconda barra sta **fuori** dal corpo che scorre, come i chip della gravita\u2019: su sessanta\r\n righe scorrere per raggiungere il controllo che le accorcia e\u2019 il gesto che il filtro esiste\r\n per togliere.\r\n-->\r\n<div class=\"fb-prob__tools\">\r\n <input\r\n type=\"search\"\r\n class=\"fb-prob__search\"\r\n placeholder=\"Filtra i rilievi\u2026\"\r\n aria-label=\"Filtra i rilievi per messaggio, codice, elemento o percorso\"\r\n title=\"Cerca nel messaggio, nel codice, nel nome dell\u2019elemento e nel percorso \u2014 cioe\u2019 in cio\u2019 che la riga mostra\"\r\n [value]=\"query()\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n\r\n <!--\r\n La tendina dei codici. E\u2019 **multipla** perche\u2019 la domanda vera e\u2019 \u00ABfammi vedere questi tre\r\n codici\u00BB, e i codici che elenca sono quelli **presenti** nel risultato con il loro conteggio:\r\n l\u2019elenco della \u00A77 e\u2019 aperto e proporre codici che questo flow non ha vorrebbe dire proporre\r\n filtri che danno zero righe.\r\n -->\r\n <div class=\"fb-prob__codes\" #codes>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__codes-toggle\"\r\n [class.fb-prob__codes-toggle--active]=\"selectedCodeCount() > 0\"\r\n [attr.aria-expanded]=\"isCodeMenuOpen()\"\r\n aria-haspopup=\"true\"\r\n [title]=\"\r\n selectedCodeCount()\r\n ? 'Filtra per codice: ' + selectedCodeCount() + ' selezionati'\r\n : 'Filtra per uno o piu\u2019 codici presenti nel risultato'\r\n \"\r\n (click)=\"toggleCodeMenu()\"\r\n >\r\n @if (selectedCodeCount()) {\r\n {{ selectedCodeCount() }} codici\r\n } @else {\r\n Codici\r\n }\r\n <span class=\"fb-prob__caret\" aria-hidden=\"true\">\u25BE</span>\r\n </button>\r\n\r\n @if (isCodeMenuOpen()) {\r\n <div class=\"fb-prob__menu\" role=\"group\" aria-label=\"Codici dei rilievi\">\r\n @if (selectedCodeCount()) {\r\n <button type=\"button\" class=\"fb-prob__menu-clear\" (click)=\"clearCodes()\">\r\n Deseleziona tutti\r\n </button>\r\n }\r\n @for (facet of codeFacets(); track facet.code) {\r\n <!--\r\n Un codice **selezionato** resta nell\u2019elenco anche quando la gravita\u2019 scelta non ne\r\n contiene piu\u2019 nessuno: si vede a zero. Senza, continuerebbe a filtrare senza che si\r\n possa vederlo ne\u2019 togliere.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__menu-item\"\r\n role=\"menuitemcheckbox\"\r\n [attr.aria-checked]=\"facet.selected\"\r\n [class.fb-prob__menu-item--selected]=\"facet.selected\"\r\n (click)=\"toggleCode(facet.code)\"\r\n >\r\n <span class=\"fb-prob__check\" aria-hidden=\"true\">{{ facet.selected ? '\u2611' : '\u2610' }}</span>\r\n <code class=\"fb-prob__menu-code\">{{ facet.code || '(senza codice)' }}</code>\r\n <span class=\"fb-prob__menu-count\">{{ facet.count }}</span>\r\n </button>\r\n } @empty {\r\n <p class=\"fb-prob__menu-empty\">Nessun codice da filtrare.</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (hasAnyFilter()) {\r\n <button type=\"button\" class=\"fb-prob__reset\" (click)=\"resetFilters()\">Azzera i filtri</button>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n @if (isFilteredEmpty()) {\r\n <!--\r\n Due messaggi perche\u2019 i rimedi sono due: con la sola gravita\u2019 attiva basta tornare a\r\n \u00ABTutti\u00BB, con testo o codici attivi il rimedio e\u2019 azzerarli \u2014 e dirlo con la frase della\r\n gravita\u2019 manderebbe a cercare il filtro sbagliato.\r\n -->\r\n @if (hasTextOrCodeFilter()) {\r\n <p class=\"fb-empty\">\r\n Nessuno dei {{ absoluteCount() }} rilievi corrisponde ai filtri attivi.\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"resetFilters()\">Azzerali</button>.\r\n </p>\r\n } @else {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo di tipo \u00AB{{ activeFilterLabel() }}\u00BB. Ce ne sono altri: scegli\r\n <button type=\"button\" class=\"fb-prob__inline-link\" (click)=\"setFilter('all')\">Tutti</button>.\r\n </p>\r\n }\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <!-- Il secchio si calcola una volta: `severity` e' una stringa aperta e serve\r\n normalizzata a classe, icona ed etichetta. -->\r\n @let bucket = bucketOf(issue);\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__item--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__item--info]=\"bucket === 'Info'\"\r\n >\r\n <!--\r\n Un clic **mostra** (seleziona e centra), il doppio clic apre: e\u2019 la stessa coppia di\r\n gesti del canvas, dove un clic seleziona il node e il doppio ne apre il dettaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__link\"\r\n (click)=\"focus(issue)\"\r\n (dblclick)=\"open(issue, $event)\"\r\n >\r\n <!-- Il glifo porta il colore: `role=\"img\"` + `aria-label` perche' la gravita\u2019 non\r\n sia leggibile solo a vista. -->\r\n <span\r\n class=\"fb-prob__icon\"\r\n [class.fb-prob__icon--error]=\"bucket === 'Error'\"\r\n [class.fb-prob__icon--warning]=\"bucket === 'Warning'\"\r\n [class.fb-prob__icon--info]=\"bucket === 'Info'\"\r\n role=\"img\"\r\n [attr.aria-label]=\"labelText(bucket)\"\r\n [title]=\"labelText(bucket)\"\r\n >{{ iconText(bucket) }}</span\r\n >\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n <!-- Il path per nome, non a indici: vedi `pathLabel`. -->\r\n \u00B7 {{ pathLabel(issue) }}\r\n }\r\n </span>\r\n </button>\r\n <!--\r\n Il comando per aprire il form. Sta accanto alla riga e non **e\u2019** la riga: scorrere\r\n l\u2019elenco dei problemi e aprire il form di ognuno sono due intenzioni diverse, e finche\u2019\r\n erano lo stesso clic la seconda accadeva per forza.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__open\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + (issue.elementName || 'questo rilievo')\"\r\n title=\"Apri il dettaglio (anche con un doppio clic sulla riga)\"\r\n (click)=\"open(issue, $event)\"\r\n >\r\n \u270E\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n <!--\r\n `hasErrors` e non `errorCount`: quello segue i filtri, e filtrando per \u00ABNote\u00BB la nota\r\n spariva pur essendoci quarantasette errori \u2014 cioe' proprio quando serve.\r\n -->\r\n @if (hasErrors()) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__icon{display:inline-grid;place-items:center;flex:0 0 auto;width:13px;height:13px;border-radius:50%;background:var(--fb-text-subtle, #98a2b3);color:var(--fb-surface, #fff);font-size:9px;font-weight:700;line-height:1}.fb-prob__icon--error{background:var(--fb-error, #c9372c)}.fb-prob__icon--warning{background:var(--fb-warning, #b7791f)}.fb-prob__icon--info{background:var(--fb-accent, #2f6feb)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{display:flex;align-items:stretch;border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__item--info{border-left-color:var(--fb-accent, #2f6feb)}.fb-prob__link{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:7px;align-items:start;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__link>.fb-prob__icon{grid-column:1;grid-row:1 / span 2;margin-top:2px}.fb-prob__message{grid-column:2;grid-row:1;font-size:12px;line-height:1.4}.fb-prob__meta{grid-column:2;grid-row:2;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__inline-link{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;text-decoration:underline;cursor:pointer}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__open{flex:none;align-self:flex-start;width:22px;height:22px;margin:2px 4px 0 0;padding:0;border:1px solid transparent;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;line-height:1;cursor:pointer}.fb-prob__open:hover,.fb-prob__open:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939)}.fb-prob__tools{display:flex;align-items:center;gap:6px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__search{box-sizing:border-box;flex:1 1 260px;max-width:420px;padding:4px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-prob__search:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-prob__codes{position:relative;flex:none}.fb-prob__codes-toggle{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__codes-toggle:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__codes-toggle--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__caret{font-size:9px}.fb-prob__menu{position:absolute;top:calc(100% + 4px);left:0;z-index:30;min-width:260px;max-height:280px;overflow-y:auto;padding:4px;border:1px solid var(--fb-border, #d6dae1);border-radius:8px;background:var(--fb-surface, #fff);box-shadow:0 8px 24px #10182824}.fb-prob__menu-clear{width:100%;padding:4px 6px;border:0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);margin-bottom:4px;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:11px;text-align:left;cursor:pointer}.fb-prob__menu-item{display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border:0;border-radius:6px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:11px;text-align:left;cursor:pointer}.fb-prob__menu-item:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__menu-item--selected{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 8%,transparent)}.fb-prob__check{flex:none;font-size:12px;color:var(--fb-accent, #2f6feb)}.fb-prob__menu-code{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}.fb-prob__menu-count{flex:none;padding:0 5px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font-size:10px;font-variant-numeric:tabular-nums}.fb-prob__menu-empty{margin:6px;color:var(--fb-text-muted, #667085);font-size:11px}.fb-prob__reset{flex:none;padding:3px 8px;border:0;border-radius:6px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__reset:hover{background:var(--fb-border, #d6dae1)}\n"] }]
|
|
16996
|
+
}], ctorParameters: () => [], propDecorators: { elementFocused: [{ type: i0.Output, args: ["elementFocused"] }], elementOpened: [{ type: i0.Output, args: ["elementOpened"] }], closed: [{ type: i0.Output, args: ["closed"] }], codesRef: [{ type: i0.ViewChild, args: ['codes', { isSignal: true }] }] } });
|
|
16361
16997
|
|
|
16362
16998
|
/**
|
|
16363
16999
|
* Versioni e ciclo di vita — FRONTEND.md §8, §9.
|
|
@@ -18478,5 +19114,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
18478
19114
|
* Generated bundle index. Do not edit.
|
|
18479
19115
|
*/
|
|
18480
19116
|
|
|
18481
|
-
export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ENUM_CHOICE_SET_DEFAULT_DISPLAY_FIELD, ENUM_CHOICE_SET_DEFAULT_VALUE_FIELD, ENUM_CHOICE_SET_FIELDS, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_FOLD_HEIGHT, FLOW_FOLD_WIDTH, FLOW_GROUP_COLORS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FieldValuePickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, GROUP_HEADER_HEIGHT, GROUP_MIN_HEIGHT, GROUP_MIN_WIDTH, GROUP_PADDING, GroupPanelComponent, HttpFlowBuilderApi, MergeTextEditorComponent, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourceDialogComponent, ResourceFormComponent, ResourcePanelComponent, RunDialogComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, boundsAround, buildClipboardPayload, canvasFoldId, canvasGroupId, canvasNodeId, checkConditionLogic, checkFlowName, choiceSetSourceOf, declaredChoiceSetSources, describeFieldPath, describePathEntry, duplicateField, elementIcon, emptyFlowDefinition, enumChoiceSetFieldType, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, foldSourceConnectorId, foldTargetConnectorId, groupColorClass, groupRect, hasAutoSize, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isFoldReference, isGlobalReference, isNumericType, isPathInside, isTypeCheckedOperator, isUnknownEnumChoiceSetField, isValidFlowName, isValued, loadPathLevel, matchesReferenceFilter, membersInside, moveCondition, moveField, navigatePath, otherSourceFieldsOf, outletByKey, outletsOf, parseCanvasFoldId, parseCanvasGroupId, parseCanvasNodeId, parseFieldPath, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, recomputeMembers, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, resolvePath, rewriteReferences, sameMembers, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
|
|
19117
|
+
export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ENUM_CHOICE_SET_DEFAULT_DISPLAY_FIELD, ENUM_CHOICE_SET_DEFAULT_VALUE_FIELD, ENUM_CHOICE_SET_FIELDS, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_FOLD_HEIGHT, FLOW_FOLD_WIDTH, FLOW_GROUP_COLORS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FieldValuePickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, GROUP_HEADER_HEIGHT, GROUP_MIN_HEIGHT, GROUP_MIN_WIDTH, GROUP_PADDING, GroupPanelComponent, HttpFlowBuilderApi, MergeTextEditorComponent, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourceDialogComponent, ResourceFormComponent, ResourcePanelComponent, RunDialogComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, boundsAround, buildClipboardPayload, canvasFoldId, canvasGroupId, canvasNodeId, checkConditionLogic, checkFlowName, choiceSetSourceOf, declaredChoiceSetSources, describeFieldPath, describePathEntry, duplicateField, elementIcon, emptyFlowDefinition, enumChoiceSetFieldType, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, foldSourceConnectorId, foldTargetConnectorId, groupColorClass, groupRect, hasAutoSize, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isFoldReference, isGlobalReference, isNumericType, isPathInside, isTypeCheckedOperator, isUnknownEnumChoiceSetField, isValidFlowName, isValued, loadPathLevel, matchesReferenceFilter, membersInside, moveCondition, moveField, navigatePath, otherSourceFieldsOf, outletByKey, outletsOf, parseCanvasFoldId, parseCanvasGroupId, parseCanvasNodeId, parseFieldPath, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, recomputeMembers, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, repeatedFieldNames, repeatingSectionOf, resolvePath, resourceFieldsOf, rewriteReferences, sameMembers, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
|
|
18482
19118
|
//# sourceMappingURL=esfaenza-flow-builder.mjs.map
|