@esfaenza/flow-builder 20.3.12 → 20.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -334,10 +334,18 @@ vuoto resta "non lo so" — primitiva non esposta, tipo senza valori dichiarati,
334
334
  da scegliere — e allora il valore si digita; con l'elenco popolato un valore fuori elenco e' un
335
335
  **avviso**, perche' nessun codice della §7 lo blocca ed e' l'editor ad accorgersene prima del runtime.
336
336
 
337
- Perche' l'editor conosca il tipo della destinazione serve saperlo: in un Assignment arriva da
338
- `getWritableReferences` (per corrispondenza esatta del nome: su `Cliente.Stato` resta ignoto), in una
339
- condizione e in un filtro dal riferimento o dal campo a sinistra, in un Transform dal **membro** della
340
- classe di destinazione. Dove il tipo non si sa, il campo resta generico invece di indovinare.
337
+ Perche' l'editor conosca il tipo della destinazione serve saperlo, e i percorsi non fanno eccezione:
338
+ `POST /flows/references` elenca le **radici** e non i percorsi, quindi su `Richiesta.Stato` la
339
+ corrispondenza esatta del nome non trova niente. Il tipo si ricava allora navigando la catena
340
+ `core/reference-path` innesta l'elenco dei riferimenti su `core/path-navigation`: trova il prefisso
341
+ dichiarato piu' lungo (§4.1), naviga il resto un segmento alla volta e prende il tipo dell'**ultimo**
342
+ segmento. Lo usano l'Assignment (su `getWritableReferences`), le condizioni e i filtri sui record —
343
+ dove la radice e' l'entita', quindi `Cliente.Citta` si risolve come il campo che e'. In un Transform
344
+ il tipo viene dal **membro** della classe di destinazione, che l'inspector già descrive.
345
+
346
+ Dove il tipo non si sa — catena interrotta, catalogo assente, segmento inesistente, percorso radicato
347
+ sull'output automatico di un elemento (che il backend non verifica, §7) — il campo resta generico
348
+ invece di indovinare: un elenco chiuso di valori sbagliati e' peggio di una casella di testo.
341
349
 
342
350
  Lo stesso controllo, nella variante `<fb-name-picker>`, vale per **ogni altro nome che il flow
343
351
  prende da fuori**: tipo di action e action, script, form, i nomi dei loro parametri di ingresso
@@ -3781,6 +3781,244 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3781
3781
  args: [{ selector: 'fb-element-palette', standalone: true, imports: [FFlowModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-palette__search\">\r\n <input\r\n type=\"search\"\r\n placeholder=\"Cerca un elemento\"\r\n aria-label=\"Cerca un elemento\"\r\n (input)=\"onFilter($any($event.target).value)\"\r\n />\r\n</div>\r\n\r\n@if (isEmpty()) {\r\n <p class=\"fb-palette__empty\">\r\n Il dizionario degli elementi non e\u2019 ancora disponibile.\r\n </p>\r\n}\r\n\r\n<div class=\"fb-palette__groups\">\r\n @for (group of groups(); track group.category) {\r\n <section class=\"fb-palette__group\">\r\n <h3 class=\"fb-palette__category\">{{ group.category }}</h3>\r\n @for (item of group.items; track item.key) {\r\n <!--\r\n `fExternalItem` rende la voce trascinabile sul canvas: il rilascio emette\r\n `fCreateNode` con questo `fData` e la posizione, che diventa locationX/locationY.\r\n Il dato e' l'oggetto {type, variant}, non il solo tipo: due voci dello stesso tipo\r\n si distinguono soltanto per la variante.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-palette__item\"\r\n fExternalItem\r\n [fExternalItemId]=\"item.key\"\r\n [fData]=\"item.data\"\r\n [class.fb-palette__item--warn]=\"isIncompatible(item)\"\r\n [title]=\"hintOf(item)\"\r\n (click)=\"pick(item)\"\r\n >\r\n <span class=\"fb-palette__icon\" [class]=\"'fb-palette__icon ' + categoryClass(group.category)\">\r\n {{ iconOf(item) }}\r\n </span>\r\n <span class=\"fb-palette__text\">\r\n <span class=\"fb-palette__label\">{{ item.label }}</span>\r\n @if (item.entry.hasAutomaticOutput) {\r\n <span class=\"fb-palette__flag\" title=\"Espone un output automatico sotto il nome dell\u2019elemento\">\r\n output automatico\r\n </span>\r\n }\r\n @if (faultLabel(item)) {\r\n <span class=\"fb-palette__flag\" title=\"Ha un ramo che l\u2019autore puo\u2019 prevedere e disegnare\">\r\n {{ faultLabel(item) }}\r\n </span>\r\n }\r\n </span>\r\n @if (isIncompatible(item)) {\r\n <span class=\"fb-palette__warn\" aria-hidden=\"true\">!</span>\r\n }\r\n </button>\r\n }\r\n </section>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-right:1px solid var(--fb-border, #d6dae1)}.fb-palette__search{padding:8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-palette__search input{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-palette__search input:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-palette__groups{flex:1;min-height:0;overflow-y:auto;padding:4px 0 12px}.fb-palette__empty{margin:12px 10px;font-size:12px;color:var(--fb-text-muted, #667085)}.fb-palette__category{margin:10px 10px 4px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:calc(100% - 12px);margin:1px 6px;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:grab;transition:background .12s ease}.fb-palette__item:hover{background:var(--fb-surface-alt, #f7f8fa)}.fb-palette__item:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-2px}.fb-palette__icon{display:grid;place-items:center;flex:0 0 auto;width:24px;height:24px;border-radius:var(--fb-radius-xs, 6px);background:var(--fb-icon-bg, #98a2b3);color:#fff;font-size:11px;font-weight:700}.cat-screen{--fb-icon-bg: #3b82f6}.cat-logic{--fb-icon-bg: #8b5cf6}.cat-data{--fb-icon-bg: #06b6d4}.cat-action{--fb-icon-bg: #f59e0b}.cat-flow{--fb-icon-bg: #14b8a6}.fb-palette__text{display:flex;flex-direction:column;min-width:0}.fb-palette__label{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-palette__flag{font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item--warn .fb-palette__label{color:var(--fb-warning, #b7791f)}.fb-palette__warn{margin-left:auto;color:var(--fb-warning, #b7791f);font-weight:700}:host ::ng-deep .f-external-item-preview{padding:4px 8px;border:1px solid var(--fb-accent, #2f6feb);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 4px 12px #1018282e;opacity:.95}\n"] }]
3782
3782
  }], propDecorators: { processType: [{ type: i0.Input, args: [{ isSignal: true, alias: "processType", required: false }] }], elementPicked: [{ type: i0.Output, args: ["elementPicked"] }] } });
3783
3783
 
3784
+ /**
3785
+ * Il tipo di cio' che un riferimento designa — FRONTEND.md §4.1, §4.3, §4.4, §4.6, §4.7.1.
3786
+ *
3787
+ * `POST /flows/references` elenca le **radici**, non i percorsi: `Richiesta` c'e', `Richiesta.Stato`
3788
+ * no. Cercare la corrispondenza esatta del valore in quell'elenco basta per una risorsa e non basta
3789
+ * per un percorso, e la conseguenza non e' cosmetica: senza il tipo dell'ultimo segmento un membro
3790
+ * `Enum` diventa una casella di testo su un insieme chiuso (§4.6) e un confronto fra tipi
3791
+ * incompatibili non si vede prima del backend (§4.3).
3792
+ *
3793
+ * Il tipo di un percorso pero' si **ricava**: e' `core/path-navigation` che naviga la catena, e qui
3794
+ * c'e' l'innesto fra i due mondi — dall'elenco dei riferimenti alla tappa da cui partire, e dalla
3795
+ * tappa all'ultimo segmento. Sta in `core/` e non in un inspector perche' i chiamanti sono tre
3796
+ * (Assignment, condizioni, filtri) e la regola del **prefisso dichiarato piu' lungo** e' una sola:
3797
+ * duplicarla vorrebbe dire farla divergere.
3798
+ *
3799
+ * Le due asimmetrie da tenere a mente:
3800
+ *
3801
+ * - **Non ogni radice e' navigabile.** Un output automatico dichiara il proprio tipo, ma la
3802
+ * specifica tiene i percorsi che ne partono fuori dalla validazione (§7): dedurne il tipo qui
3803
+ * significherebbe far dire all'editor cio' che il backend non verifica. Stessa scelta del
3804
+ * `reference-picker`, e le due devono restare la stessa.
3805
+ * - **"Non lo so" resta "non lo so".** Catena interrotta, catalogo assente, segmento inesistente:
3806
+ * l'esito e' `null` e il chiamante ricade sul controllo generico. Un tipo indovinato e' peggio
3807
+ * di un tipo mancante, perche' fa comparire un elenco chiuso di valori sbagliati.
3808
+ */
3809
+ /**
3810
+ * Il riferimento dichiarato piu' **lungo** che sia un prefisso del valore, sui confini del punto.
3811
+ *
3812
+ * Non e' il primo segmento: un nome dichiarato puo' contenere il punto, e le globali sono il caso
3813
+ * per cui la regola esiste — di `$User.Anagrafica.Citta` il dizionario dichiara `$User.Anagrafica`
3814
+ * (§4.1: vince il prefisso dichiarato piu' lungo).
3815
+ */
3816
+ function declaredPrefixOf(references, value) {
3817
+ const trimmed = (value ?? '').trim();
3818
+ if (!trimmed) {
3819
+ return undefined;
3820
+ }
3821
+ let best;
3822
+ for (const candidate of references) {
3823
+ if (!candidate.name || (trimmed !== candidate.name && !trimmed.startsWith(`${candidate.name}.`))) {
3824
+ continue;
3825
+ }
3826
+ if (!best || candidate.name.length > best.name.length) {
3827
+ best = candidate;
3828
+ }
3829
+ }
3830
+ return best;
3831
+ }
3832
+ /**
3833
+ * La risorsa da cui il valore sta navigando, se e solo se il percorso e' verificabile: una
3834
+ * `Structure` con la sua classe, un record con la sua entita'. Un output automatico e' escluso di
3835
+ * proposito (vedi l'intestazione), e una collection non si naviga — il percorso designa l'insieme.
3836
+ */
3837
+ function navigableRootOf(references, value, isStructure) {
3838
+ const reference = declaredPrefixOf(references, value);
3839
+ if (!reference || reference.kind === 'ElementOutput' || !reference.objectType || reference.isCollection) {
3840
+ return undefined;
3841
+ }
3842
+ const trimmed = (value ?? '').trim();
3843
+ const path = trimmed.length > reference.name.length ? trimmed.slice(reference.name.length + 1) : '';
3844
+ if (isStructure(reference.dataType)) {
3845
+ return { reference, container: { kind: 'structure', name: reference.objectType }, path };
3846
+ }
3847
+ return reference.dataType === 'Object'
3848
+ ? { reference, container: { kind: 'object', name: reference.objectType }, path }
3849
+ : undefined;
3850
+ }
3851
+ /** Il tipo di un segmento risolto: campo o membro, ridotti al minimo comune (§4.7.1). */
3852
+ function typeOfEntry(entry) {
3853
+ return {
3854
+ dataType: entry.dataType,
3855
+ objectType: entry.objectType ?? undefined,
3856
+ isCollection: entry.isCollection,
3857
+ isWritable: entry.isWritable,
3858
+ origin: 'member',
3859
+ };
3860
+ }
3861
+ function typeOfReference(reference) {
3862
+ return {
3863
+ dataType: reference.dataType ?? undefined,
3864
+ objectType: reference.objectType ?? undefined,
3865
+ isCollection: reference.isCollection,
3866
+ isWritable: reference.isWritable,
3867
+ origin: 'resource',
3868
+ };
3869
+ }
3870
+ /**
3871
+ * Il tipo di un riferimento: la risorsa se il nome e' in elenco, altrimenti l'ultimo segmento del
3872
+ * percorso navigato. `null` = non si sa, e non autorizza a mostrare un elenco chiuso.
3873
+ *
3874
+ * `usage` vale solo per l'ultimo segmento quando la tappa e' un'entita' (§4.4): in un filtro serve
3875
+ * `filterable`, altrove `any` — chiederlo con l'uso sbagliato farebbe sparire un campo che esiste.
3876
+ */
3877
+ async function resolveReferenceType(catalog, references, value, isStructure, usage) {
3878
+ const trimmed = (value ?? '').trim();
3879
+ if (!trimmed) {
3880
+ return null;
3881
+ }
3882
+ const exact = references.find((candidate) => candidate.name === trimmed);
3883
+ if (exact) {
3884
+ return typeOfReference(exact);
3885
+ }
3886
+ const root = navigableRootOf(references, trimmed, isStructure);
3887
+ if (!root?.path) {
3888
+ return null;
3889
+ }
3890
+ return resolvePathType(catalog, root.container, root.path, usage);
3891
+ }
3892
+ /** Come sopra, dentro una tappa già nota: e' il caso dei filtri, dove la radice e' l'entita'. */
3893
+ async function resolvePathType(catalog, container, path, usage) {
3894
+ const trimmed = (path ?? '').trim();
3895
+ if (!trimmed) {
3896
+ return null;
3897
+ }
3898
+ const resolution = await resolvePath(catalog, container, trimmed, { usage });
3899
+ // Solo `resolved` e' autorevole: `unknown` e `unverifiable` sono cio' che il picker segnala, non
3900
+ // un tipo su cui costruire un elenco di valori.
3901
+ return resolution.status === 'resolved' && resolution.last ? typeOfEntry(resolution.last) : null;
3902
+ }
3903
+ function sameType(a, b) {
3904
+ if (!a || !b) {
3905
+ return a === b;
3906
+ }
3907
+ return (a.dataType === b.dataType &&
3908
+ a.objectType === b.objectType &&
3909
+ !!a.isCollection === !!b.isCollection &&
3910
+ a.isWritable === b.isWritable &&
3911
+ a.origin === b.origin);
3912
+ }
3913
+ /**
3914
+ * Il motore: risolve in background e pubblica per lettura sincrona.
3915
+ *
3916
+ * Perche' non un `computed`: la risoluzione e' asincrona (una chiamata di catalogo per tappa) e i
3917
+ * valori sono **molti** — un'operazione di Assignment per riga. Quindi un `effect` che risolve e una
3918
+ * mappa da cui i template leggono. Da chiamare in un **contesto di iniezione**.
3919
+ *
3920
+ * Il token di sequenza per chiave non e' cosmetico: una risposta superata che arriva dopo
3921
+ * scriverebbe il tipo di un valore che l'utente ha già cambiato, e l'editor mostrerebbe l'elenco di
3922
+ * valori sbagliato senza che nulla lo segnali.
3923
+ */
3924
+ function trackTypes(request) {
3925
+ const types = signal(new Map(), ...(ngDevMode ? [{ debugName: "types" }] : []));
3926
+ const scope = signal('', ...(ngDevMode ? [{ debugName: "scope" }] : []));
3927
+ /**
3928
+ * Fuori dai signal di proposito: e' il registro di cio' che e' già stato chiesto, non uno stato da
3929
+ * cui ridisegnare. Leggerlo dentro l'`effect` che lo scrive sarebbe un ciclo.
3930
+ */
3931
+ const requested = new Map();
3932
+ const tokens = new Map();
3933
+ let sequence = 0;
3934
+ effect(() => {
3935
+ const current = request();
3936
+ scope.set(current?.scope ?? '');
3937
+ if (!current) {
3938
+ return;
3939
+ }
3940
+ for (const value of new Set(current.values)) {
3941
+ const trimmed = (value ?? '').trim();
3942
+ if (!trimmed) {
3943
+ continue;
3944
+ }
3945
+ const key = `${current.scope}${trimmed}`;
3946
+ if (requested.get(key) === current.version) {
3947
+ continue;
3948
+ }
3949
+ requested.set(key, current.version);
3950
+ const token = ++sequence;
3951
+ tokens.set(key, token);
3952
+ void current.resolve(trimmed).then((result) => {
3953
+ if (tokens.get(key) !== token) {
3954
+ return;
3955
+ }
3956
+ const known = types();
3957
+ // Riscrivere la mappa con lo stesso contenuto ridisegnerebbe l'inspector a ogni battuta.
3958
+ if (known.has(key) && sameType(known.get(key), result)) {
3959
+ return;
3960
+ }
3961
+ const next = new Map(known);
3962
+ next.set(key, result);
3963
+ types.set(next);
3964
+ });
3965
+ }
3966
+ });
3967
+ const typeOf = (value) => {
3968
+ const trimmed = (value ?? '').trim();
3969
+ if (!trimmed) {
3970
+ return undefined;
3971
+ }
3972
+ return types().get(`${scope()}${trimmed}`) ?? undefined;
3973
+ };
3974
+ return {
3975
+ typeOf,
3976
+ dataTypeOf: (value) => typeOf(value)?.dataType,
3977
+ objectTypeOf: (value) => typeOf(value)?.objectType,
3978
+ };
3979
+ }
3980
+ /**
3981
+ * I tipi di piu' riferimenti insieme, risolti in background. È cio' che sostituisce la ricerca per
3982
+ * corrispondenza esatta negli inspector: sulle risorse risponde come prima, sui percorsi risponde
3983
+ * dove prima taceva.
3984
+ */
3985
+ function referenceTypes(catalog, isStructure, request) {
3986
+ return trackTypes(() => {
3987
+ const current = request();
3988
+ if (!current) {
3989
+ return null;
3990
+ }
3991
+ const references = current.references;
3992
+ const usage = current.usage ?? 'any';
3993
+ return {
3994
+ scope: `references|${usage}`,
3995
+ // L'elenco arriva da una richiesta: cambia identita' a ogni risposta, ed e' esattamente
3996
+ // quando le deduzioni vanno rifatte (una variabile rinominata, un tipo cambiato).
3997
+ version: references,
3998
+ values: current.values,
3999
+ resolve: (value) => resolveReferenceType(catalog, references, value, isStructure, usage),
4000
+ };
4001
+ });
4002
+ }
4003
+ /** I tipi di piu' percorsi dentro una tappa nota: i filtri su un'entita' (§4.4). */
4004
+ function pathTypes(catalog, request) {
4005
+ return trackTypes(() => {
4006
+ const current = request();
4007
+ const container = current?.container;
4008
+ if (!current || !container?.name) {
4009
+ return null;
4010
+ }
4011
+ const usage = current.usage ?? 'any';
4012
+ return {
4013
+ scope: `${container.kind}:${container.name}|${usage}`,
4014
+ // Tappa e uso stanno già nello scope: dentro lo stesso scope la risposta non cambia.
4015
+ version: 'container',
4016
+ values: current.paths,
4017
+ resolve: (path) => resolvePathType(catalog, container, path, usage),
4018
+ };
4019
+ });
4020
+ }
4021
+
3784
4022
  /**
3785
4023
  * Selettore di riferimento — FRONTEND.md §4.1, §4.5, §4.7, §5.2, §6.4, §13.3.
3786
4024
  *
@@ -3941,57 +4179,18 @@ class ReferencePickerComponent {
3941
4179
  });
3942
4180
  }
3943
4181
  /**
3944
- * Il riferimento dichiarato piu' **lungo** che sia un prefisso del valore, sui confini del punto.
4182
+ * Il riferimento dichiarato piu' **lungo** che sia un prefisso del valore, e la tappa da cui
4183
+ * navigare il resto. La regola sta in `core/reference-path` perche' non e' solo del picker: gli
4184
+ * inspector ne ricavano il **tipo** dell'ultimo segmento con la stessa catena, e due copie di
4185
+ * «prefisso piu' lungo» divergerebbero (§4.1, §4.7.1).
3945
4186
  *
3946
- * Non basta il primo segmento: un nome dichiarato puo' contenere il punto. Le globali sono il caso
3947
- * per cui la regola esiste — di `$User.Anagrafica.Citta` il dizionario dichiara `$User.Anagrafica`,
3948
- * e cercare la corrispondenza esatta di tutto cio' che segue lo scope segnalerebbe come rotto un
3949
- * percorso giusto (§4.1: vince il prefisso dichiarato piu' lungo).
4187
+ * Fra **tutte** le radici, non fra quelle filtrate: la radice di `Richiesta.Ragione` in un campo
4188
+ * `String` sta solo nell'elenco non filtrato.
3950
4189
  */
3951
- declaredPrefix = computed(() => {
3952
- const value = (this.value() ?? '')?.trim();
3953
- if (!value) {
3954
- return undefined;
3955
- }
3956
- let best;
3957
- for (const candidate of this.roots()) {
3958
- if (value !== candidate.name && !value.startsWith(`${candidate.name}.`)) {
3959
- continue;
3960
- }
3961
- if (!best || candidate.name.length > best.name.length) {
3962
- best = candidate;
3963
- }
3964
- }
3965
- return best;
3966
- }, ...(ngDevMode ? [{ debugName: "declaredPrefix" }] : []));
3967
- /**
3968
- * La risorsa da cui il valore corrente sta navigando, quando la sua forma e' **dichiarata**: una
3969
- * `Structure` con la classe, un record con l'entita'. È cio' che rende il percorso verificabile.
3970
- *
3971
- * Un output automatico e' escluso di proposito: la primitiva ne dichiara il tipo, ma la
3972
- * specifica tiene i percorsi che ne partono fuori dalla validazione (§7, ultimo capoverso), e
3973
- * l'editor non deve accusare cio' che il backend non verifica.
3974
- */
3975
- navigableRoot = computed(() => {
3976
- // Fra tutte le radici: quella di `Richiesta.Ragione` in un campo `String` sta solo nell'elenco
3977
- // non filtrato.
3978
- const reference = this.declaredPrefix();
3979
- if (!reference || reference.kind === 'ElementOutput' || !reference.objectType || reference.isCollection) {
3980
- return undefined;
3981
- }
3982
- if (this.dictionaries.isStructure(reference.dataType)) {
3983
- return { reference, container: { kind: 'structure', name: reference.objectType } };
3984
- }
3985
- return reference.dataType === 'Object'
3986
- ? { reference, container: { kind: 'object', name: reference.objectType } }
3987
- : undefined;
3988
- }, ...(ngDevMode ? [{ debugName: "navigableRoot" }] : []));
4190
+ declaredPrefix = computed(() => declaredPrefixOf(this.roots(), this.value()), ...(ngDevMode ? [{ debugName: "declaredPrefix" }] : []));
4191
+ navigableRoot = computed(() => navigableRootOf(this.roots(), this.value(), (dataType) => this.dictionaries.isStructure(dataType)), ...(ngDevMode ? [{ debugName: "navigableRoot" }] : []));
3989
4192
  /** Il percorso dopo la radice: `Cliente.Email` di `Richiesta.Cliente.Email`. */
3990
- navigatedPath = computed(() => {
3991
- const root = this.navigableRoot()?.reference.name;
3992
- const value = (this.value() ?? '')?.trim();
3993
- return root && value.length > root.length ? value.slice(root.length + 1) : '';
3994
- }, ...(ngDevMode ? [{ debugName: "navigatedPath" }] : []));
4193
+ navigatedPath = computed(() => this.navigableRoot()?.path ?? '', ...(ngDevMode ? [{ debugName: "navigatedPath" }] : []));
3995
4194
  navigation = navigatePath(this.catalog, () => {
3996
4195
  const root = this.navigableRoot();
3997
4196
  return root ? { root: root.container, path: this.navigatedPath() } : null;
@@ -5505,6 +5704,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
5505
5704
  */
5506
5705
  class ConditionEditorComponent {
5507
5706
  dictionaries = inject(FlowDictionaryStore);
5707
+ catalog = inject(FlowCatalogStore);
5508
5708
  api = inject(FlowBuilderApi);
5509
5709
  store = inject(FlowDocumentStore);
5510
5710
  /** Il contenitore: porta `conditions`, `conditionLogic` e, se serve, `formula`. */
@@ -5529,6 +5729,20 @@ class ConditionEditorComponent {
5529
5729
  * fra testo e numero prima che lo dica la validazione (§4.3).
5530
5730
  */
5531
5731
  references = signal([], ...(ngDevMode ? [{ debugName: "references" }] : []));
5732
+ /**
5733
+ * Il tipo dei due lati, risorsa o **percorso**. L'elenco porta le radici e non i percorsi (§6.4):
5734
+ * su `Richiesta.Stato` la corrispondenza esatta non trova niente, e il tipo si ricava navigando la
5735
+ * classe (§4.7.1). È cio' che fa comparire l'elenco dei valori su un membro `Enum` (§4.6) e che
5736
+ * rende visibile un confronto fra tipi incompatibili anche dentro un'istanza (§4.3).
5737
+ */
5738
+ types = referenceTypes(this.catalog, (dataType) => this.dictionaries.isStructure(dataType), () => ({
5739
+ references: this.references(),
5740
+ // Entrambi i lati: il destro serve a `rightDataType` quando e' un riferimento.
5741
+ values: this.conditions().flatMap((condition) => [
5742
+ condition.leftValueReference,
5743
+ condition.rightValue?.elementReference,
5744
+ ]),
5745
+ }));
5532
5746
  constructor() {
5533
5747
  effect(() => {
5534
5748
  const definition = this.store.document();
@@ -5544,12 +5758,9 @@ class ConditionEditorComponent {
5544
5758
  .catch(() => this.references.set([]));
5545
5759
  });
5546
5760
  }
5547
- /** Il tipo di un riferimento, `undefined` se navigato o ignoto: lì non si segnala (§4.3). */
5761
+ /** Il tipo di un riferimento, `undefined` dove non si sa: lì non si segnala (§4.3). */
5548
5762
  typeOfReference(reference) {
5549
- if (!reference) {
5550
- return undefined;
5551
- }
5552
- return this.references().find((entry) => entry.name === reference)?.dataType ?? undefined;
5763
+ return this.types.dataTypeOf(reference);
5553
5764
  }
5554
5765
  /** Il tipo del lato sinistro: guida il secondo operando. */
5555
5766
  leftDataType(condition) {
@@ -5557,15 +5768,11 @@ class ConditionEditorComponent {
5557
5768
  }
5558
5769
  /**
5559
5770
  * Il tipo concreto del lato sinistro quando e' un `Enum`: senza di questo il valore di confronto
5560
- * resterebbe una casella di testo su un insieme chiuso (§4.6). Su un percorso navigato
5561
- * (`Cliente.Stato`) il riferimento non e' in elenco e resta `undefined`: si digita, come prima.
5771
+ * resterebbe una casella di testo su un insieme chiuso (§4.6). Vale anche su un percorso navigato
5772
+ * (`Richiesta.Stato`, `$Record.Stato`): il tipo del membro o del campo lo ricava {@link types}.
5562
5773
  */
5563
5774
  leftObjectType(condition) {
5564
- const reference = condition.leftValueReference;
5565
- if (!reference) {
5566
- return undefined;
5567
- }
5568
- return this.references().find((entry) => entry.name === reference)?.objectType ?? undefined;
5775
+ return this.types.objectTypeOf(condition.leftValueReference);
5569
5776
  }
5570
5777
  /** Il tipo del lato destro, dedotto dal campo valorizzato o dal riferimento scelto. */
5571
5778
  rightDataType(condition) {
@@ -5815,8 +6022,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
5815
6022
  *
5816
6023
  * - I campi proposti passano da `GET /schema/{object}/fields?usage=filterable`, così un
5817
6024
  * campo non filtrabile non arriva nemmeno alla validazione (`FIELD_NOT_FILTERABLE`).
5818
- * La scrittura libera resta possibile, perche' i percorsi di relazione
5819
- * (`Cliente.Citta`) non sono enumerabili e la validazione non li verifica.
6025
+ * La scrittura libera resta possibile un catalogo incompleto non deve bloccare il campo — ma i
6026
+ * percorsi di relazione (`Cliente.Citta`) **si navigano**: il tipo dell'ultimo segmento si ricava
6027
+ * con `core/reference-path`, ed e' cio' che fa comparire l'elenco dei valori su un `Enum` (§4.4).
5820
6028
  */
5821
6029
  class RecordFilterEditorComponent {
5822
6030
  catalog = inject(FlowCatalogStore);
@@ -5838,25 +6046,25 @@ class RecordFilterEditorComponent {
5838
6046
  emptyWarning = input(null, ...(ngDevMode ? [{ debugName: "emptyWarning" }] : []));
5839
6047
  emptyWarningSeverity = input('warn', ...(ngDevMode ? [{ debugName: "emptyWarningSeverity" }] : []));
5840
6048
  changed = output();
5841
- fields = signal([], ...(ngDevMode ? [{ debugName: "fields" }] : []));
5842
6049
  /** Tutti i campi, filtrabili o no: serve solo a riconoscere le chiavi composte (§5.7). */
5843
6050
  allFields = signal([], ...(ngDevMode ? [{ debugName: "allFields" }] : []));
5844
6051
  filters = computed(() => this.holder().filters ?? [], ...(ngDevMode ? [{ debugName: "filters" }] : []));
5845
6052
  operators = computed(() => this.dictionaries.recordFilterOperators(), ...(ngDevMode ? [{ debugName: "operators" }] : []));
6053
+ /**
6054
+ * Il tipo del campo filtrato, **compresi i percorsi di relazione**: `Cliente.Citta` non e' nella
6055
+ * lista piatta dei campi dell'entita' e il suo tipo si ricava navigando la catena (§4.4). Senza,
6056
+ * un campo `Enum` raggiunto per relazione tornava una casella di testo su un insieme chiuso (§4.6).
6057
+ *
6058
+ * L'`usage` e' quello del campo — `filterable` fra i filtri — e vale per l'**ultimo** segmento: i
6059
+ * segmenti intermedi li chiede `resolvePath` con `any`, perche' l'uso riguarda il campo confrontato
6060
+ * e non la relazione che porta a lui.
6061
+ */
6062
+ types = pathTypes(this.catalog, () => ({
6063
+ container: this.object() ? { kind: 'object', name: this.object() } : null,
6064
+ paths: this.filters().map((filter) => filter.field),
6065
+ usage: this.usage(),
6066
+ }));
5846
6067
  constructor() {
5847
- effect(() => {
5848
- const object = this.object();
5849
- const usage = this.usage();
5850
- if (!object) {
5851
- this.fields.set([]);
5852
- return;
5853
- }
5854
- void this.catalog
5855
- .listFields(object, usage)
5856
- .then((list) => this.fields.set(list ?? []))
5857
- // Catalogo non disponibile: il campo resta scrivibile a mano.
5858
- .catch(() => this.fields.set([]));
5859
- });
5860
6068
  effect(() => {
5861
6069
  const object = this.object();
5862
6070
  if (!object) {
@@ -5996,18 +6204,17 @@ class RecordFilterEditorComponent {
5996
6204
  }
5997
6205
  /**
5998
6206
  * Il tipo del campo, per proporre il controllo giusto sul valore: e' il motivo per cui il
5999
- * catalogo resta caricato qui, e non solo dentro il picker.
6207
+ * catalogo resta interrogato qui, e non solo dentro il picker.
6000
6208
  */
6001
6209
  fieldDataType(filter) {
6002
- return this.fields().find((field) => field.name === filter.field)?.dataType;
6210
+ return this.types.dataTypeOf(filter.field);
6003
6211
  }
6004
6212
  /**
6005
6213
  * Il tipo concreto del campo: su un campo `Enum` e' cio' che permette di proporre i valori
6006
- * ammessi invece di farli digitare (§4.6). Su un percorso di relazione il campo non e' in elenco
6007
- * e resta `undefined`, come per il tipo.
6214
+ * ammessi invece di farli digitare (§4.6).
6008
6215
  */
6009
6216
  fieldObjectType(filter) {
6010
- return this.fields().find((field) => field.name === filter.field)?.objectType ?? undefined;
6217
+ return this.types.objectTypeOf(filter.field);
6011
6218
  }
6012
6219
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordFilterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6013
6220
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordFilterEditorComponent, isStandalone: true, selector: "fb-record-filter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, supportsLogic: { classPropertyName: "supportsLogic", publicName: "supportsLogic", isSignal: true, isRequired: false, transformFunction: null }, supportsFormula: { classPropertyName: "supportsFormula", publicName: "supportsFormula", isSignal: true, isRequired: false, transformFunction: null }, emptyWarning: { classPropertyName: "emptyWarning", publicName: "emptyWarning", isSignal: true, isRequired: false, transformFunction: null }, emptyWarningSeverity: { classPropertyName: "emptyWarningSeverity", publicName: "emptyWarningSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object()) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"holder().filterFormula || ''\"\r\n (input)=\"setFormula($any($event.target).value)\"\r\n ></textarea>\r\n <p class=\"fb-field__hint\">Valutata dal motore di regole, in alternativa ai filtri.</p>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{display:flex;flex-wrap:wrap;gap:3px;margin-bottom:8px}.fb-filter__mode{padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-filter__mode:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-filter__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -6555,12 +6762,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
6555
6762
  class AssignmentInspectorComponent extends NodeInspectorBase {
6556
6763
  elementType = 'Assignment';
6557
6764
  api = inject(FlowBuilderApi);
6765
+ catalog = inject(FlowCatalogStore);
6558
6766
  /**
6559
6767
  * I riferimenti **scrivibili**, per conoscere il tipo della destinazione. Non e' un doppione del
6560
6768
  * picker: serve il tipo, ed e' cio' che permette all'editor del valore di mostrare il controllo
6561
6769
  * giusto — e su un `Enum` di proporre i valori del tipo invece di farli digitare (§4.6).
6562
6770
  */
6563
6771
  writableReferences = signal([], ...(ngDevMode ? [{ debugName: "writableReferences" }] : []));
6772
+ /**
6773
+ * Il tipo della destinazione, risorsa o **percorso**. L'elenco porta le radici e non i percorsi
6774
+ * (§6.4): su `Richiesta.Stato` la corrispondenza esatta non trova niente, e il tipo si ricava
6775
+ * navigando la classe — altrimenti un membro `Enum` resterebbe una casella di testo (§4.6, §4.7.1).
6776
+ *
6777
+ * `usage` resta quello di default (`any`): un campo di `$Record` si assegna se e' createable
6778
+ * **oppure** updateable, e qui il contesto non dice quale dei due (§5.7).
6779
+ */
6780
+ targetTypes = referenceTypes(this.catalog, (dataType) => this.dictionaries.isStructure(dataType), () => ({
6781
+ references: this.writableReferences(),
6782
+ values: this.items().map((item) => item.assignToReference),
6783
+ }));
6564
6784
  constructor() {
6565
6785
  super();
6566
6786
  effect(() => {
@@ -6576,22 +6796,11 @@ class AssignmentInspectorComponent extends NodeInspectorBase {
6576
6796
  .catch(() => this.writableReferences.set([]));
6577
6797
  });
6578
6798
  }
6579
- /**
6580
- * Il tipo della destinazione, solo per corrispondenza **esatta** del nome: un percorso navigato
6581
- * (`Cliente.Stato`) non e' in elenco, e lì il tipo resta ignoto invece di essere indovinato.
6582
- */
6583
- targetOf(item) {
6584
- const reference = item.assignToReference;
6585
- if (!reference) {
6586
- return undefined;
6587
- }
6588
- return this.writableReferences().find((entry) => entry.name === reference);
6589
- }
6590
6799
  targetDataType(item) {
6591
- return this.targetOf(item)?.dataType ?? undefined;
6800
+ return this.targetTypes.dataTypeOf(item.assignToReference);
6592
6801
  }
6593
6802
  targetObjectType(item) {
6594
- return this.targetOf(item)?.objectType ?? undefined;
6803
+ return this.targetTypes.objectTypeOf(item.assignToReference);
6595
6804
  }
6596
6805
  assignment = computed(() => this.node(), ...(ngDevMode ? [{ debugName: "assignment" }] : []));
6597
6806
  items = computed(() => this.assignment().assignmentItems ?? [], ...(ngDevMode ? [{ debugName: "items" }] : []));
@@ -6679,11 +6888,11 @@ class AssignmentInspectorComponent extends NodeInspectorBase {
6679
6888
  return !operator.startsWith('Remove') || operator === 'RemovePosition' || operator === 'RemoveUncommon';
6680
6889
  }
6681
6890
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: AssignmentInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6682
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"$any(targetDataType(item))\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6891
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Vale anche dentro una classe \u2014 \u00ABRichiesta.Stato\u00BB \u2014 perche'\r\n il tipo si ricava navigando i membri (\u00A74.7.1). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"targetDataType(item)\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6683
6892
  }
6684
6893
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: AssignmentInspectorComponent, decorators: [{
6685
6894
  type: Component,
6686
- args: [{ selector: 'fb-assignment-inspector', standalone: true, imports: [ConnectorEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"$any(targetDataType(item))\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
6895
+ args: [{ selector: 'fb-assignment-inspector', standalone: true, imports: [ConnectorEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveItem($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi l\u2019operazione\"\r\n (click)=\"removeItem($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <!--\r\n Il tipo della destinazione guida il controllo: su un Enum i valori proponibili sono\r\n quelli del suo tipo, e farli digitare e' il modo piu' facile di scriverne uno che il\r\n runtime non riconosce (\u00A74.6). Vale anche dentro una classe \u2014 \u00ABRichiesta.Stato\u00BB \u2014 perche'\r\n il tipo si ricava navigando i membri (\u00A74.7.1). Ignoto il tipo, il campo resta generico.\r\n -->\r\n <fb-value-editor\r\n [value]=\"item.value\"\r\n label=\"Valore\"\r\n [dataType]=\"targetDataType(item)\"\r\n [objectType]=\"targetObjectType(item)\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
6687
6896
  }], ctorParameters: () => [] });
6688
6897
 
6689
6898
  /**