@esfaenza/dpe-builder 20.3.5 → 20.3.6
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/fesm2022/esfaenza-dpe-builder.mjs +135 -10
- package/fesm2022/esfaenza-dpe-builder.mjs.map +1 -1
- package/index.d.ts +85 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { throwError, defer, catchError, tap, EMPTY, switchMap, of, debounceTime } from 'rxjs';
|
|
1
|
+
import { throwError, defer, catchError, map, tap, EMPTY, switchMap, of, debounceTime } from 'rxjs';
|
|
2
2
|
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { InjectionToken, inject, Injectable, makeEnvironmentProviders, signal, computed, DestroyRef, input, ChangeDetectionStrategy, Component, ElementRef, output, viewChild, effect, Directive, untracked, Injector, afterNextRender } from '@angular/core';
|
|
@@ -255,6 +255,27 @@ class HttpDpeBuilderApi extends DpeBuilderApi {
|
|
|
255
255
|
segment(value) {
|
|
256
256
|
return encodeURIComponent(value);
|
|
257
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* La chiave che finisce in un segmento di rotta, **verificata prima di partire**.
|
|
260
|
+
*
|
|
261
|
+
* Perche' esiste: `encodeURIComponent(undefined)` vale `'undefined'`, e senza questo controllo un
|
|
262
|
+
* nome mancante non produceva nessun errore — produceva `PUT /definitions/undefined`, cioe' una
|
|
263
|
+
* richiesta sensata per il trasporto e priva di senso per il dominio. La sbaglia il backend
|
|
264
|
+
* (`404`, o peggio: sovrascrive una definizione che si chiama davvero cosi'), e chi guarda la rete
|
|
265
|
+
* vede una chiave inventata dal client senza sapere da dove arrivi.
|
|
266
|
+
*
|
|
267
|
+
* Rifiuta **senza emettere la richiesta**: una chiave assente non si ritenta e non riguarda il
|
|
268
|
+
* backend. La categoria e' `Backend` e non `MissingService` — il comando c'e' e non va disabilitato
|
|
269
|
+
* — ne' `Transport`, che significherebbe «ritenta».
|
|
270
|
+
*/
|
|
271
|
+
requireKey(value, primitive, what) {
|
|
272
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
273
|
+
throw new DpeApiError('Backend', `${primitive}: ${what} manca, e finirebbe nell'URL come "${String(value)}". ` +
|
|
274
|
+
'Se il documento arriva dal backend, verifica che la risposta sia in camelCase: ' +
|
|
275
|
+
'`Name` invece di `name` non produce nessun errore, produce un campo `undefined`.');
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
258
279
|
/**
|
|
259
280
|
* Cio' che avvolge **ogni** chiamata, e l'unico posto in cui si decide se una chiamata parte.
|
|
260
281
|
*
|
|
@@ -301,6 +322,15 @@ class HttpDpeBuilderApi extends DpeBuilderApi {
|
|
|
301
322
|
* nomina la primitiva — e serve solo all'umano: l'editor guarda la categoria.
|
|
302
323
|
*/
|
|
303
324
|
fail(error, primitive) {
|
|
325
|
+
/*
|
|
326
|
+
* Un `DpeApiError` passa **intatto**: l'ha classificato chi l'ha alzato (la chiave mancante, una
|
|
327
|
+
* risposta che non e' una definizione), e ri-tradurlo qui lo farebbe cadere sul ramo «non e' una
|
|
328
|
+
* `HttpErrorResponse`», cioe' `Transport` — che significa «ritenta» e sarebbe la reazione
|
|
329
|
+
* sbagliata a un contratto rotto.
|
|
330
|
+
*/
|
|
331
|
+
if (DpeApiError.is(error)) {
|
|
332
|
+
return throwError(() => error);
|
|
333
|
+
}
|
|
304
334
|
const response = error instanceof HttpErrorResponse ? error : undefined;
|
|
305
335
|
let category = 'Backend';
|
|
306
336
|
if (response?.status === 501) {
|
|
@@ -338,7 +368,7 @@ class HttpDpeBuilderApi extends DpeBuilderApi {
|
|
|
338
368
|
return this.request('getTables', () => this.httpGet('/tables'));
|
|
339
369
|
}
|
|
340
370
|
getTable(tableName) {
|
|
341
|
-
return this.request('getTable', () => this.httpGet(`/tables/${this.segment(tableName)}`));
|
|
371
|
+
return this.request('getTable', () => this.httpGet(`/tables/${this.segment(this.requireKey(tableName, 'getTable', 'il nome della tabella'))}`));
|
|
342
372
|
}
|
|
343
373
|
// --- primitive opzionali --------------------------------------------------
|
|
344
374
|
/** L'**unica** rotta con involucro: il corpo porta anche i valori dei parametri (§7-bis). */
|
|
@@ -346,10 +376,46 @@ class HttpDpeBuilderApi extends DpeBuilderApi {
|
|
|
346
376
|
return this.request('compilePreview', () => this.httpPost('/compile', { definition, parameterValues }));
|
|
347
377
|
}
|
|
348
378
|
load(name) {
|
|
349
|
-
return this.request('load', () => this.httpGet(`/definitions/${this.segment(name)}`));
|
|
379
|
+
return this.request('load', () => this.httpGet(`/definitions/${this.segment(this.requireKey(name, 'load', 'il nome della definizione'))}`).pipe(map((payload) => this.requireDefinition(payload))));
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* La risposta di `load` **e' una definizione**, e qui si verifica invece di sperarlo.
|
|
383
|
+
*
|
|
384
|
+
* Costa quattro righe e trasforma il difetto peggiore di questo contratto in un errore immediato
|
|
385
|
+
* che nomina la causa. Senza, una risposta della forma sbagliata non produce nessun errore:
|
|
386
|
+
* produce un editor con il canvas vuoto e un `name` `undefined`, che si manifesta molto piu' tardi
|
|
387
|
+
* come un salvataggio verso una chiave inventata. I tre modi in cui succede davvero:
|
|
388
|
+
*
|
|
389
|
+
* - il backend restituisce **l'involucro della lettura** (`{ definition, diagnostics, hasErrors }`)
|
|
390
|
+
* invece del documento: e' l'esito di `IDpeService.Deserialize`, ed e' un errore facile da fare
|
|
391
|
+
* scrivendo `Ok(read)` al posto di `Ok(documento)`;
|
|
392
|
+
* - la risposta e' in **PascalCase**: `Name` non e' `name`, e in TypeScript non e' un errore;
|
|
393
|
+
* - la rotta risponde un **sommario** (`DpeDefinitionSummary`) invece della definizione: si
|
|
394
|
+
* somigliano, e il sommario ha `name` ma non `formatVersion` ne' i nodi.
|
|
395
|
+
*
|
|
396
|
+
* Si controllano le due proprieta' **obbligatorie** del documento e nient'altro: una definizione
|
|
397
|
+
* puo' legittimamente non avere nessun nodo, e verificare di piu' vorrebbe dire validare, che e'
|
|
398
|
+
* del backend.
|
|
399
|
+
*/
|
|
400
|
+
requireDefinition(payload) {
|
|
401
|
+
const documento = payload;
|
|
402
|
+
const haNome = typeof documento?.name === 'string' && documento.name.trim().length > 0;
|
|
403
|
+
const haVersione = typeof documento?.formatVersion === 'number';
|
|
404
|
+
if (!haNome || !haVersione) {
|
|
405
|
+
throw new DpeApiError('Backend', "load: la risposta non e' una definizione (mancano `name` e/o `formatVersion`). " +
|
|
406
|
+
"Le tre cause reali: il backend restituisce l'involucro di lettura " +
|
|
407
|
+
"(`{ definition, diagnostics }`) invece del documento; risponde in PascalCase " +
|
|
408
|
+
"(`Name` non e' `name`); risponde un sommario invece della definizione.", payload);
|
|
409
|
+
}
|
|
410
|
+
return payload;
|
|
350
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Il nome viene dal **documento** e non da un parametro: e' la chiave, e la rotta e'
|
|
414
|
+
* `PUT /definitions/{definition.name}`. Se manca, la chiamata si rifiuta qui — un salvataggio
|
|
415
|
+
* senza chiave non e' un salvataggio, e una definizione nuova si crea con `createDefinition`.
|
|
416
|
+
*/
|
|
351
417
|
save(definition) {
|
|
352
|
-
return this.request('save', () => this.httpPut(`/definitions/${this.segment(definition
|
|
418
|
+
return this.request('save', () => this.httpPut(`/definitions/${this.segment(this.requireKey(definition?.name, 'save', 'il nome della definizione'))}`, definition));
|
|
353
419
|
}
|
|
354
420
|
/**
|
|
355
421
|
* La dichiarazione dell'ambiente (ADR 0017). Questo client la implementa perche' conosce
|
|
@@ -395,7 +461,7 @@ class HttpDpeBuilderApi extends DpeBuilderApi {
|
|
|
395
461
|
return this.request('createDefinition', () => this.httpPost('/definitions', definition));
|
|
396
462
|
}
|
|
397
463
|
deleteDefinition(name) {
|
|
398
|
-
return this.request('deleteDefinition', () => this.httpDelete(`/definitions/${this.segment(name)}`));
|
|
464
|
+
return this.request('deleteDefinition', () => this.httpDelete(`/definitions/${this.segment(this.requireKey(name, 'deleteDefinition', 'il nome della definizione'))}`));
|
|
399
465
|
}
|
|
400
466
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: HttpDpeBuilderApi, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
401
467
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: HttpDpeBuilderApi });
|
|
@@ -1520,7 +1586,15 @@ class DpeDocumentStore {
|
|
|
1520
1586
|
// -------------------------------------------------------------------------
|
|
1521
1587
|
// Mutazioni — sincrone e granulari
|
|
1522
1588
|
// -------------------------------------------------------------------------
|
|
1523
|
-
/**
|
|
1589
|
+
/**
|
|
1590
|
+
* Proprieta' della radice: etichetta, descrizione, dialetto, parametri.
|
|
1591
|
+
*
|
|
1592
|
+
* **`name` e `formatVersion` non sono nell'insieme**, e il tipo lo impedisce invece di dirlo in un
|
|
1593
|
+
* commento. `name` e' la chiave del catalogo: rinominare e' creare-e-cancellare (ADR 0019), non
|
|
1594
|
+
* una modifica del documento — e `Partial<DpeDefinition>` permetteva `{ name: undefined }`, che
|
|
1595
|
+
* lasciava nel documento una chiave senza valore. Da li' il salvataggio partiva verso
|
|
1596
|
+
* `PUT /definitions/undefined`. `formatVersion` e' del formato, non dell'autore.
|
|
1597
|
+
*/
|
|
1524
1598
|
patchDefinition(patch) {
|
|
1525
1599
|
this.commit({ ...this._definition(), ...patch });
|
|
1526
1600
|
}
|
|
@@ -1884,7 +1958,21 @@ class DpeCatalogStore {
|
|
|
1884
1958
|
*/
|
|
1885
1959
|
mutate(source) {
|
|
1886
1960
|
this._error.set(null);
|
|
1887
|
-
return source.pipe(
|
|
1961
|
+
return source.pipe(
|
|
1962
|
+
/*
|
|
1963
|
+
* La risposta di una scrittura **e' un sommario**, e chi crea apre il nome che c'e' dentro
|
|
1964
|
+
* (puo' essere stato normalizzato). Se il nome non c'e', l'apertura partirebbe con
|
|
1965
|
+
* `undefined` e il difetto ricomparirebbe due schermate dopo: qui diventa subito un errore
|
|
1966
|
+
* nel banner del catalogo, che nomina la causa.
|
|
1967
|
+
*/
|
|
1968
|
+
map((summary) => {
|
|
1969
|
+
if (typeof summary?.name !== 'string' || summary.name.trim().length === 0) {
|
|
1970
|
+
throw new DpeApiError('Backend', "La scrittura non ha restituito un sommario con `name`: senza il nome non c'e' niente " +
|
|
1971
|
+
'da aprire. Un backend che risponde `204`, che restituisce il documento invece del ' +
|
|
1972
|
+
'sommario, o che risponde in PascalCase produce esattamente questo.', summary);
|
|
1973
|
+
}
|
|
1974
|
+
return summary;
|
|
1975
|
+
}), tap({
|
|
1888
1976
|
subscribe: () => this._isMutating.set(true),
|
|
1889
1977
|
next: () => {
|
|
1890
1978
|
this._isMutating.set(false);
|
|
@@ -8522,7 +8610,44 @@ class DpeBuilderComponent {
|
|
|
8522
8610
|
const declared = this.validation.environmentDialect();
|
|
8523
8611
|
return declared !== undefined && declared !== this.dialect() ? declared : undefined;
|
|
8524
8612
|
}, ...(ngDevMode ? [{ debugName: "dialectMismatch" }] : []));
|
|
8525
|
-
|
|
8613
|
+
/**
|
|
8614
|
+
* Il documento porta una chiave utilizzabile. Il nome **e'** la chiave del catalogo, e la rotta di
|
|
8615
|
+
* aggiornamento e' `PUT /definitions/{name}`: senza nome non c'e' niente da aggiornare, e una
|
|
8616
|
+
* definizione nuova si crea dal catalogo (ADR 0019), non da qui.
|
|
8617
|
+
*
|
|
8618
|
+
* Sta qui e non solo nel client per la ragione che questo progetto applica alle primitive
|
|
8619
|
+
* assenti: un comando disabilitato che dice il motivo e' meglio di un comando attivo che fallisce
|
|
8620
|
+
* alla pressione. Il client rifiuta comunque — e' lui che compone l'URL, e un ospite che chiami
|
|
8621
|
+
* `save()` per vie proprie deve trovare la stessa risposta.
|
|
8622
|
+
*/
|
|
8623
|
+
hasKey = computed(() => (this.store.definition().name ?? '').trim().length > 0, ...(ngDevMode ? [{ debugName: "hasKey" }] : []));
|
|
8624
|
+
/**
|
|
8625
|
+
* Il documento ricevuto **non e' una definizione**, detto subito e a schermo.
|
|
8626
|
+
*
|
|
8627
|
+
* L'unico ingresso e' l'input `definition`, e finora l'editor accettava qualunque oggetto: con un
|
|
8628
|
+
* documento della forma sbagliata mostrava un canvas vuoto — che sembra una definizione senza
|
|
8629
|
+
* nodi, cioe' uno stato legittimo — e il difetto ricompariva molto piu' tardi come un salvataggio
|
|
8630
|
+
* verso una chiave che il client doveva inventare. Le tre forme che arrivano davvero al posto del
|
|
8631
|
+
* documento sono l'involucro di lettura (`{ definition, diagnostics }`), il **sommario** del
|
|
8632
|
+
* catalogo (ha `name` ma non `formatVersion` ne' i nodi) e una risposta in PascalCase.
|
|
8633
|
+
*
|
|
8634
|
+
* Si controllano le due proprieta' obbligatorie e nient'altro: una definizione senza nodi e'
|
|
8635
|
+
* legittima, e verificare di piu' sarebbe validare — che e' del backend (I6).
|
|
8636
|
+
*/
|
|
8637
|
+
documentProblem = computed(() => {
|
|
8638
|
+
const document = this.store.definition();
|
|
8639
|
+
if (typeof document?.formatVersion !== 'number') {
|
|
8640
|
+
return 'manca `formatVersion`';
|
|
8641
|
+
}
|
|
8642
|
+
if (!this.hasKey()) {
|
|
8643
|
+
return "manca `name`, che e' la chiave del catalogo";
|
|
8644
|
+
}
|
|
8645
|
+
return undefined;
|
|
8646
|
+
}, ...(ngDevMode ? [{ debugName: "documentProblem" }] : []));
|
|
8647
|
+
canSave = computed(() => this.validation.isSaveAvailable() &&
|
|
8648
|
+
!this.validation.isSaving() &&
|
|
8649
|
+
this.store.isDirty() &&
|
|
8650
|
+
this.hasKey(), ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
8526
8651
|
nodeCount = computed(() => dpeAllNodes(this.store.definition()).length, ...(ngDevMode ? [{ debugName: "nodeCount" }] : []));
|
|
8527
8652
|
constructor() {
|
|
8528
8653
|
// Caricamento: si reagisce all'**identita'** dell'input, non al suo contenuto. Lo store
|
|
@@ -8637,7 +8762,7 @@ class DpeBuilderComponent {
|
|
|
8637
8762
|
this.store.markSaved();
|
|
8638
8763
|
}
|
|
8639
8764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8640
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeBuilderComponent, isStandalone: true, selector: "dpe-builder", inputs: { definition: { classPropertyName: "definition", publicName: "definition", isSignal: true, isRequired: true, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { definitionChange: "definitionChange" }, host: { listeners: { "keydown": "onKeyDown($event)" } }, providers: [DpeDocumentStore, DpeLayoutService, DpeValidationService, DpeMetadataStore], viewQueries: [{ propertyName: "canvas", first: true, predicate: DpeCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\r\n `.dpe-builder` porta le variabili del tema: e\u2019 la classe radice su cui l\u2019app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all\u2019ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a \u00ABnessuna modifica\u00BB.\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall\u2019ultimo salvataggio'\r\n : 'Il documento coincide con l\u2019ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n \u25CF modificata\r\n } @else {\r\n \u25CB nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e\u2019 dichiarato dall\u2019ambiente, non scelto\r\n qui. Mostra quello della definizione, che e\u2019 quello con cui il backend compilera\u2019 (ADR\r\n 0009). Il titolo dice le due cose che contano \u2014 chi lo decide, e che le espressioni sono\r\n SQL nativo di quel dialetto (ADR 0006).\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e\u2019 dichiarato dall\u2019ambiente e non si modifica dall\u2019editor. Le espressioni sono SQL nativo di questo dialetto: la definizione gli appartiene.\"\r\n >\r\n Dialetto \u00B7 <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e\u2019 una conseguenza dell\u2019immutabilita\u2019 dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n \u21B6 Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n \u21B7 Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n \u2317 Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E\u2019 il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice \u00ABquesto editor non salva\u00BB; un\r\n bottone disabilitato con il motivo dice \u00ABqui non si salva, ed ecco perche\u2019\u00BB \u2014 ed e\u2019 la\r\n verita\u2019 quando l\u2019host non espone `save()`. La disponibilita\u2019 si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l\u2019host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n \u2913 Salvataggio\u2026\r\n } @else {\r\n \u2913 Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e\u2019 \u00ABnessuna modifica\u00BB nell\u2019indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">\u2713 Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso\u2026</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire \u00ABva tutto bene\u00BB\r\n quando non lo sappiamo piu\u2019 e\u2019 peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell\u2019ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: correggere significherebbe migrare le\r\n espressioni SQL native, ed e\u2019 dell\u2019host. Non e\u2019 una regola di dominio duplicata \u2014 non giudica\r\n la definizione, riporta il disaccordo fra due fatti che l\u2019ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L\u2019ambiente dichiara {{ dialectLabels[declared] }}, la definizione e\u2019 scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera\u2019 con il dialetto <strong>della definizione</strong>. Allinearli e\u2019 una\r\n migrazione delle espressioni SQL native e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e\u2019 peggio di nessun salvataggio,\r\n perche\u2019 l\u2019indicatore \u00ABmodificata\u00BB sparirebbe per un\u2019operazione che non e\u2019 avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n />\r\n\r\n <dpe-node-inspector class=\"dpe-shell__inspector\" (layoutRequested)=\"runAutoLayout()\" />\r\n </div>\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "component", type: DpeCanvasComponent, selector: "dpe-canvas", inputs: ["isEditable"], outputs: ["nodeOpened", "nodeDropped"] }, { kind: "component", type: DpeDiagnosticsPanelComponent, selector: "dpe-diagnostics-panel", outputs: ["navigate"] }, { kind: "component", type: DpeNodeInspectorComponent, selector: "dpe-node-inspector", outputs: ["layoutRequested"] }, { kind: "component", type: DpeNodePaletteComponent, selector: "dpe-node-palette", outputs: ["nodeRequested"] }, { kind: "component", type: DpeSchemaPanelComponent, selector: "dpe-schema-panel" }, { kind: "component", type: DpeSqlPreviewComponent, selector: "dpe-sql-preview" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8765
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeBuilderComponent, isStandalone: true, selector: "dpe-builder", inputs: { definition: { classPropertyName: "definition", publicName: "definition", isSignal: true, isRequired: true, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { definitionChange: "definitionChange" }, host: { listeners: { "keydown": "onKeyDown($event)" } }, providers: [DpeDocumentStore, DpeLayoutService, DpeValidationService, DpeMetadataStore], viewQueries: [{ propertyName: "canvas", first: true, predicate: DpeCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\r\n `.dpe-builder` porta le variabili del tema: e\u2019 la classe radice su cui l\u2019app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all\u2019ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a \u00ABnessuna modifica\u00BB.\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall\u2019ultimo salvataggio'\r\n : 'Il documento coincide con l\u2019ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n \u25CF modificata\r\n } @else {\r\n \u25CB nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e\u2019 dichiarato dall\u2019ambiente, non scelto\r\n qui. Mostra quello della definizione, che e\u2019 quello con cui il backend compilera\u2019 (ADR\r\n 0009). Il titolo dice le due cose che contano \u2014 chi lo decide, e che le espressioni sono\r\n SQL nativo di quel dialetto (ADR 0006).\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e\u2019 dichiarato dall\u2019ambiente e non si modifica dall\u2019editor. Le espressioni sono SQL nativo di questo dialetto: la definizione gli appartiene.\"\r\n >\r\n Dialetto \u00B7 <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e\u2019 una conseguenza dell\u2019immutabilita\u2019 dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n \u21B6 Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n \u21B7 Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n \u2317 Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E\u2019 il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice \u00ABquesto editor non salva\u00BB; un\r\n bottone disabilitato con il motivo dice \u00ABqui non si salva, ed ecco perche\u2019\u00BB \u2014 ed e\u2019 la\r\n verita\u2019 quando l\u2019host non espone `save()`. La disponibilita\u2019 si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l\u2019host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : !hasKey()\r\n ? 'Il documento non ha un nome: e\u2019 la chiave del catalogo, e va creato da la\u2019 (l\u2019aggiornamento e\u2019 PUT /definitions/{nome})'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n \u2913 Salvataggio\u2026\r\n } @else {\r\n \u2913 Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e\u2019 \u00ABnessuna modifica\u00BB nell\u2019indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">\u2713 Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso\u2026</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire \u00ABva tutto bene\u00BB\r\n quando non lo sappiamo piu\u2019 e\u2019 peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell\u2019ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: correggere significherebbe migrare le\r\n espressioni SQL native, ed e\u2019 dell\u2019host. Non e\u2019 una regola di dominio duplicata \u2014 non giudica\r\n la definizione, riporta il disaccordo fra due fatti che l\u2019ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L\u2019ambiente dichiara {{ dialectLabels[declared] }}, la definizione e\u2019 scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera\u2019 con il dialetto <strong>della definizione</strong>. Allinearli e\u2019 una\r\n migrazione delle espressioni SQL native e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il documento ricevuto non e' una definizione. Banner **persistente** e in cima: e' un difetto\r\n dell'integrazione, non dell'utente, e un canvas vuoto da solo non lo dice \u2014 sembra una\r\n definizione senza nodi. Non si nasconde l'editor: cio' che c'e' resta visibile, e il salvataggio\r\n e' disabilitato da se' perche' la chiave manca.\r\n -->\r\n @if (documentProblem(); as problem) {\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Il documento ricevuto non e\u2019 una definizione: {{ problem }}.</strong>\r\n <p>\r\n L\u2019editor riceve il documento dall\u2019applicazione ospite, e questo non ha la forma di una\r\n <code>DpeDefinition</code>. Le tre cause reali: il backend restituisce l\u2019involucro di lettura\r\n (<code>{{ '{' }} definition, diagnostics {{ '}' }}</code>) invece del documento; l\u2019ospite\r\n passa il <strong>sommario</strong> del catalogo al posto della definizione caricata con\r\n <code>load(nome)</code>; la risposta e\u2019 in PascalCase (<code>Name</code> non e\u2019\r\n <code>name</code>).\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e\u2019 peggio di nessun salvataggio,\r\n perche\u2019 l\u2019indicatore \u00ABmodificata\u00BB sparirebbe per un\u2019operazione che non e\u2019 avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n />\r\n\r\n <dpe-node-inspector class=\"dpe-shell__inspector\" (layoutRequested)=\"runAutoLayout()\" />\r\n </div>\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "component", type: DpeCanvasComponent, selector: "dpe-canvas", inputs: ["isEditable"], outputs: ["nodeOpened", "nodeDropped"] }, { kind: "component", type: DpeDiagnosticsPanelComponent, selector: "dpe-diagnostics-panel", outputs: ["navigate"] }, { kind: "component", type: DpeNodeInspectorComponent, selector: "dpe-node-inspector", outputs: ["layoutRequested"] }, { kind: "component", type: DpeNodePaletteComponent, selector: "dpe-node-palette", outputs: ["nodeRequested"] }, { kind: "component", type: DpeSchemaPanelComponent, selector: "dpe-schema-panel" }, { kind: "component", type: DpeSqlPreviewComponent, selector: "dpe-sql-preview" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8641
8766
|
}
|
|
8642
8767
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeBuilderComponent, decorators: [{
|
|
8643
8768
|
type: Component,
|
|
@@ -8648,7 +8773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
8648
8773
|
DpeNodePaletteComponent,
|
|
8649
8774
|
DpeSchemaPanelComponent,
|
|
8650
8775
|
DpeSqlPreviewComponent,
|
|
8651
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DpeDocumentStore, DpeLayoutService, DpeValidationService, DpeMetadataStore], host: { '(keydown)': 'onKeyDown($event)' }, template: "<!--\r\n `.dpe-builder` porta le variabili del tema: e\u2019 la classe radice su cui l\u2019app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all\u2019ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a \u00ABnessuna modifica\u00BB.\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall\u2019ultimo salvataggio'\r\n : 'Il documento coincide con l\u2019ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n \u25CF modificata\r\n } @else {\r\n \u25CB nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e\u2019 dichiarato dall\u2019ambiente, non scelto\r\n qui. Mostra quello della definizione, che e\u2019 quello con cui il backend compilera\u2019 (ADR\r\n 0009). Il titolo dice le due cose che contano \u2014 chi lo decide, e che le espressioni sono\r\n SQL nativo di quel dialetto (ADR 0006).\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e\u2019 dichiarato dall\u2019ambiente e non si modifica dall\u2019editor. Le espressioni sono SQL nativo di questo dialetto: la definizione gli appartiene.\"\r\n >\r\n Dialetto \u00B7 <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e\u2019 una conseguenza dell\u2019immutabilita\u2019 dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n \u21B6 Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n \u21B7 Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n \u2317 Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E\u2019 il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice \u00ABquesto editor non salva\u00BB; un\r\n bottone disabilitato con il motivo dice \u00ABqui non si salva, ed ecco perche\u2019\u00BB \u2014 ed e\u2019 la\r\n verita\u2019 quando l\u2019host non espone `save()`. La disponibilita\u2019 si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l\u2019host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n \u2913 Salvataggio\u2026\r\n } @else {\r\n \u2913 Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e\u2019 \u00ABnessuna modifica\u00BB nell\u2019indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">\u2713 Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso\u2026</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire \u00ABva tutto bene\u00BB\r\n quando non lo sappiamo piu\u2019 e\u2019 peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell\u2019ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: correggere significherebbe migrare le\r\n espressioni SQL native, ed e\u2019 dell\u2019host. Non e\u2019 una regola di dominio duplicata \u2014 non giudica\r\n la definizione, riporta il disaccordo fra due fatti che l\u2019ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L\u2019ambiente dichiara {{ dialectLabels[declared] }}, la definizione e\u2019 scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera\u2019 con il dialetto <strong>della definizione</strong>. Allinearli e\u2019 una\r\n migrazione delle espressioni SQL native e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e\u2019 peggio di nessun salvataggio,\r\n perche\u2019 l\u2019indicatore \u00ABmodificata\u00BB sparirebbe per un\u2019operazione che non e\u2019 avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n />\r\n\r\n <dpe-node-inspector class=\"dpe-shell__inspector\" (layoutRequested)=\"runAutoLayout()\" />\r\n </div>\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n" }]
|
|
8776
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DpeDocumentStore, DpeLayoutService, DpeValidationService, DpeMetadataStore], host: { '(keydown)': 'onKeyDown($event)' }, template: "<!--\r\n `.dpe-builder` porta le variabili del tema: e\u2019 la classe radice su cui l\u2019app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all\u2019ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a \u00ABnessuna modifica\u00BB.\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall\u2019ultimo salvataggio'\r\n : 'Il documento coincide con l\u2019ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n \u25CF modificata\r\n } @else {\r\n \u25CB nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e\u2019 dichiarato dall\u2019ambiente, non scelto\r\n qui. Mostra quello della definizione, che e\u2019 quello con cui il backend compilera\u2019 (ADR\r\n 0009). Il titolo dice le due cose che contano \u2014 chi lo decide, e che le espressioni sono\r\n SQL nativo di quel dialetto (ADR 0006).\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e\u2019 dichiarato dall\u2019ambiente e non si modifica dall\u2019editor. Le espressioni sono SQL nativo di questo dialetto: la definizione gli appartiene.\"\r\n >\r\n Dialetto \u00B7 <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e\u2019 una conseguenza dell\u2019immutabilita\u2019 dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n \u21B6 Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n \u21B7 Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n \u2317 Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E\u2019 il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice \u00ABquesto editor non salva\u00BB; un\r\n bottone disabilitato con il motivo dice \u00ABqui non si salva, ed ecco perche\u2019\u00BB \u2014 ed e\u2019 la\r\n verita\u2019 quando l\u2019host non espone `save()`. La disponibilita\u2019 si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l\u2019host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : !hasKey()\r\n ? 'Il documento non ha un nome: e\u2019 la chiave del catalogo, e va creato da la\u2019 (l\u2019aggiornamento e\u2019 PUT /definitions/{nome})'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n \u2913 Salvataggio\u2026\r\n } @else {\r\n \u2913 Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e\u2019 \u00ABnessuna modifica\u00BB nell\u2019indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">\u2713 Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso\u2026</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire \u00ABva tutto bene\u00BB\r\n quando non lo sappiamo piu\u2019 e\u2019 peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell\u2019ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: correggere significherebbe migrare le\r\n espressioni SQL native, ed e\u2019 dell\u2019host. Non e\u2019 una regola di dominio duplicata \u2014 non giudica\r\n la definizione, riporta il disaccordo fra due fatti che l\u2019ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L\u2019ambiente dichiara {{ dialectLabels[declared] }}, la definizione e\u2019 scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera\u2019 con il dialetto <strong>della definizione</strong>. Allinearli e\u2019 una\r\n migrazione delle espressioni SQL native e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il documento ricevuto non e' una definizione. Banner **persistente** e in cima: e' un difetto\r\n dell'integrazione, non dell'utente, e un canvas vuoto da solo non lo dice \u2014 sembra una\r\n definizione senza nodi. Non si nasconde l'editor: cio' che c'e' resta visibile, e il salvataggio\r\n e' disabilitato da se' perche' la chiave manca.\r\n -->\r\n @if (documentProblem(); as problem) {\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Il documento ricevuto non e\u2019 una definizione: {{ problem }}.</strong>\r\n <p>\r\n L\u2019editor riceve il documento dall\u2019applicazione ospite, e questo non ha la forma di una\r\n <code>DpeDefinition</code>. Le tre cause reali: il backend restituisce l\u2019involucro di lettura\r\n (<code>{{ '{' }} definition, diagnostics {{ '}' }}</code>) invece del documento; l\u2019ospite\r\n passa il <strong>sommario</strong> del catalogo al posto della definizione caricata con\r\n <code>load(nome)</code>; la risposta e\u2019 in PascalCase (<code>Name</code> non e\u2019\r\n <code>name</code>).\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e\u2019 peggio di nessun salvataggio,\r\n perche\u2019 l\u2019indicatore \u00ABmodificata\u00BB sparirebbe per un\u2019operazione che non e\u2019 avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n />\r\n\r\n <dpe-node-inspector class=\"dpe-shell__inspector\" (layoutRequested)=\"runAutoLayout()\" />\r\n </div>\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n" }]
|
|
8652
8777
|
}], ctorParameters: () => [], propDecorators: { definition: [{ type: i0.Input, args: [{ isSignal: true, alias: "definition", required: true }] }], isEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditable", required: false }] }], definitionChange: [{ type: i0.Output, args: ["definitionChange"] }], canvas: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DpeCanvasComponent), { isSignal: true }] }] } });
|
|
8653
8778
|
|
|
8654
8779
|
/**
|