@esfaenza/flow-builder 20.3.48 → 20.3.50

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.
@@ -3935,6 +3935,11 @@ class FlowDictionaryStore {
3935
3935
  assigneeTypes = computed(() => this._dictionaries().assigneeTypes ?? [], ...(ngDevMode ? [{ debugName: "assigneeTypes" }] : []));
3936
3936
  conditionLogicModes = computed(() => this._dictionaries().conditionLogicModes ?? [], ...(ngDevMode ? [{ debugName: "conditionLogicModes" }] : []));
3937
3937
  regionContainerTypes = computed(() => this._dictionaries().regionContainerTypes ?? [], ...(ngDevMode ? [{ debugName: "regionContainerTypes" }] : []));
3938
+ /**
3939
+ * §5.2 — i formati di un campo di testo. Elenco **aperto**: vuoto significa «non lo so» e un nome
3940
+ * fuori elenco e' un'osservazione, non un errore — i pattern li registra l'host, non la libreria.
3941
+ */
3942
+ screenFieldFormats = computed(() => this._dictionaries().screenFieldFormats ?? [], ...(ngDevMode ? [{ debugName: "screenFieldFormats" }] : []));
3938
3943
  screenFieldInputsRevisited = computed(() => this._dictionaries().screenFieldInputsRevisited ?? [], ...(ngDevMode ? [{ debugName: "screenFieldInputsRevisited" }] : []));
3939
3944
  /**
3940
3945
  * §5.2 — le proprieta' citabili in un choice set da enum. Elenco vuoto: il dizionario non le
@@ -8018,6 +8023,12 @@ class NamePickerComponent {
8018
8023
  * La gravita' dell'avviso. Non e' cosmetica: un'action o un form che non esistono sono
8019
8024
  * **errori** che bloccano l'attivazione (`ACTION_UNKNOWN`, `FORM_UNKNOWN`), mentre un flow
8020
8025
  * senza versione attiva e' un avviso. Mostrarli con lo stesso colore direbbe il falso.
8026
+ *
8027
+ * `'note'` e' il terzo caso, e serve dove l'elenco **non e' autoritativo**: il `format` di un campo
8028
+ * di screen (§5.2) lo registra l'host, quindi un nome fuori catalogo non e' ne' un errore ne' un
8029
+ * difetto — e' un'osservazione (`SCREEN_FIELD_FORMAT_UNKNOWN`). Si scrive senza colore, come il
8030
+ * `connector-editor` scrive il promemoria del ramo di guasto: il colore d'avviso su un nome
8031
+ * legittimo manda a cercare un difetto che non c'e'.
8021
8032
  */
8022
8033
  unknownSeverity = input('warn', ...(ngDevMode ? [{ debugName: "unknownSeverity" }] : []));
8023
8034
  /**
@@ -12405,6 +12416,28 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
12405
12416
  selectedAutoSelectsSingleChoice = computed(() => this.selectedField()?.autoSelectSingleChoice === true, ...(ngDevMode ? [{ debugName: "selectedAutoSelectsSingleChoice" }] : []));
12406
12417
  /** §5.2 — `scale` su un campo non numerico e' `SCALE_NOT_APPLICABLE`. */
12407
12418
  scaleApplies = computed(() => this.dictionary.supportsScale(this.selectedField()?.dataType), ...(ngDevMode ? [{ debugName: "scaleApplies" }] : []));
12419
+ /**
12420
+ * §5.2 — il `format` vale dove il campo raccoglie **un solo testo**: altrove e'
12421
+ * `SCREEN_FIELD_FORMAT_NOT_APPLICABLE` e non ha effetto. La condizione e' quella, non l'elenco dei
12422
+ * tipi di campo: un `dataType` assente vale `String` perche' e' il default del contratto, e il
12423
+ * plurale lo dice il dizionario (`isCollection`), non il codice.
12424
+ */
12425
+ formatApplies = computed(() => {
12426
+ const entry = this.selectedType();
12427
+ if (!entry?.storesValue || entry.isCollection) {
12428
+ return false;
12429
+ }
12430
+ return (this.selectedField()?.dataType ?? 'String') === 'String';
12431
+ }, ...(ngDevMode ? [{ debugName: "formatApplies" }] : []));
12432
+ /**
12433
+ * I formati da proporre. L'elenco e' **aperto** — i pattern li registra l'host — quindi vuoto
12434
+ * significa «non lo so» e un nome fuori elenco non si accusa: il picker lo scrive come nota.
12435
+ */
12436
+ formatOptions = computed(() => this.dictionary.screenFieldFormats().map((entry) => ({
12437
+ name: entry.value,
12438
+ label: entry.label,
12439
+ description: entry.description,
12440
+ })), ...(ngDevMode ? [{ debugName: "formatOptions" }] : []));
12408
12441
  requiresObjectType = computed(() => this.dictionary.requiresObjectType(this.selectedField()?.dataType), ...(ngDevMode ? [{ debugName: "requiresObjectType" }] : []));
12409
12442
  objectTypeIsStructure = computed(() => this.dictionary.isStructure(this.selectedField()?.dataType), ...(ngDevMode ? [{ debugName: "objectTypeIsStructure" }] : []));
12410
12443
  /** L'oggetto di un `ObjectProvided`, cioe' la parte prima del punto (§5.2). */
@@ -12506,6 +12539,25 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
12506
12539
  }, ...(ngDevMode ? [{ debugName: "repeaterIssue" }] : []));
12507
12540
  /** §5.2 — il massimo mancante e' un **avviso**: senza, l'utente puo' aggiungere righe all'infinito. */
12508
12541
  repeaterHasNoLimit = computed(() => this.selectedIsRepeatingSection() && this.selectedField()?.maxInstances === undefined, ...(ngDevMode ? [{ debugName: "repeaterHasNoLimit" }] : []));
12542
+ /**
12543
+ * §5.2 — righe iniziali fuori dai limiti. Il runtime le riporta comunque dentro, quindi non e' un
12544
+ * difetto che rompe qualcosa: e' che il numero scritto **non e' quello che si vedra'**, e finche'
12545
+ * nessuno lo dice si crede il contrario. Va detto qui, dove i tre numeri stanno insieme.
12546
+ */
12547
+ repeaterInitialOutOfRange = computed(() => {
12548
+ const field = this.selectedField();
12549
+ const initial = field?.initialInstances;
12550
+ if (!this.selectedIsRepeatingSection() || initial === undefined) {
12551
+ return null;
12552
+ }
12553
+ if (field?.minInstances !== undefined && initial < field.minInstances) {
12554
+ return `Sotto il minimo (${field.minInstances}): la schermata si aprira’ comunque con ${field.minInstances} righe.`;
12555
+ }
12556
+ if (field?.maxInstances !== undefined && initial > field.maxInstances) {
12557
+ return `Sopra il massimo (${field.maxInstances}): la schermata si aprira’ comunque con ${field.maxInstances} righe.`;
12558
+ }
12559
+ return null;
12560
+ }, ...(ngDevMode ? [{ debugName: "repeaterInitialOutOfRange" }] : []));
12509
12561
  /**
12510
12562
  * La variabile del documento con quel nome, se e' una variabile. Non un riferimento qualunque: una
12511
12563
  * costante o una formula qui e' `REPEATING_SECTION_INVALID`, e distinguerle e' il punto.
@@ -12574,6 +12626,7 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
12574
12626
  delete field.defaultValue;
12575
12627
  delete field.scale;
12576
12628
  delete field.maxLength;
12629
+ delete field.format;
12577
12630
  delete field.validationRule;
12578
12631
  delete field.inputsOnNextNavToAssocScrn;
12579
12632
  }
@@ -12595,6 +12648,7 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
12595
12648
  delete field.itemReference;
12596
12649
  delete field.minInstances;
12597
12650
  delete field.maxInstances;
12651
+ delete field.initialInstances;
12598
12652
  delete field.allowAdd;
12599
12653
  delete field.allowRemove;
12600
12654
  delete field.addButtonLabel;
@@ -13140,7 +13194,7 @@ class DynamicScreenInspectorComponent extends NodeInspectorBase {
13140
13194
  });
13141
13195
  }
13142
13196
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DynamicScreenInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13143
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", 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\r\n <!-- --------------------------------------------- regole della schermata (\u00A75.2) -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Regole della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Le verifiche che <strong>non appartengono a nessun campo</strong>: \u00ABalmeno un punto fra le\r\n due sezioni\u00BB, \u00ABla somma delle percentuali fa cento\u00BB. Si valutano all\u2019invio, dopo i campi e\r\n dopo le righe, e una regola non soddisfatta <strong>impedisce alla schermata di\r\n avanzare</strong>. La <code>validationRule</code> di un campo non basta \u2014 giudica un solo\r\n valore, e su una sezione ripetibile e\u2019 addirittura inerte.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (rule of screenRules(); 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\">{{ rule.errorMessage || '(senza messaggio)' }}</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--sm\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeScreenRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 qui `Formula` **e'** un `conditionLogic` ammesso, a differenza di un\r\n `filterLogic`: la regola puo' essere un'espressione booleana. Resta il consiglio,\r\n scritto sotto, di preferire le condizioni.\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"La schermata e\u2019 valida quando\"\r\n [allowFormula]=\"true\"\r\n [issuePath]=\"'validationRules[' + $index + ']'\"\r\n (changed)=\"onScreenRuleConditionsChanged($index, $event)\"\r\n />\r\n @if (screenRuleIsEmpty(rule)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La regola non verifica niente: senza condizioni non impedisce mai l\u2019invio.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\r\n <fb-merge-text-editor\r\n [text]=\"rule.errorMessage || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Indica almeno un punto di fornitura, luce o gas.\"\r\n ariaLabel=\"Messaggio della regola\"\r\n (textChange)=\"setScreenRuleProperty($index, 'errorMessage', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il testo dell\u2019autore e non nomina nessun campo: il runtime non ne costruisce\r\n uno, perche\u2019 un campo da nominare non c\u2019e\u2019.\r\n </p>\r\n </fb-merge-text-editor>\r\n @if (screenRuleHasNoMessage(rule)) {\r\n <p class=\"fb-field__error\">\r\n Obbligatorio: senza, la schermata si rifiuta di avanzare senza dire perche\u2019\r\n (SCREEN_RULE_INVALID).\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggancia il messaggio a un campo</label>\r\n <!--\r\n Stesso elenco degli inneschi: i campi della schermata **senza** quelli dentro una\r\n riga, che esistono in una copia per elemento e non individuano un controllo (\u00A75.2).\r\n -->\r\n <fb-name-picker\r\n [value]=\"rule.fieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo\"\r\n placeholder=\"Nessuno: il messaggio e\u2019 della schermata\"\r\n unknownMessage=\"Questo nome non e\u2019 un campo di questa schermata.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha campi a cui agganciare il messaggio.\"\r\n (valueChange)=\"setScreenRuleProperty($index, 'fieldName', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Facoltativo. Senza, il rilievo e\u2019 <strong>della schermata</strong> e il frontend lo\r\n mostra dove mostra i messaggi che non hanno un controllo \u2014 in testa al form, o\r\n accanto ad Avanti.\r\n </p>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna regola: la schermata avanza se i campi sono a posto.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenRule()\">Aggiungi regola</button>\r\n <p class=\"fb-field__hint\">\r\n Scrivile con le <strong>condizioni</strong>, non con la formula: <code>IsEmpty</code> su una\r\n collection e\u2019 un operatore che il runtime valuta, mentre la stessa domanda in una formula\r\n chiede al motore una funzione che potrebbe non avere. Vale soprattutto per la verifica piu\u2019\r\n frequente \u2014 che una sezione ripetibile abbia almeno una riga.\r\n </p>\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' }}.&lt;membro&gt;</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>&#123;!Riferimento&#125;</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' }}.&lt;membro&gt;</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>&#123;!{{ selectedField()!.itemReference || 'Cursore' }}.Membro&#125;</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 });
13197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", 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\r\n <!-- --------------------------------------------- regole della schermata (\u00A75.2) -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Regole della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Le verifiche che <strong>non appartengono a nessun campo</strong>: \u00ABalmeno un punto fra le\r\n due sezioni\u00BB, \u00ABla somma delle percentuali fa cento\u00BB. Si valutano all\u2019invio, dopo i campi e\r\n dopo le righe, e una regola non soddisfatta <strong>impedisce alla schermata di\r\n avanzare</strong>. La <code>validationRule</code> di un campo non basta \u2014 giudica un solo\r\n valore, e su una sezione ripetibile e\u2019 addirittura inerte.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (rule of screenRules(); 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\">{{ rule.errorMessage || '(senza messaggio)' }}</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--sm\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeScreenRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 qui `Formula` **e'** un `conditionLogic` ammesso, a differenza di un\r\n `filterLogic`: la regola puo' essere un'espressione booleana. Resta il consiglio,\r\n scritto sotto, di preferire le condizioni.\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"La schermata e\u2019 valida quando\"\r\n [allowFormula]=\"true\"\r\n [issuePath]=\"'validationRules[' + $index + ']'\"\r\n (changed)=\"onScreenRuleConditionsChanged($index, $event)\"\r\n />\r\n @if (screenRuleIsEmpty(rule)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La regola non verifica niente: senza condizioni non impedisce mai l\u2019invio.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\r\n <fb-merge-text-editor\r\n [text]=\"rule.errorMessage || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Indica almeno un punto di fornitura, luce o gas.\"\r\n ariaLabel=\"Messaggio della regola\"\r\n (textChange)=\"setScreenRuleProperty($index, 'errorMessage', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il testo dell\u2019autore e non nomina nessun campo: il runtime non ne costruisce\r\n uno, perche\u2019 un campo da nominare non c\u2019e\u2019.\r\n </p>\r\n </fb-merge-text-editor>\r\n @if (screenRuleHasNoMessage(rule)) {\r\n <p class=\"fb-field__error\">\r\n Obbligatorio: senza, la schermata si rifiuta di avanzare senza dire perche\u2019\r\n (SCREEN_RULE_INVALID).\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggancia il messaggio a un campo</label>\r\n <!--\r\n Stesso elenco degli inneschi: i campi della schermata **senza** quelli dentro una\r\n riga, che esistono in una copia per elemento e non individuano un controllo (\u00A75.2).\r\n -->\r\n <fb-name-picker\r\n [value]=\"rule.fieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo\"\r\n placeholder=\"Nessuno: il messaggio e\u2019 della schermata\"\r\n unknownMessage=\"Questo nome non e\u2019 un campo di questa schermata.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha campi a cui agganciare il messaggio.\"\r\n (valueChange)=\"setScreenRuleProperty($index, 'fieldName', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Facoltativo. Senza, il rilievo e\u2019 <strong>della schermata</strong> e il frontend lo\r\n mostra dove mostra i messaggi che non hanno un controllo \u2014 in testa al form, o\r\n accanto ad Avanti.\r\n </p>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna regola: la schermata avanza se i campi sono a posto.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenRule()\">Aggiungi regola</button>\r\n <p class=\"fb-field__hint\">\r\n Scrivile con le <strong>condizioni</strong>, non con la formula: <code>IsEmpty</code> su una\r\n collection e\u2019 un operatore che il runtime valuta, mentre la stessa domanda in una formula\r\n chiede al motore una funzione che potrebbe non avere. Vale soprattutto per la verifica piu\u2019\r\n frequente \u2014 che una sezione ripetibile abbia almeno una riga.\r\n </p>\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' }}.&lt;membro&gt;</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>&#123;!Riferimento&#125;</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 @if (formatApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formato</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.format\"\r\n [options]=\"formatOptions()\"\r\n label=\"Formato\"\r\n placeholder=\"Nessuno: un testo qualunque\"\r\n unknownSeverity=\"note\"\r\n unknownMessage=\"Non e\u2019 fra i formati proposti: se l\u2019host ha registrato il suo pattern va bene cosi\u2019, altrimenti il campo non verifica niente (SCREEN_FIELD_FORMAT_UNKNOWN).\"\r\n emptyMessage=\"Nessun formato dichiarato: puoi scriverne uno registrato dall\u2019host.\"\r\n (valueChange)=\"setFieldProperty('format', $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n <strong>Che cosa</strong> il campo raccoglie, oltre al fatto di essere un testo: il\r\n frontend ne ricava il tipo di input, e il runtime rifiuta l\u2019invio sbagliato\r\n (FORMAT_INVALID). L\u2019elenco non e\u2019 chiuso \u2014 i pattern li registra l\u2019host, non la\r\n libreria \u2014 quindi un nome fuori elenco non e\u2019 un difetto.\r\n </p>\r\n <p class=\"fb-field__hint\">\r\n Non e\u2019 un\u2019etichetta che il flow puo\u2019 leggere: se a valle serve sapere che un numero e\u2019\r\n un cellulare, quella distinzione sta nel dato (due campi, o due membri di una\r\n <code>Structure</code>). E non riscrive il valore \u2014 togliere spazi o aggiungere il\r\n prefisso e\u2019 una trasformazione, e la fa una screen action.\r\n </p>\r\n </div>\r\n } @else if (selectedField()!.format) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>format</code> su un campo che non raccoglie un solo testo non ha effetto\r\n (SCREEN_FIELD_FORMAT_NOT_APPLICABLE).\r\n </p>\r\n }\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' }}.&lt;membro&gt;</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 <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe gia\u2019 aperte</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.initialInstances ?? ''\"\r\n (change)=\"setNumberProperty('initialInstances', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Quante righe trovare <strong>gia\u2019 aperte</strong> arrivando sulla schermata, ed e\u2019 un\u2019altra\r\n cosa dal minimo: il minimo e\u2019 un vincolo, queste sono una comodita\u2019 e si possono togliere\r\n tutte. Un punto luce e un punto gas gia\u2019 aperti, dove chi ha una sola commodity toglie\r\n quello che non gli serve, sono <code>1</code> qui con <code>0</code> come minimo; col\r\n minimo a 1 sarebbe un modulo che <strong>obbliga</strong> a entrambe.\r\n </p>\r\n <p class=\"fb-field__hint\">\r\n Si applicano <strong>una volta sola</strong>, e solo se nessuno ha ancora scritto la\r\n collection: righe arrivate da un ciclo o da una schermata precedente <em>sono</em> le righe\r\n iniziali e non vengono diluite con righe vuote, e chi le toglie tutte non se le ritrova.\r\n </p>\r\n @if (repeaterInitialOutOfRange(); as issue) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ issue }}</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>&#123;!{{ selectedField()!.itemReference || 'Cursore' }}.Membro&#125;</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 <!--\r\n Il formato sta **fuori** dal @switch perche' non e' del tipo di campo: e' di cio' che il campo\r\n raccoglie, e va visto anche dove non si applica \u2014 e\u2019 l\u00EC che si riconosce un\r\n SCREEN_FIELD_FORMAT_NOT_APPLICABLE senza aprire le proprieta\u2019.\r\n -->\r\n @if (field.format) {\r\n <span class=\"fb-scr__tag\">{{ field.format }}</span>\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 });
13144
13198
  }
13145
13199
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DynamicScreenInspectorComponent, decorators: [{
13146
13200
  type: Component,
@@ -13161,7 +13215,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
13161
13215
  StructureMemberPickerComponent,
13162
13216
  StructurePickerComponent,
13163
13217
  ValueEditorComponent,
13164
- ], 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\r\n <!-- --------------------------------------------- regole della schermata (\u00A75.2) -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Regole della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Le verifiche che <strong>non appartengono a nessun campo</strong>: \u00ABalmeno un punto fra le\r\n due sezioni\u00BB, \u00ABla somma delle percentuali fa cento\u00BB. Si valutano all\u2019invio, dopo i campi e\r\n dopo le righe, e una regola non soddisfatta <strong>impedisce alla schermata di\r\n avanzare</strong>. La <code>validationRule</code> di un campo non basta \u2014 giudica un solo\r\n valore, e su una sezione ripetibile e\u2019 addirittura inerte.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (rule of screenRules(); 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\">{{ rule.errorMessage || '(senza messaggio)' }}</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--sm\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeScreenRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 qui `Formula` **e'** un `conditionLogic` ammesso, a differenza di un\r\n `filterLogic`: la regola puo' essere un'espressione booleana. Resta il consiglio,\r\n scritto sotto, di preferire le condizioni.\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"La schermata e\u2019 valida quando\"\r\n [allowFormula]=\"true\"\r\n [issuePath]=\"'validationRules[' + $index + ']'\"\r\n (changed)=\"onScreenRuleConditionsChanged($index, $event)\"\r\n />\r\n @if (screenRuleIsEmpty(rule)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La regola non verifica niente: senza condizioni non impedisce mai l\u2019invio.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\r\n <fb-merge-text-editor\r\n [text]=\"rule.errorMessage || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Indica almeno un punto di fornitura, luce o gas.\"\r\n ariaLabel=\"Messaggio della regola\"\r\n (textChange)=\"setScreenRuleProperty($index, 'errorMessage', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il testo dell\u2019autore e non nomina nessun campo: il runtime non ne costruisce\r\n uno, perche\u2019 un campo da nominare non c\u2019e\u2019.\r\n </p>\r\n </fb-merge-text-editor>\r\n @if (screenRuleHasNoMessage(rule)) {\r\n <p class=\"fb-field__error\">\r\n Obbligatorio: senza, la schermata si rifiuta di avanzare senza dire perche\u2019\r\n (SCREEN_RULE_INVALID).\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggancia il messaggio a un campo</label>\r\n <!--\r\n Stesso elenco degli inneschi: i campi della schermata **senza** quelli dentro una\r\n riga, che esistono in una copia per elemento e non individuano un controllo (\u00A75.2).\r\n -->\r\n <fb-name-picker\r\n [value]=\"rule.fieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo\"\r\n placeholder=\"Nessuno: il messaggio e\u2019 della schermata\"\r\n unknownMessage=\"Questo nome non e\u2019 un campo di questa schermata.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha campi a cui agganciare il messaggio.\"\r\n (valueChange)=\"setScreenRuleProperty($index, 'fieldName', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Facoltativo. Senza, il rilievo e\u2019 <strong>della schermata</strong> e il frontend lo\r\n mostra dove mostra i messaggi che non hanno un controllo \u2014 in testa al form, o\r\n accanto ad Avanti.\r\n </p>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna regola: la schermata avanza se i campi sono a posto.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenRule()\">Aggiungi regola</button>\r\n <p class=\"fb-field__hint\">\r\n Scrivile con le <strong>condizioni</strong>, non con la formula: <code>IsEmpty</code> su una\r\n collection e\u2019 un operatore che il runtime valuta, mentre la stessa domanda in una formula\r\n chiede al motore una funzione che potrebbe non avere. Vale soprattutto per la verifica piu\u2019\r\n frequente \u2014 che una sezione ripetibile abbia almeno una riga.\r\n </p>\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' }}.&lt;membro&gt;</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>&#123;!Riferimento&#125;</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' }}.&lt;membro&gt;</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>&#123;!{{ selectedField()!.itemReference || 'Cursore' }}.Membro&#125;</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"] }]
13218
+ ], 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\r\n <!-- --------------------------------------------- regole della schermata (\u00A75.2) -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Regole della schermata</legend>\r\n <p class=\"fb-section__note\">\r\n Le verifiche che <strong>non appartengono a nessun campo</strong>: \u00ABalmeno un punto fra le\r\n due sezioni\u00BB, \u00ABla somma delle percentuali fa cento\u00BB. Si valutano all\u2019invio, dopo i campi e\r\n dopo le righe, e una regola non soddisfatta <strong>impedisce alla schermata di\r\n avanzare</strong>. La <code>validationRule</code> di un campo non basta \u2014 giudica un solo\r\n valore, e su una sezione ripetibile e\u2019 addirittura inerte.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (rule of screenRules(); 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\">{{ rule.errorMessage || '(senza messaggio)' }}</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--sm\"\r\n aria-label=\"Rimuovi la regola\"\r\n (click)=\"removeScreenRule($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!--\r\n \u00A75.2 \u2014 qui `Formula` **e'** un `conditionLogic` ammesso, a differenza di un\r\n `filterLogic`: la regola puo' essere un'espressione booleana. Resta il consiglio,\r\n scritto sotto, di preferire le condizioni.\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"La schermata e\u2019 valida quando\"\r\n [allowFormula]=\"true\"\r\n [issuePath]=\"'validationRules[' + $index + ']'\"\r\n (changed)=\"onScreenRuleConditionsChanged($index, $event)\"\r\n />\r\n @if (screenRuleIsEmpty(rule)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La regola non verifica niente: senza condizioni non impedisce mai l\u2019invio.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\r\n <fb-merge-text-editor\r\n [text]=\"rule.errorMessage || ''\"\r\n [rows]=\"2\"\r\n commitOn=\"change\"\r\n placeholder=\"Indica almeno un punto di fornitura, luce o gas.\"\r\n ariaLabel=\"Messaggio della regola\"\r\n (textChange)=\"setScreenRuleProperty($index, 'errorMessage', $event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il testo dell\u2019autore e non nomina nessun campo: il runtime non ne costruisce\r\n uno, perche\u2019 un campo da nominare non c\u2019e\u2019.\r\n </p>\r\n </fb-merge-text-editor>\r\n @if (screenRuleHasNoMessage(rule)) {\r\n <p class=\"fb-field__error\">\r\n Obbligatorio: senza, la schermata si rifiuta di avanzare senza dire perche\u2019\r\n (SCREEN_RULE_INVALID).\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Aggancia il messaggio a un campo</label>\r\n <!--\r\n Stesso elenco degli inneschi: i campi della schermata **senza** quelli dentro una\r\n riga, che esistono in una copia per elemento e non individuano un controllo (\u00A75.2).\r\n -->\r\n <fb-name-picker\r\n [value]=\"rule.fieldName\"\r\n [options]=\"triggerFieldOptions()\"\r\n label=\"Campo\"\r\n placeholder=\"Nessuno: il messaggio e\u2019 della schermata\"\r\n unknownMessage=\"Questo nome non e\u2019 un campo di questa schermata.\"\r\n unknownSeverity=\"error\"\r\n emptyMessage=\"La schermata non ha campi a cui agganciare il messaggio.\"\r\n (valueChange)=\"setScreenRuleProperty($index, 'fieldName', $event ?? '')\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Facoltativo. Senza, il rilievo e\u2019 <strong>della schermata</strong> e il frontend lo\r\n mostra dove mostra i messaggi che non hanno un controllo \u2014 in testa al form, o\r\n accanto ad Avanti.\r\n </p>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna regola: la schermata avanza se i campi sono a posto.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScreenRule()\">Aggiungi regola</button>\r\n <p class=\"fb-field__hint\">\r\n Scrivile con le <strong>condizioni</strong>, non con la formula: <code>IsEmpty</code> su una\r\n collection e\u2019 un operatore che il runtime valuta, mentre la stessa domanda in una formula\r\n chiede al motore una funzione che potrebbe non avere. Vale soprattutto per la verifica piu\u2019\r\n frequente \u2014 che una sezione ripetibile abbia almeno una riga.\r\n </p>\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' }}.&lt;membro&gt;</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>&#123;!Riferimento&#125;</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 @if (formatApplies()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formato</label>\r\n <fb-name-picker\r\n [value]=\"selectedField()!.format\"\r\n [options]=\"formatOptions()\"\r\n label=\"Formato\"\r\n placeholder=\"Nessuno: un testo qualunque\"\r\n unknownSeverity=\"note\"\r\n unknownMessage=\"Non e\u2019 fra i formati proposti: se l\u2019host ha registrato il suo pattern va bene cosi\u2019, altrimenti il campo non verifica niente (SCREEN_FIELD_FORMAT_UNKNOWN).\"\r\n emptyMessage=\"Nessun formato dichiarato: puoi scriverne uno registrato dall\u2019host.\"\r\n (valueChange)=\"setFieldProperty('format', $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n <strong>Che cosa</strong> il campo raccoglie, oltre al fatto di essere un testo: il\r\n frontend ne ricava il tipo di input, e il runtime rifiuta l\u2019invio sbagliato\r\n (FORMAT_INVALID). L\u2019elenco non e\u2019 chiuso \u2014 i pattern li registra l\u2019host, non la\r\n libreria \u2014 quindi un nome fuori elenco non e\u2019 un difetto.\r\n </p>\r\n <p class=\"fb-field__hint\">\r\n Non e\u2019 un\u2019etichetta che il flow puo\u2019 leggere: se a valle serve sapere che un numero e\u2019\r\n un cellulare, quella distinzione sta nel dato (due campi, o due membri di una\r\n <code>Structure</code>). E non riscrive il valore \u2014 togliere spazi o aggiungere il\r\n prefisso e\u2019 una trasformazione, e la fa una screen action.\r\n </p>\r\n </div>\r\n } @else if (selectedField()!.format) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n <code>format</code> su un campo che non raccoglie un solo testo non ha effetto\r\n (SCREEN_FIELD_FORMAT_NOT_APPLICABLE).\r\n </p>\r\n }\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' }}.&lt;membro&gt;</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 <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Righe gia\u2019 aperte</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"0\"\r\n [value]=\"selectedField()!.initialInstances ?? ''\"\r\n (change)=\"setNumberProperty('initialInstances', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Quante righe trovare <strong>gia\u2019 aperte</strong> arrivando sulla schermata, ed e\u2019 un\u2019altra\r\n cosa dal minimo: il minimo e\u2019 un vincolo, queste sono una comodita\u2019 e si possono togliere\r\n tutte. Un punto luce e un punto gas gia\u2019 aperti, dove chi ha una sola commodity toglie\r\n quello che non gli serve, sono <code>1</code> qui con <code>0</code> come minimo; col\r\n minimo a 1 sarebbe un modulo che <strong>obbliga</strong> a entrambe.\r\n </p>\r\n <p class=\"fb-field__hint\">\r\n Si applicano <strong>una volta sola</strong>, e solo se nessuno ha ancora scritto la\r\n collection: righe arrivate da un ciclo o da una schermata precedente <em>sono</em> le righe\r\n iniziali e non vengono diluite con righe vuote, e chi le toglie tutte non se le ritrova.\r\n </p>\r\n @if (repeaterInitialOutOfRange(); as issue) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ issue }}</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>&#123;!{{ selectedField()!.itemReference || 'Cursore' }}.Membro&#125;</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 <!--\r\n Il formato sta **fuori** dal @switch perche' non e' del tipo di campo: e' di cio' che il campo\r\n raccoglie, e va visto anche dove non si applica \u2014 e\u2019 l\u00EC che si riconosce un\r\n SCREEN_FIELD_FORMAT_NOT_APPLICABLE senza aprire le proprieta\u2019.\r\n -->\r\n @if (field.format) {\r\n <span class=\"fb-scr__tag\">{{ field.format }}</span>\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"] }]
13165
13219
  }], ctorParameters: () => [], propDecorators: { initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], composer: [{ type: i0.ViewChild, args: ['composer', { isSignal: true }] }] } });
13166
13220
 
13167
13221
  /**