@esfaenza/dpe-builder 20.3.7 → 20.3.8
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 +576 -232
- package/fesm2022/esfaenza-dpe-builder.mjs.map +1 -1
- package/index.d.ts +152 -19
- package/package.json +1 -1
- package/styles/dpe-builder.css +432 -59
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { throwError, defer, catchError, map, tap, EMPTY, switchMap, of, debounceTime } from 'rxjs';
|
|
1
|
+
import { throwError, defer, catchError, map, tap, EMPTY, switchMap, of, filter, 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';
|
|
@@ -1446,7 +1446,20 @@ function dpeNodeSubtitle(collection, node) {
|
|
|
1446
1446
|
*/
|
|
1447
1447
|
/** Profondita' della cronologia: oltre, le istantanee piu' vecchie si buttano. */
|
|
1448
1448
|
const HISTORY_LIMIT = 100;
|
|
1449
|
-
/**
|
|
1449
|
+
/**
|
|
1450
|
+
* Una definizione vuota ma **leggibile**: `formatVersion` e `dialect` non sono opzionali.
|
|
1451
|
+
*
|
|
1452
|
+
* **Serve a `createDefinition`, non a montare l'editor.** E' il documento minimo che si manda al
|
|
1453
|
+
* backend per far nascere una definizione nel catalogo (ADR 0019), e da li' in poi quella che si
|
|
1454
|
+
* edita e' la definizione **caricata con `load(nome)`**.
|
|
1455
|
+
*
|
|
1456
|
+
* Montare l'editor su un documento costruito qui, o su uno costruito a mano senza nome, produce due
|
|
1457
|
+
* sintomi che sembrano scollegati e sono la stessa cosa: la validazione risponde
|
|
1458
|
+
* `DPE_MODEL_REQUIRED_MISSING` su `name` e `label` — per il backend una stringa vuota e un campo
|
|
1459
|
+
* assente sono lo stesso caso — e il salvataggio non ha una chiave per la rotta
|
|
1460
|
+
* `PUT /definitions/{name}`. L'editor lo dichiara in cima con un banner e disabilita «Salva», ma la
|
|
1461
|
+
* correzione e' a monte: creare dal catalogo e caricare.
|
|
1462
|
+
*/
|
|
1450
1463
|
function dpeEmptyDefinition(name, label, dialect) {
|
|
1451
1464
|
return { formatVersion: 1, name, label, dialect };
|
|
1452
1465
|
}
|
|
@@ -1461,6 +1474,16 @@ class DpeDocumentStore {
|
|
|
1461
1474
|
* sulla selezione.
|
|
1462
1475
|
*/
|
|
1463
1476
|
_selectedNodeNames = signal([], ...(ngDevMode ? [{ debugName: "_selectedNodeNames" }] : []));
|
|
1477
|
+
/**
|
|
1478
|
+
* La dialog di modifica del nodo e' aperta. **Un booleano**, non un secondo nome di nodo: il
|
|
1479
|
+
* nodo su cui si sta agendo resta `selectedNodeName()`, e due registrazioni dello stesso fatto
|
|
1480
|
+
* sono esattamente cio' che questo store dichiara di non voler avere (ci ha gia' rimesso una
|
|
1481
|
+
* volta con la doppia selezione di @foblex/flow).
|
|
1482
|
+
*
|
|
1483
|
+
* Non entra nella cronologia: aprire una finestra non e' una modifica del documento, e un undo
|
|
1484
|
+
* che aprisse o chiudesse finestre sarebbe un comando che fa due cose.
|
|
1485
|
+
*/
|
|
1486
|
+
_isNodeEditorOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isNodeEditorOpen" }] : []));
|
|
1464
1487
|
_past = signal([], ...(ngDevMode ? [{ debugName: "_past" }] : []));
|
|
1465
1488
|
_future = signal([], ...(ngDevMode ? [{ debugName: "_future" }] : []));
|
|
1466
1489
|
/**
|
|
@@ -1471,8 +1494,18 @@ class DpeDocumentStore {
|
|
|
1471
1494
|
* — e tornare a "no" quando si annulla fino allo stato di partenza.
|
|
1472
1495
|
*/
|
|
1473
1496
|
_savedSnapshot = signal(JSON.stringify(dpeEmptyDefinition('', '', 'Sqlite')), ...(ngDevMode ? [{ debugName: "_savedSnapshot" }] : []));
|
|
1497
|
+
/**
|
|
1498
|
+
* Vero da quando un documento e' stato **caricato**. Distingue il segnaposto del costruttore da
|
|
1499
|
+
* una definizione vera, e serve a chi non deve lavorare sul segnaposto: la validazione non lo
|
|
1500
|
+
* manda al backend (`DpeValidationService`), perche' la risposta sarebbero i quattro rilievi di
|
|
1501
|
+
* una definizione vuota — `name`, `label`, `dataSources` e nessun writeback — che descrivono il
|
|
1502
|
+
* segnaposto e non il lavoro dell'utente, e che dall'editor nessuno puo' correggere.
|
|
1503
|
+
*/
|
|
1504
|
+
_hasDocument = signal(false, ...(ngDevMode ? [{ debugName: "_hasDocument" }] : []));
|
|
1474
1505
|
/** La definizione corrente: unica fonte di verita' per canvas, ispettori e pannelli. */
|
|
1475
1506
|
definition = this._definition.asReadonly();
|
|
1507
|
+
/** Vero da quando `load` ha portato un documento: prima c'e' solo il segnaposto. */
|
|
1508
|
+
hasDocument = this._hasDocument.asReadonly();
|
|
1476
1509
|
/** Nodi e archi derivati dalla definizione. Ricalcolati solo quando la definizione cambia. */
|
|
1477
1510
|
graph = computed(() => dpeBuildGraph(this._definition()), ...(ngDevMode ? [{ debugName: "graph" }] : []));
|
|
1478
1511
|
selectedNodeNames = this._selectedNodeNames.asReadonly();
|
|
@@ -1487,6 +1520,11 @@ class DpeDocumentStore {
|
|
|
1487
1520
|
const name = this.selectedNodeName();
|
|
1488
1521
|
return name === undefined ? undefined : dpeFindNode(this._definition(), name)?.node;
|
|
1489
1522
|
}, ...(ngDevMode ? [{ debugName: "selectedNode" }] : []));
|
|
1523
|
+
/**
|
|
1524
|
+
* La dialog di modifica del nodo e' aperta. Non e' una seconda identita': il nodo e'
|
|
1525
|
+
* `selectedNodeName()`.
|
|
1526
|
+
*/
|
|
1527
|
+
isNodeEditorOpen = this._isNodeEditorOpen.asReadonly();
|
|
1490
1528
|
// --- cio' che arriva dal backend ------------------------------------------
|
|
1491
1529
|
validateResult = this._validateResult.asReadonly();
|
|
1492
1530
|
compileResult = this._compileResult.asReadonly();
|
|
@@ -1543,11 +1581,16 @@ class DpeDocumentStore {
|
|
|
1543
1581
|
*/
|
|
1544
1582
|
load(definition) {
|
|
1545
1583
|
this._definition.set(definition);
|
|
1584
|
+
this._hasDocument.set(true);
|
|
1546
1585
|
// Cio' che e' appena arrivato **e'** lo stato salvato: aprire un documento non lo sporca.
|
|
1547
1586
|
this._savedSnapshot.set(JSON.stringify(definition));
|
|
1548
1587
|
this._past.set([]);
|
|
1549
1588
|
this._future.set([]);
|
|
1550
1589
|
this._selectedNodeNames.set([]);
|
|
1590
|
+
// Una dialog rimasta aperta guarderebbe un nodo di **un'altra** definizione: il nome potrebbe
|
|
1591
|
+
// anche esistere qui, e allora si starebbe modificando un nodo che nessuno ha aperto. E' il
|
|
1592
|
+
// caso che si dimentica, perche' non fallisce — mostra la cosa sbagliata.
|
|
1593
|
+
this._isNodeEditorOpen.set(false);
|
|
1551
1594
|
this._validateResult.set(undefined);
|
|
1552
1595
|
this._compileResult.set(undefined);
|
|
1553
1596
|
}
|
|
@@ -1583,6 +1626,24 @@ class DpeDocumentStore {
|
|
|
1583
1626
|
selectMany(nodeNames) {
|
|
1584
1627
|
this._selectedNodeNames.set(Array.from(new Set(nodeNames)));
|
|
1585
1628
|
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Seleziona **solo** quel nodo e apre la dialog di modifica.
|
|
1631
|
+
*
|
|
1632
|
+
* La selezione **collassa a un nodo solo**: il gesto «apri questo» dichiara su chi si sta
|
|
1633
|
+
* agendo, e la regola gia' in vigore e' che con piu' di un nodo selezionato non si mostrano i
|
|
1634
|
+
* campi di uno.
|
|
1635
|
+
*
|
|
1636
|
+
* Le due scritture sono **esplicite** e non conseguenza di un `effect`: un effect che scrive
|
|
1637
|
+
* signal renderebbe l'ordine di questi due fatti dipendente dalla schedulazione, cioe' non
|
|
1638
|
+
* testabile.
|
|
1639
|
+
*/
|
|
1640
|
+
openNodeEditor(nodeName) {
|
|
1641
|
+
this.select(nodeName);
|
|
1642
|
+
this._isNodeEditorOpen.set(true);
|
|
1643
|
+
}
|
|
1644
|
+
closeNodeEditor() {
|
|
1645
|
+
this._isNodeEditorOpen.set(false);
|
|
1646
|
+
}
|
|
1586
1647
|
// -------------------------------------------------------------------------
|
|
1587
1648
|
// Mutazioni — sincrone e granulari
|
|
1588
1649
|
// -------------------------------------------------------------------------
|
|
@@ -1635,6 +1696,9 @@ class DpeDocumentStore {
|
|
|
1635
1696
|
if (!located) {
|
|
1636
1697
|
return;
|
|
1637
1698
|
}
|
|
1699
|
+
// Si legge **prima** della potatura della selezione: dopo, `selectedNodeName()` e' gia' un
|
|
1700
|
+
// altro nodo (o nessuno) e non si saprebbe piu' se la dialog stava guardando questo.
|
|
1701
|
+
const wasOpenOnIt = this._isNodeEditorOpen() && this.selectedNodeName() === nodeName;
|
|
1638
1702
|
const existing = (definition[located.collection] ?? []);
|
|
1639
1703
|
const next = existing.filter((node) => node.name !== nodeName);
|
|
1640
1704
|
this.commit(this.withCollection(definition, located.collection, next));
|
|
@@ -1645,6 +1709,13 @@ class DpeDocumentStore {
|
|
|
1645
1709
|
if (selected.includes(nodeName)) {
|
|
1646
1710
|
this._selectedNodeNames.set(selected.filter((name) => name !== nodeName));
|
|
1647
1711
|
}
|
|
1712
|
+
// Il bersaglio della dialog e' svanito: dopo l'eliminazione non c'e' piu' niente da
|
|
1713
|
+
// modificare. La chiusura sta qui e non nel componente perche' «Elimina il nodo» sta nel
|
|
1714
|
+
// piede della dialog, e un componente che si chiude da se' dopo aver chiesto una mutazione
|
|
1715
|
+
// sarebbe una seconda regola sullo stesso fatto.
|
|
1716
|
+
if (wasOpenOnIt) {
|
|
1717
|
+
this._isNodeEditorOpen.set(false);
|
|
1718
|
+
}
|
|
1648
1719
|
}
|
|
1649
1720
|
/**
|
|
1650
1721
|
* L'**unico** punto che rinomina un nodo (**I5**): delega alla funzione pura, che aggiorna
|
|
@@ -2313,7 +2384,20 @@ class DpeValidationService {
|
|
|
2313
2384
|
hasResult = computed(() => this.store.validateResult() !== undefined, ...(ngDevMode ? [{ debugName: "hasResult" }] : []));
|
|
2314
2385
|
constructor() {
|
|
2315
2386
|
toObservable(this.store.definition)
|
|
2316
|
-
.pipe(
|
|
2387
|
+
.pipe(
|
|
2388
|
+
/*
|
|
2389
|
+
* **Non si valida il segnaposto.** Lo store nasce con `dpeEmptyDefinition('', '', 'Sqlite')`
|
|
2390
|
+
* e questo servizio si iscrive alla costruzione: se il documento vero arriva dopo la
|
|
2391
|
+
* finestra di debounce — un ospite che monta l'editor mentre `load(nome)` e' in volo, cioe'
|
|
2392
|
+
* il caso normale con una rete vera — la finestra si chiudeva sul segnaposto e l'editor
|
|
2393
|
+
* validava un documento che nessuno aveva fornito. La risposta era esatta e inutile:
|
|
2394
|
+
* `DPE_MODEL_REQUIRED_MISSING` su `name`, `label` e `dataSources` piu'
|
|
2395
|
+
* `DPE_GRAPH_NO_WRITEBACK` — quattro rilievi del segnaposto, che l'utente non puo'
|
|
2396
|
+
* correggere da qui e che sembrano riguardare la sua definizione appena creata.
|
|
2397
|
+
*
|
|
2398
|
+
* Il filtro sta **prima** del debounce: il segnaposto non deve nemmeno aprire la finestra.
|
|
2399
|
+
*/
|
|
2400
|
+
filter(() => this.store.hasDocument()), debounceTime(VALIDATE_DEBOUNCE_MS), tap(() => {
|
|
2317
2401
|
this._isValidating.set(true);
|
|
2318
2402
|
this._error.set(undefined);
|
|
2319
2403
|
}),
|
|
@@ -3185,6 +3269,165 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
3185
3269
|
}]
|
|
3186
3270
|
}], propDecorators: { navigate: [{ type: i0.Output, args: ["navigate"] }] } });
|
|
3187
3271
|
|
|
3272
|
+
/**
|
|
3273
|
+
* I **fatti dal backend** su un nodo: schema in uscita e rilievi che lo riguardano.
|
|
3274
|
+
*
|
|
3275
|
+
* Sono in un componente a se' perche' nella dialog di modifica (ADR 0026) non stanno dove stanno
|
|
3276
|
+
* i campi: i campi prendono la colonna larga, questi la colonna stretta di destra. Metterli **di
|
|
3277
|
+
* fianco** e non sopra il form evita in anticipo il muro di avvisi che spinge il primo campo
|
|
3278
|
+
* fuori dallo schermo — con i rilievi in colonna il muro non si forma, quindi si mostrano
|
|
3279
|
+
* **tutti** (**I6**: l'elenco della validazione e' esaustivo, e troncarlo sarebbe mentire).
|
|
3280
|
+
*
|
|
3281
|
+
* In testa sta lo stato **valido ma non compilabile** (**I10**), perche' e' il fatto che spiega gli
|
|
3282
|
+
* altri due: senza di esso una colonna che elenca i rilievi tace proprio sul motivo per cui da
|
|
3283
|
+
* questo nodo non uscira' nessun SQL. «Non compilabile» e «non valido» sono due significati
|
|
3284
|
+
* distinti, e l'editor li tiene distinti per decisione (ADR 0011).
|
|
3285
|
+
*
|
|
3286
|
+
* Non calcola niente. Lo schema arriva da `validate` (**I4**: la propagazione e' del backend) e i
|
|
3287
|
+
* rilievi arrivano gia' raggruppati per nodo: qui si legge e si rende.
|
|
3288
|
+
*/
|
|
3289
|
+
class DpeNodeFactsComponent {
|
|
3290
|
+
store = inject(DpeDocumentStore);
|
|
3291
|
+
/**
|
|
3292
|
+
* Il nodo di cui si mostrano i fatti. Arriva per input e non dalla selezione: chi monta questo
|
|
3293
|
+
* componente sa quale nodo sta guardando, e leggere qui la selezione lo farebbe cambiare
|
|
3294
|
+
* bersaglio sotto la dialog che lo contiene.
|
|
3295
|
+
*/
|
|
3296
|
+
nodeName = input.required(...(ngDevMode ? [{ debugName: "nodeName" }] : []));
|
|
3297
|
+
/**
|
|
3298
|
+
* Il nodo e' valido ma **non compilabile** (**I10**). Due rami, ed e' la **stessa espressione**
|
|
3299
|
+
* che `DpeNodeInspectorBase.isNotCompilable` usa gia': il codice diagnostico
|
|
3300
|
+
* `DPE_NODE_NOT_COMPILABLE` raggruppato per nodo, **oppure** lo schema che dichiara
|
|
3301
|
+
* `isCompilable: false`. Servono entrambi perche' il backend puo' dirlo per due vie — un rilievo
|
|
3302
|
+
* sul nodo, o la riga di schema — e riconoscerne una sola vorrebbe dire tacere nell'altro caso.
|
|
3303
|
+
*
|
|
3304
|
+
* Qui non si giudica niente: la decisione e' del compilatore, questa e' la sua risposta.
|
|
3305
|
+
*/
|
|
3306
|
+
isNotCompilable = computed(() => (this.store.diagnosticsByNode().get(this.nodeName())?.isNotCompilable ?? false) ||
|
|
3307
|
+
this.store.schemaByNode().get(this.nodeName())?.isCompilable === false, ...(ngDevMode ? [{ debugName: "isNotCompilable" }] : []));
|
|
3308
|
+
columns = computed(() => this.store.schemaByNode().get(this.nodeName())?.columns ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
3309
|
+
diagnostics = computed(() => this.store.diagnosticsByNode().get(this.nodeName())?.diagnostics ?? [], ...(ngDevMode ? [{ debugName: "diagnostics" }] : []));
|
|
3310
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeFactsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3311
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeNodeFactsComponent, isStandalone: true, selector: "dpe-node-facts", inputs: { nodeName: { classPropertyName: "nodeName", publicName: "nodeName", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
3312
|
+
@if (isNotCompilable()) {
|
|
3313
|
+
<!--
|
|
3314
|
+
Tono deliberatamente **non** da errore: colore proprio (ne’ rosso ne’ giallo) e le stesse
|
|
3315
|
+
parole degli ispettori di Forecast e CustomNode, perche’ e’ lo stesso fatto detto nello
|
|
3316
|
+
stesso linguaggio. Chiedere di correggere qualcosa qui sarebbe una bugia.
|
|
3317
|
+
-->
|
|
3318
|
+
<p class="dpe-note dpe-note--not-compilable">
|
|
3319
|
+
<span class="dpe-badge dpe-badge--not-compilable">non compilabile</span>
|
|
3320
|
+
Il nodo e’ <strong>valido</strong>: non c’e’ niente da correggere. Il compilatore non lo
|
|
3321
|
+
traduce in questa versione, quindi per questo nodo
|
|
3322
|
+
<strong>non verra’ generato SQL</strong>.
|
|
3323
|
+
</p>
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
<section class="dpe-section">
|
|
3327
|
+
<h4 class="dpe-section__title">Schema in uscita</h4>
|
|
3328
|
+
@if (columns().length === 0) {
|
|
3329
|
+
<p class="dpe-empty">
|
|
3330
|
+
Nessuna colonna: il backend non ha (ancora) propagato uno schema per questo nodo.
|
|
3331
|
+
</p>
|
|
3332
|
+
} @else {
|
|
3333
|
+
<div class="dpe-columns">
|
|
3334
|
+
@for (column of columns(); track $index) {
|
|
3335
|
+
<span
|
|
3336
|
+
class="dpe-column"
|
|
3337
|
+
[class.dpe-column--nullable]="column.isNullable"
|
|
3338
|
+
[title]="column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')"
|
|
3339
|
+
>
|
|
3340
|
+
{{ column.name }}
|
|
3341
|
+
</span>
|
|
3342
|
+
}
|
|
3343
|
+
</div>
|
|
3344
|
+
<p class="dpe-field__hint">
|
|
3345
|
+
Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo
|
|
3346
|
+
calcoliamo qui.
|
|
3347
|
+
</p>
|
|
3348
|
+
}
|
|
3349
|
+
</section>
|
|
3350
|
+
|
|
3351
|
+
@if (diagnostics().length > 0) {
|
|
3352
|
+
<section class="dpe-section">
|
|
3353
|
+
<h4 class="dpe-section__title">Rilievi su questo nodo</h4>
|
|
3354
|
+
@for (diagnostic of diagnostics(); track $index) {
|
|
3355
|
+
<p
|
|
3356
|
+
class="dpe-field__issue"
|
|
3357
|
+
[class.dpe-field__issue--warning]="diagnostic.severity === 'Warning'"
|
|
3358
|
+
[class.dpe-field__issue--info]="diagnostic.severity === 'Info'"
|
|
3359
|
+
>
|
|
3360
|
+
<code>{{ diagnostic.code }}</code> — {{ diagnostic.message }}
|
|
3361
|
+
</p>
|
|
3362
|
+
}
|
|
3363
|
+
</section>
|
|
3364
|
+
}
|
|
3365
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3366
|
+
}
|
|
3367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeFactsComponent, decorators: [{
|
|
3368
|
+
type: Component,
|
|
3369
|
+
args: [{
|
|
3370
|
+
selector: 'dpe-node-facts',
|
|
3371
|
+
standalone: true,
|
|
3372
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3373
|
+
template: `
|
|
3374
|
+
@if (isNotCompilable()) {
|
|
3375
|
+
<!--
|
|
3376
|
+
Tono deliberatamente **non** da errore: colore proprio (ne’ rosso ne’ giallo) e le stesse
|
|
3377
|
+
parole degli ispettori di Forecast e CustomNode, perche’ e’ lo stesso fatto detto nello
|
|
3378
|
+
stesso linguaggio. Chiedere di correggere qualcosa qui sarebbe una bugia.
|
|
3379
|
+
-->
|
|
3380
|
+
<p class="dpe-note dpe-note--not-compilable">
|
|
3381
|
+
<span class="dpe-badge dpe-badge--not-compilable">non compilabile</span>
|
|
3382
|
+
Il nodo e’ <strong>valido</strong>: non c’e’ niente da correggere. Il compilatore non lo
|
|
3383
|
+
traduce in questa versione, quindi per questo nodo
|
|
3384
|
+
<strong>non verra’ generato SQL</strong>.
|
|
3385
|
+
</p>
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
<section class="dpe-section">
|
|
3389
|
+
<h4 class="dpe-section__title">Schema in uscita</h4>
|
|
3390
|
+
@if (columns().length === 0) {
|
|
3391
|
+
<p class="dpe-empty">
|
|
3392
|
+
Nessuna colonna: il backend non ha (ancora) propagato uno schema per questo nodo.
|
|
3393
|
+
</p>
|
|
3394
|
+
} @else {
|
|
3395
|
+
<div class="dpe-columns">
|
|
3396
|
+
@for (column of columns(); track $index) {
|
|
3397
|
+
<span
|
|
3398
|
+
class="dpe-column"
|
|
3399
|
+
[class.dpe-column--nullable]="column.isNullable"
|
|
3400
|
+
[title]="column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')"
|
|
3401
|
+
>
|
|
3402
|
+
{{ column.name }}
|
|
3403
|
+
</span>
|
|
3404
|
+
}
|
|
3405
|
+
</div>
|
|
3406
|
+
<p class="dpe-field__hint">
|
|
3407
|
+
Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo
|
|
3408
|
+
calcoliamo qui.
|
|
3409
|
+
</p>
|
|
3410
|
+
}
|
|
3411
|
+
</section>
|
|
3412
|
+
|
|
3413
|
+
@if (diagnostics().length > 0) {
|
|
3414
|
+
<section class="dpe-section">
|
|
3415
|
+
<h4 class="dpe-section__title">Rilievi su questo nodo</h4>
|
|
3416
|
+
@for (diagnostic of diagnostics(); track $index) {
|
|
3417
|
+
<p
|
|
3418
|
+
class="dpe-field__issue"
|
|
3419
|
+
[class.dpe-field__issue--warning]="diagnostic.severity === 'Warning'"
|
|
3420
|
+
[class.dpe-field__issue--info]="diagnostic.severity === 'Info'"
|
|
3421
|
+
>
|
|
3422
|
+
<code>{{ diagnostic.code }}</code> — {{ diagnostic.message }}
|
|
3423
|
+
</p>
|
|
3424
|
+
}
|
|
3425
|
+
</section>
|
|
3426
|
+
}
|
|
3427
|
+
`,
|
|
3428
|
+
}]
|
|
3429
|
+
}], propDecorators: { nodeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeName", required: true }] }] } });
|
|
3430
|
+
|
|
3188
3431
|
/**
|
|
3189
3432
|
* `[dpeValue]` su un `<select>`.
|
|
3190
3433
|
*
|
|
@@ -7491,48 +7734,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
7491
7734
|
}] });
|
|
7492
7735
|
|
|
7493
7736
|
/**
|
|
7494
|
-
*
|
|
7737
|
+
* **Solo** i campi modificabili di **un** nodo: i campi comuni, poi l'ispettore della categoria.
|
|
7738
|
+
*
|
|
7739
|
+
* Cio' che non c'e' piu', e dove e' andato (ADR 0026): la testata di identita' e' della dialog che
|
|
7740
|
+
* lo contiene, le due sezioni di fatti in sola lettura sono di `dpe-node-facts`, e il caso «piu' di
|
|
7741
|
+
* un nodo selezionato» e' una striscia contestuale del guscio. Non c'e' **nessun** booleano di
|
|
7742
|
+
* modalita' al loro posto: si e' spostato contenuto, non aggiunto un interruttore che cambia il
|
|
7743
|
+
* significato del componente.
|
|
7495
7744
|
*
|
|
7496
7745
|
* La **rinomina** sta qui e non nei singoli ispettori, per due ragioni. E' identica per tutte le
|
|
7497
7746
|
* categorie, e soprattutto e' un refactoring globale (**I5**): passa dall'unico punto che
|
|
7498
7747
|
* riscrive i riferimenti a valle, e prima di applicarla **dice quante occorrenze tocchera'**.
|
|
7499
|
-
* Rinominare non e' modificare un campo, e non deve sembrarlo.
|
|
7500
|
-
*
|
|
7501
|
-
* Con **piu' di un nodo selezionato** non si mostra l'editor del primo: si dichiara quanti sono e
|
|
7502
|
-
* si offrono solo le operazioni valide su molti. Mostrare i campi di uno mentre N sono evidenziati
|
|
7503
|
-
* e' la stessa bugia della doppia evidenziazione, detta piu' piano — l'utente crede di stare
|
|
7504
|
-
* agendo su cio' che vede selezionato.
|
|
7748
|
+
* Rinominare non e' modificare un campo, e non deve sembrarlo. E' anche l'unica eccezione al
|
|
7749
|
+
* commit dal vivo: ha una bozza locale perche' ha un bottone, non perche' sia in una dialog.
|
|
7505
7750
|
*/
|
|
7506
7751
|
class DpeNodeInspectorComponent {
|
|
7507
7752
|
store = inject(DpeDocumentStore);
|
|
7508
|
-
/**
|
|
7509
|
-
* Il riordino richiede le misure delle card, che le conosce il canvas: l'ispettore **chiede**,
|
|
7510
|
-
* non calcola. Componente stupido, orchestrazione fuori.
|
|
7511
|
-
*/
|
|
7512
|
-
layoutRequested = output();
|
|
7513
|
-
selectedNames = computed(() => this.store.selectedNodeNames(), ...(ngDevMode ? [{ debugName: "selectedNames" }] : []));
|
|
7514
7753
|
located = computed(() => {
|
|
7515
7754
|
const name = this.store.selectedNodeName();
|
|
7516
7755
|
return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);
|
|
7517
7756
|
}, ...(ngDevMode ? [{ debugName: "located" }] : []));
|
|
7518
7757
|
nameDraft = signal('', ...(ngDevMode ? [{ debugName: "nameDraft" }] : []));
|
|
7519
|
-
categoryLabel = computed(() => {
|
|
7520
|
-
const entry = this.located();
|
|
7521
|
-
return entry ? dpeNodeCategory(entry.collection).label : '';
|
|
7522
|
-
}, ...(ngDevMode ? [{ debugName: "categoryLabel" }] : []));
|
|
7523
|
-
/** La classe di famiglia dell'icona: la stessa funzione che usano palette e canvas. */
|
|
7524
|
-
familyClass = computed(() => {
|
|
7525
|
-
const entry = this.located();
|
|
7526
|
-
return entry ? dpeNodeIconFamilyClass(entry.collection) : '';
|
|
7527
|
-
}, ...(ngDevMode ? [{ debugName: "familyClass" }] : []));
|
|
7528
|
-
columns = computed(() => {
|
|
7529
|
-
const entry = this.located();
|
|
7530
|
-
return entry ? (this.store.schemaByNode().get(entry.node.name)?.columns ?? []) : [];
|
|
7531
|
-
}, ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
7532
|
-
diagnostics = computed(() => {
|
|
7533
|
-
const entry = this.located();
|
|
7534
|
-
return entry ? (this.store.diagnosticsByNode().get(entry.node.name)?.diagnostics ?? []) : [];
|
|
7535
|
-
}, ...(ngDevMode ? [{ debugName: "diagnostics" }] : []));
|
|
7536
7758
|
referenceCount = computed(() => {
|
|
7537
7759
|
const entry = this.located();
|
|
7538
7760
|
return entry ? this.store.countReferences(entry.node.name) : 0;
|
|
@@ -7550,15 +7772,6 @@ class DpeNodeInspectorComponent {
|
|
|
7550
7772
|
this.nameDraft.set(entry?.node.name ?? '');
|
|
7551
7773
|
});
|
|
7552
7774
|
}
|
|
7553
|
-
/**
|
|
7554
|
-
* Elimina i nodi selezionati. Si itera su una **copia** dei nomi: `removeNode` pota la selezione
|
|
7555
|
-
* a ogni giro, e scorrere il signal mentre lo si svuota salterebbe la meta' dei nodi.
|
|
7556
|
-
*/
|
|
7557
|
-
removeSelected() {
|
|
7558
|
-
for (const name of [...this.selectedNames()]) {
|
|
7559
|
-
this.store.removeNode(name);
|
|
7560
|
-
}
|
|
7561
|
-
}
|
|
7562
7775
|
rename(currentName) {
|
|
7563
7776
|
this.store.renameNode(currentName, this.nameDraft().trim());
|
|
7564
7777
|
}
|
|
@@ -7611,59 +7824,8 @@ class DpeNodeInspectorComponent {
|
|
|
7611
7824
|
return node;
|
|
7612
7825
|
}
|
|
7613
7826
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7614
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeNodeInspectorComponent, isStandalone: true, selector: "dpe-node-inspector",
|
|
7615
|
-
@if (
|
|
7616
|
-
<!--
|
|
7617
|
-
Selezione multipla: si dice quanti sono e si offre solo cio' che ha senso su molti. Nessun
|
|
7618
|
-
campo del primo nodo, perche' non e' su quello che l’utente sta agendo.
|
|
7619
|
-
-->
|
|
7620
|
-
<div class="dpe-inspector__head">
|
|
7621
|
-
<strong>{{ selectedNames().length }} nodi selezionati</strong>
|
|
7622
|
-
<span class="dpe-inspector__category">selezione</span>
|
|
7623
|
-
</div>
|
|
7624
|
-
|
|
7625
|
-
<ul class="dpe-multi__names">
|
|
7626
|
-
@for (name of selectedNames(); track $index) {
|
|
7627
|
-
<li class="dpe-multi__name">{{ name }}</li>
|
|
7628
|
-
}
|
|
7629
|
-
</ul>
|
|
7630
|
-
|
|
7631
|
-
<p class="dpe-note">
|
|
7632
|
-
I campi di un singolo nodo si modificano con un nodo solo selezionato: mostrarli qui
|
|
7633
|
-
farebbe credere di agire su tutti.
|
|
7634
|
-
</p>
|
|
7635
|
-
|
|
7636
|
-
<div class="dpe-row">
|
|
7637
|
-
<button type="button" class="dpe-btn" (click)="removeSelected()">
|
|
7638
|
-
Elimina i {{ selectedNames().length }} nodi
|
|
7639
|
-
</button>
|
|
7640
|
-
<!--
|
|
7641
|
-
Il riordino e’ dichiaratamente **dell’intero grafo**: dagre su un sottografo sarebbe una
|
|
7642
|
-
funzione nuova, e chiamarla «riordina i selezionati» mentre riordina tutto sarebbe una
|
|
7643
|
-
bugia — la stessa classe di difetto che questo pannello esiste per evitare.
|
|
7644
|
-
-->
|
|
7645
|
-
<button type="button" class="dpe-btn" (click)="layoutRequested.emit()">
|
|
7646
|
-
Riordina il grafo
|
|
7647
|
-
</button>
|
|
7648
|
-
</div>
|
|
7649
|
-
|
|
7650
|
-
<p class="dpe-note dpe-note--warning">
|
|
7651
|
-
L’eliminazione lascia i riferimenti a valle così come sono: diventano riferimenti rotti, e
|
|
7652
|
-
il backend li segnala. Si annulla con ↶.
|
|
7653
|
-
</p>
|
|
7654
|
-
} @else if (located(); as entry) {
|
|
7655
|
-
<div class="dpe-inspector__head">
|
|
7656
|
-
<!--
|
|
7657
|
-
La stessa icona della palette e della card: il terzo posto in cui compare il tipo, e
|
|
7658
|
-
deve avere lo stesso aspetto degli altri due.
|
|
7659
|
-
-->
|
|
7660
|
-
<span class="dpe-inspector__icon" [class]="'dpe-inspector__icon ' + familyClass()">
|
|
7661
|
-
<dpe-node-icon [collection]="entry.collection" />
|
|
7662
|
-
</span>
|
|
7663
|
-
<strong class="dpe-inspector__name">{{ entry.node.label || entry.node.name }}</strong>
|
|
7664
|
-
<span class="dpe-inspector__category">{{ categoryLabel() }}</span>
|
|
7665
|
-
</div>
|
|
7666
|
-
|
|
7827
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeNodeInspectorComponent, isStandalone: true, selector: "dpe-node-inspector", ngImport: i0, template: `
|
|
7828
|
+
@if (located(); as entry) {
|
|
7667
7829
|
<div class="dpe-form">
|
|
7668
7830
|
<div class="dpe-field">
|
|
7669
7831
|
<label class="dpe-field__label" for="node-name">Nome tecnico</label>
|
|
@@ -7751,50 +7913,10 @@ class DpeNodeInspectorComponent {
|
|
|
7751
7913
|
}
|
|
7752
7914
|
}
|
|
7753
7915
|
</section>
|
|
7754
|
-
|
|
7755
|
-
<section class="dpe-section">
|
|
7756
|
-
<h4 class="dpe-section__title">Schema in uscita</h4>
|
|
7757
|
-
@if (columns().length === 0) {
|
|
7758
|
-
<p class="dpe-empty">
|
|
7759
|
-
Nessuna colonna: il backend non ha (ancora) propagato uno schema per questo nodo.
|
|
7760
|
-
</p>
|
|
7761
|
-
} @else {
|
|
7762
|
-
<div class="dpe-columns">
|
|
7763
|
-
@for (column of columns(); track $index) {
|
|
7764
|
-
<span
|
|
7765
|
-
class="dpe-column"
|
|
7766
|
-
[class.dpe-column--nullable]="column.isNullable"
|
|
7767
|
-
[title]="column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')"
|
|
7768
|
-
>
|
|
7769
|
-
{{ column.name }}
|
|
7770
|
-
</span>
|
|
7771
|
-
}
|
|
7772
|
-
</div>
|
|
7773
|
-
<p class="dpe-field__hint">
|
|
7774
|
-
Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo
|
|
7775
|
-
calcoliamo qui.
|
|
7776
|
-
</p>
|
|
7777
|
-
}
|
|
7778
|
-
</section>
|
|
7779
|
-
|
|
7780
|
-
@if (diagnostics().length > 0) {
|
|
7781
|
-
<section class="dpe-section">
|
|
7782
|
-
<h4 class="dpe-section__title">Rilievi su questo nodo</h4>
|
|
7783
|
-
@for (diagnostic of diagnostics(); track $index) {
|
|
7784
|
-
<p
|
|
7785
|
-
class="dpe-field__issue"
|
|
7786
|
-
[class.dpe-field__issue--warning]="diagnostic.severity === 'Warning'"
|
|
7787
|
-
[class.dpe-field__issue--info]="diagnostic.severity === 'Info'"
|
|
7788
|
-
>
|
|
7789
|
-
<code>{{ diagnostic.code }}</code> — {{ diagnostic.message }}
|
|
7790
|
-
</p>
|
|
7791
|
-
}
|
|
7792
|
-
</section>
|
|
7793
|
-
}
|
|
7794
7916
|
} @else {
|
|
7795
7917
|
<p class="dpe-empty">Nessun nodo selezionato.</p>
|
|
7796
7918
|
}
|
|
7797
|
-
`, isInline: true, dependencies: [{ kind: "component", type: DpeAggregateInspectorComponent, selector: "dpe-aggregate-inspector" }, { kind: "component", type: DpeAppendInspectorComponent, selector: "dpe-append-inspector" }, { kind: "component", type: DpeAtomicWritebackInspectorComponent, selector: "dpe-atomic-writeback-inspector" }, { kind: "component", type: DpeCustomNodeInspectorComponent, selector: "dpe-custom-node-inspector" }, { kind: "component", type: DpeDataSourceInspectorComponent, selector: "dpe-data-source-inspector" }, { kind: "component", type: DpeFilterInspectorComponent, selector: "dpe-filter-inspector" }, { kind: "component", type: DpeForecastInspectorComponent, selector: "dpe-forecast-inspector" }, { kind: "component", type: DpeHierarchyPathInspectorComponent, selector: "dpe-hierarchy-path-inspector" }, { kind: "component", type: DpeJoinInspectorComponent, selector: "dpe-join-inspector" }, { kind: "component", type: DpeTransformInspectorComponent, selector: "dpe-transform-inspector" }, { kind: "component", type: DpeWritebackInspectorComponent, selector: "dpe-writeback-inspector" }
|
|
7919
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DpeAggregateInspectorComponent, selector: "dpe-aggregate-inspector" }, { kind: "component", type: DpeAppendInspectorComponent, selector: "dpe-append-inspector" }, { kind: "component", type: DpeAtomicWritebackInspectorComponent, selector: "dpe-atomic-writeback-inspector" }, { kind: "component", type: DpeCustomNodeInspectorComponent, selector: "dpe-custom-node-inspector" }, { kind: "component", type: DpeDataSourceInspectorComponent, selector: "dpe-data-source-inspector" }, { kind: "component", type: DpeFilterInspectorComponent, selector: "dpe-filter-inspector" }, { kind: "component", type: DpeForecastInspectorComponent, selector: "dpe-forecast-inspector" }, { kind: "component", type: DpeHierarchyPathInspectorComponent, selector: "dpe-hierarchy-path-inspector" }, { kind: "component", type: DpeJoinInspectorComponent, selector: "dpe-join-inspector" }, { kind: "component", type: DpeTransformInspectorComponent, selector: "dpe-transform-inspector" }, { kind: "component", type: DpeWritebackInspectorComponent, selector: "dpe-writeback-inspector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7798
7920
|
}
|
|
7799
7921
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeInspectorComponent, decorators: [{
|
|
7800
7922
|
type: Component,
|
|
@@ -7813,62 +7935,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
7813
7935
|
DpeJoinInspectorComponent,
|
|
7814
7936
|
DpeTransformInspectorComponent,
|
|
7815
7937
|
DpeWritebackInspectorComponent,
|
|
7816
|
-
DpeNodeIconComponent,
|
|
7817
7938
|
],
|
|
7818
7939
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7819
7940
|
template: `
|
|
7820
|
-
@if (
|
|
7821
|
-
<!--
|
|
7822
|
-
Selezione multipla: si dice quanti sono e si offre solo cio' che ha senso su molti. Nessun
|
|
7823
|
-
campo del primo nodo, perche' non e' su quello che l’utente sta agendo.
|
|
7824
|
-
-->
|
|
7825
|
-
<div class="dpe-inspector__head">
|
|
7826
|
-
<strong>{{ selectedNames().length }} nodi selezionati</strong>
|
|
7827
|
-
<span class="dpe-inspector__category">selezione</span>
|
|
7828
|
-
</div>
|
|
7829
|
-
|
|
7830
|
-
<ul class="dpe-multi__names">
|
|
7831
|
-
@for (name of selectedNames(); track $index) {
|
|
7832
|
-
<li class="dpe-multi__name">{{ name }}</li>
|
|
7833
|
-
}
|
|
7834
|
-
</ul>
|
|
7835
|
-
|
|
7836
|
-
<p class="dpe-note">
|
|
7837
|
-
I campi di un singolo nodo si modificano con un nodo solo selezionato: mostrarli qui
|
|
7838
|
-
farebbe credere di agire su tutti.
|
|
7839
|
-
</p>
|
|
7840
|
-
|
|
7841
|
-
<div class="dpe-row">
|
|
7842
|
-
<button type="button" class="dpe-btn" (click)="removeSelected()">
|
|
7843
|
-
Elimina i {{ selectedNames().length }} nodi
|
|
7844
|
-
</button>
|
|
7845
|
-
<!--
|
|
7846
|
-
Il riordino e’ dichiaratamente **dell’intero grafo**: dagre su un sottografo sarebbe una
|
|
7847
|
-
funzione nuova, e chiamarla «riordina i selezionati» mentre riordina tutto sarebbe una
|
|
7848
|
-
bugia — la stessa classe di difetto che questo pannello esiste per evitare.
|
|
7849
|
-
-->
|
|
7850
|
-
<button type="button" class="dpe-btn" (click)="layoutRequested.emit()">
|
|
7851
|
-
Riordina il grafo
|
|
7852
|
-
</button>
|
|
7853
|
-
</div>
|
|
7854
|
-
|
|
7855
|
-
<p class="dpe-note dpe-note--warning">
|
|
7856
|
-
L’eliminazione lascia i riferimenti a valle così come sono: diventano riferimenti rotti, e
|
|
7857
|
-
il backend li segnala. Si annulla con ↶.
|
|
7858
|
-
</p>
|
|
7859
|
-
} @else if (located(); as entry) {
|
|
7860
|
-
<div class="dpe-inspector__head">
|
|
7861
|
-
<!--
|
|
7862
|
-
La stessa icona della palette e della card: il terzo posto in cui compare il tipo, e
|
|
7863
|
-
deve avere lo stesso aspetto degli altri due.
|
|
7864
|
-
-->
|
|
7865
|
-
<span class="dpe-inspector__icon" [class]="'dpe-inspector__icon ' + familyClass()">
|
|
7866
|
-
<dpe-node-icon [collection]="entry.collection" />
|
|
7867
|
-
</span>
|
|
7868
|
-
<strong class="dpe-inspector__name">{{ entry.node.label || entry.node.name }}</strong>
|
|
7869
|
-
<span class="dpe-inspector__category">{{ categoryLabel() }}</span>
|
|
7870
|
-
</div>
|
|
7871
|
-
|
|
7941
|
+
@if (located(); as entry) {
|
|
7872
7942
|
<div class="dpe-form">
|
|
7873
7943
|
<div class="dpe-field">
|
|
7874
7944
|
<label class="dpe-field__label" for="node-name">Nome tecnico</label>
|
|
@@ -7956,52 +8026,302 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
7956
8026
|
}
|
|
7957
8027
|
}
|
|
7958
8028
|
</section>
|
|
8029
|
+
} @else {
|
|
8030
|
+
<p class="dpe-empty">Nessun nodo selezionato.</p>
|
|
8031
|
+
}
|
|
8032
|
+
`,
|
|
8033
|
+
}]
|
|
8034
|
+
}], ctorParameters: () => [] });
|
|
8035
|
+
|
|
8036
|
+
/**
|
|
8037
|
+
* La finestra di modifica di un nodo: elemento `<dialog>` **nativo**, aperto con `showModal()`
|
|
8038
|
+
* (ADR 0026).
|
|
8039
|
+
*
|
|
8040
|
+
* Perche' l'elemento nativo e non un overlay scritto a mano: **Esc**, il **focus trap** (con la
|
|
8041
|
+
* finestra aperta il Tab non esce), il **fondo inerte** per puntatore *e* tastiera, `::backdrop`
|
|
8042
|
+
* come pseudo-elemento standard e nessun `z-index` — il top layer sta sopra ogni stacking context
|
|
8043
|
+
* senza partecipare alla gara. Sono tutte cose che un overlay dovrebbe reimplementare, e il focus
|
|
8044
|
+
* trap in particolare non si scrive in due righe.
|
|
8045
|
+
*
|
|
8046
|
+
* **Una sola istanza** nel guscio, fratello di `.dpe-shell__body`. Resta nel sottoalbero DOM del
|
|
8047
|
+
* componente e non si trapianta in `document.body`: il top layer e' una promozione **visiva**, non
|
|
8048
|
+
* un trapianto, quindi gli eventi risalgono l'albero e il `Ctrl+S` del guscio continua a
|
|
8049
|
+
* funzionare.
|
|
8050
|
+
*
|
|
8051
|
+
* **Le modifiche si applicano dal vivo**: gli ispettori scrivono nello store a ogni `(change)`,
|
|
8052
|
+
* qui non esiste nessuna bozza locale e il piede ha «Chiudi», non «OK / Annulla». Una bozza
|
|
8053
|
+
* produrrebbe due nozioni di annullamento in disaccordo con ↶ e rilievi che descrivono un
|
|
8054
|
+
* documento diverso da quello mostrato. Conseguenza dichiarata in interfaccia: **chiudere non
|
|
8055
|
+
* annulla**.
|
|
8056
|
+
*
|
|
8057
|
+
* Il nodo **non arriva per input**: si rideriva da `selectedNodeName()`, cosi' se cambia sotto
|
|
8058
|
+
* (una rinomina) la dialog lo segue invece di guardare un nome che non esiste piu'.
|
|
8059
|
+
*/
|
|
8060
|
+
/**
|
|
8061
|
+
* Contatore di istanza per l'id dell'etichetta. Serve perche' `aria-labelledby` punta a un id, e
|
|
8062
|
+
* due editor nella stessa pagina sono due dialog: con un id fisso il documento avrebbe due
|
|
8063
|
+
* elementi con lo stesso id e il nome accessibile della seconda finestra sarebbe quello della
|
|
8064
|
+
* prima. Non e' una preferenza di stile — un id duplicato e' un difetto che solo uno screen
|
|
8065
|
+
* reader mostra.
|
|
8066
|
+
*/
|
|
8067
|
+
let nextLabelId = 0;
|
|
8068
|
+
class DpeNodeDialogComponent {
|
|
8069
|
+
store = inject(DpeDocumentStore);
|
|
8070
|
+
dialogRef = viewChild.required('dlg');
|
|
8071
|
+
labelId = `dpe-node-dialog-label-${(nextLabelId += 1)}`;
|
|
8072
|
+
/**
|
|
8073
|
+
* Il nodo corrente, **riderivato** dallo store come fa il dispatcher degli ispettori. Non si
|
|
8074
|
+
* conserva il nome: se il nodo viene rinominato, `selectedNodeName()` porta gia' il nome nuovo.
|
|
8075
|
+
*/
|
|
8076
|
+
located = computed(() => {
|
|
8077
|
+
const name = this.store.selectedNodeName();
|
|
8078
|
+
return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);
|
|
8079
|
+
}, ...(ngDevMode ? [{ debugName: "located" }] : []));
|
|
8080
|
+
categoryLabel = computed(() => {
|
|
8081
|
+
const entry = this.located();
|
|
8082
|
+
return entry ? dpeNodeCategory(entry.collection).label : '';
|
|
8083
|
+
}, ...(ngDevMode ? [{ debugName: "categoryLabel" }] : []));
|
|
8084
|
+
familyClass = computed(() => {
|
|
8085
|
+
const entry = this.located();
|
|
8086
|
+
return entry ? dpeNodeIconFamilyClass(entry.collection) : '';
|
|
8087
|
+
}, ...(ngDevMode ? [{ debugName: "familyClass" }] : []));
|
|
8088
|
+
/**
|
|
8089
|
+
* Vero quando il fondo ha ricevuto il `pointerdown` di questo clic. Vedi `onDialogClick`: la
|
|
8090
|
+
* verifica di coppia non e' prudenza, e' la correzione di un difetto reale.
|
|
8091
|
+
*/
|
|
8092
|
+
pointerDownWasOnBackdrop = false;
|
|
8093
|
+
constructor() {
|
|
8094
|
+
effect(() => {
|
|
8095
|
+
const el = this.dialogRef().nativeElement;
|
|
8096
|
+
const shouldBeOpen = this.store.isNodeEditorOpen() && this.located() !== undefined;
|
|
8097
|
+
// La guardia su `el.open` **non e' cosmetica**: `showModal()` su una dialog gia' aperta
|
|
8098
|
+
// lancia `InvalidStateError`, e questo effect riesegue a ogni mutazione del documento
|
|
8099
|
+
// (legge `located()`). Senza, aprire e modificare un campo basterebbe a far comparire
|
|
8100
|
+
// l'eccezione in console.
|
|
8101
|
+
if (shouldBeOpen && !el.open) {
|
|
8102
|
+
el.showModal();
|
|
8103
|
+
}
|
|
8104
|
+
else if (!shouldBeOpen && el.open) {
|
|
8105
|
+
el.close();
|
|
8106
|
+
}
|
|
8107
|
+
});
|
|
8108
|
+
}
|
|
8109
|
+
/**
|
|
8110
|
+
* **Esc.** Il browser chiede di chiudere e la finestra e' ancora aperta: la richiesta riguarda
|
|
8111
|
+
* per definizione la sessione corrente, quindi si chiude e non serve nessuna verifica.
|
|
8112
|
+
*
|
|
8113
|
+
* Perche' `cancel` e' legato, contro cio' che sembra sufficiente: in Chrome 151 l'evento `close`
|
|
8114
|
+
* **arriva molto in ritardo** (vedi `onClose`), quindi con il solo `close` lo store restava
|
|
8115
|
+
* «aperta» dopo Esc — e il sintomo era che **riaprire lo stesso nodo non faceva niente**: il
|
|
8116
|
+
* signal non cambiava, l'effect non rieseguiva, `showModal()` non veniva mai chiamato. Sul
|
|
8117
|
+
* bottone del piede non si vedeva, perche' quella via passa dallo store.
|
|
8118
|
+
*
|
|
8119
|
+
* `cancel` sarebbe annullabile: non si annulla. La chiusura dell'elemento arriva comunque dal
|
|
8120
|
+
* browser, e la guardia su `el.open` nell'effect regge in entrambi gli ordini.
|
|
8121
|
+
*/
|
|
8122
|
+
onCancel() {
|
|
8123
|
+
this.store.closeNodeEditor();
|
|
8124
|
+
}
|
|
8125
|
+
/**
|
|
8126
|
+
* **L'elemento si e' chiuso.** La guardia non e' prudenza: Chrome accoda `close` come task
|
|
8127
|
+
* dell'elemento e lo consegna anche **dopo** una riapertura — misurato nella demo, la traccia
|
|
8128
|
+
* degli eventi mostra `close` con `open: true` subito dopo un `showModal()`. Senza la guardia
|
|
8129
|
+
* quel `close` in ritardo chiudeva la finestra appena aperta, e il sintomo era una dialog che
|
|
8130
|
+
* «non si apre a volte»: sparisce da sola dopo essere comparsa, e con la finestra aperta si
|
|
8131
|
+
* riapriva solo alla prima modifica del documento (l'unica cosa che faceva rieseguire l'effect).
|
|
8132
|
+
*
|
|
8133
|
+
* Se l'elemento risulta **aperto**, questo `close` riguarda una sessione precedente e si ignora.
|
|
8134
|
+
* Se risulta chiuso, la notifica e' di questa sessione — e allineare qui lo store e' anche cio'
|
|
8135
|
+
* che ripara una divergenza, se il browser chiudesse la finestra per vie sue.
|
|
8136
|
+
*/
|
|
8137
|
+
onClose() {
|
|
8138
|
+
if (this.dialogRef().nativeElement.open) {
|
|
8139
|
+
return;
|
|
8140
|
+
}
|
|
8141
|
+
this.store.closeNodeEditor();
|
|
8142
|
+
}
|
|
8143
|
+
onPointerDown(event) {
|
|
8144
|
+
this.pointerDownWasOnBackdrop = event.target === this.dialogRef().nativeElement;
|
|
8145
|
+
}
|
|
8146
|
+
/**
|
|
8147
|
+
* Chiusura al clic sul fondo, con la **verifica di coppia** e non con il solo controllo sul
|
|
8148
|
+
* click. I clic sul velo sono recapitati all'elemento `<dialog>` stesso — ma anche un
|
|
8149
|
+
* trascinamento di selezione del testo che **inizia** dentro una casella e **finisce** fuori
|
|
8150
|
+
* produce un `click` con `target` uguale alla dialog. Con la sola verifica sul click, selezionare
|
|
8151
|
+
* del testo chiuderebbe la finestra.
|
|
8152
|
+
*/
|
|
8153
|
+
onDialogClick(event) {
|
|
8154
|
+
if (this.pointerDownWasOnBackdrop && event.target === this.dialogRef().nativeElement) {
|
|
8155
|
+
this.store.closeNodeEditor();
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8159
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DpeNodeDialogComponent, isStandalone: true, selector: "dpe-node-dialog", viewQueries: [{ propertyName: "dialogRef", first: true, predicate: ["dlg"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
8160
|
+
<!--
|
|
8161
|
+
L’elemento dialog sta **sempre** nel template, e solo il contenuto e’ condizionato: cosi’
|
|
8162
|
+
viewChild.required non puo’ fallire e l’effect che apre non deve difendersi da un riferimento
|
|
8163
|
+
assente. Una dialog vuota e chiusa non rende niente — il foglio dell’agente utente le da’
|
|
8164
|
+
display: none.
|
|
8165
|
+
-->
|
|
8166
|
+
<dialog
|
|
8167
|
+
#dlg
|
|
8168
|
+
class="dpe-dialog"
|
|
8169
|
+
[attr.aria-labelledby]="labelId"
|
|
8170
|
+
(close)="onClose()"
|
|
8171
|
+
(cancel)="onCancel()"
|
|
8172
|
+
(click)="onDialogClick($event)"
|
|
8173
|
+
(pointerdown)="onPointerDown($event)"
|
|
8174
|
+
>
|
|
8175
|
+
@if (located(); as entry) {
|
|
8176
|
+
<header class="dpe-dialog__head">
|
|
8177
|
+
<!--
|
|
8178
|
+
Quarto posto in cui compare il tipo di nodo (palette, card del canvas, ispettore, qui):
|
|
8179
|
+
stessa icona e stessa classe di famiglia, calcolate dalle stesse funzioni. Un tipo che
|
|
8180
|
+
cambia aspetto da un posto all’altro e’ un tipo che non si riconosce.
|
|
8181
|
+
-->
|
|
8182
|
+
<span class="dpe-dialog__icon" [class]="'dpe-dialog__icon ' + familyClass()">
|
|
8183
|
+
<dpe-node-icon [collection]="entry.collection" />
|
|
8184
|
+
</span>
|
|
8185
|
+
<strong class="dpe-dialog__label" [id]="labelId">
|
|
8186
|
+
{{ entry.node.label || entry.node.name }}
|
|
8187
|
+
</strong>
|
|
8188
|
+
<span class="dpe-dialog__category">{{ categoryLabel() }}</span>
|
|
8189
|
+
<code class="dpe-dialog__name">{{ entry.node.name }}</code>
|
|
8190
|
+
<button
|
|
8191
|
+
type="button"
|
|
8192
|
+
class="dpe-dialog__btn dpe-dialog__btn--icon"
|
|
8193
|
+
aria-label="Chiudi la finestra di modifica"
|
|
8194
|
+
title="Chiudi (Esc). Le modifiche sono già nel documento."
|
|
8195
|
+
(click)="store.closeNodeEditor()"
|
|
8196
|
+
>
|
|
8197
|
+
✕
|
|
8198
|
+
</button>
|
|
8199
|
+
</header>
|
|
8200
|
+
|
|
8201
|
+
<div class="dpe-dialog__body">
|
|
8202
|
+
<!-- Colonna larga: i campi. E’ la ragione per cui questa finestra esiste. -->
|
|
8203
|
+
<div class="dpe-dialog__main">
|
|
8204
|
+
<dpe-node-inspector />
|
|
8205
|
+
</div>
|
|
7959
8206
|
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
8207
|
+
<!--
|
|
8208
|
+
Colonna stretta: i fatti dal backend su **questo** nodo. Non entra cio’ che riguarda il
|
|
8209
|
+
grafo — diagnostiche di tutta la definizione, schema di tutti i nodi e SQL generato
|
|
8210
|
+
hanno gia’ le loro linguette nel pannello inferiore.
|
|
8211
|
+
-->
|
|
8212
|
+
<aside class="dpe-dialog__side">
|
|
8213
|
+
<dpe-node-facts [nodeName]="entry.node.name" />
|
|
8214
|
+
</aside>
|
|
8215
|
+
</div>
|
|
8216
|
+
|
|
8217
|
+
<footer class="dpe-dialog__foot">
|
|
8218
|
+
<p class="dpe-dialog__note">
|
|
8219
|
+
Le modifiche sono <strong>già nel documento</strong>: chiudere non le annulla. Si torna
|
|
8220
|
+
indietro con ↶ nella barra.
|
|
7965
8221
|
</p>
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
8222
|
+
<button
|
|
8223
|
+
type="button"
|
|
8224
|
+
class="dpe-dialog__btn dpe-dialog__btn--danger"
|
|
8225
|
+
title="Elimina il nodo: i riferimenti a valle restano e diventano riferimenti rotti. Si annulla con ↶."
|
|
8226
|
+
(click)="store.removeNode(entry.node.name)"
|
|
8227
|
+
>
|
|
8228
|
+
Elimina il nodo
|
|
8229
|
+
</button>
|
|
8230
|
+
<button type="button" class="dpe-dialog__btn" (click)="store.closeNodeEditor()">
|
|
8231
|
+
Chiudi
|
|
8232
|
+
</button>
|
|
8233
|
+
</footer>
|
|
8234
|
+
}
|
|
8235
|
+
</dialog>
|
|
8236
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DpeNodeFactsComponent, selector: "dpe-node-facts", inputs: ["nodeName"] }, { kind: "component", type: DpeNodeInspectorComponent, selector: "dpe-node-inspector" }, { kind: "component", type: DpeNodeIconComponent, selector: "dpe-node-icon", inputs: ["collection"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8237
|
+
}
|
|
8238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeNodeDialogComponent, decorators: [{
|
|
8239
|
+
type: Component,
|
|
8240
|
+
args: [{
|
|
8241
|
+
selector: 'dpe-node-dialog',
|
|
8242
|
+
standalone: true,
|
|
8243
|
+
imports: [DpeNodeFactsComponent, DpeNodeInspectorComponent, DpeNodeIconComponent],
|
|
8244
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
8245
|
+
template: `
|
|
8246
|
+
<!--
|
|
8247
|
+
L’elemento dialog sta **sempre** nel template, e solo il contenuto e’ condizionato: cosi’
|
|
8248
|
+
viewChild.required non puo’ fallire e l’effect che apre non deve difendersi da un riferimento
|
|
8249
|
+
assente. Una dialog vuota e chiusa non rende niente — il foglio dell’agente utente le da’
|
|
8250
|
+
display: none.
|
|
8251
|
+
-->
|
|
8252
|
+
<dialog
|
|
8253
|
+
#dlg
|
|
8254
|
+
class="dpe-dialog"
|
|
8255
|
+
[attr.aria-labelledby]="labelId"
|
|
8256
|
+
(close)="onClose()"
|
|
8257
|
+
(cancel)="onCancel()"
|
|
8258
|
+
(click)="onDialogClick($event)"
|
|
8259
|
+
(pointerdown)="onPointerDown($event)"
|
|
8260
|
+
>
|
|
8261
|
+
@if (located(); as entry) {
|
|
8262
|
+
<header class="dpe-dialog__head">
|
|
8263
|
+
<!--
|
|
8264
|
+
Quarto posto in cui compare il tipo di nodo (palette, card del canvas, ispettore, qui):
|
|
8265
|
+
stessa icona e stessa classe di famiglia, calcolate dalle stesse funzioni. Un tipo che
|
|
8266
|
+
cambia aspetto da un posto all’altro e’ un tipo che non si riconosce.
|
|
8267
|
+
-->
|
|
8268
|
+
<span class="dpe-dialog__icon" [class]="'dpe-dialog__icon ' + familyClass()">
|
|
8269
|
+
<dpe-node-icon [collection]="entry.collection" />
|
|
8270
|
+
</span>
|
|
8271
|
+
<strong class="dpe-dialog__label" [id]="labelId">
|
|
8272
|
+
{{ entry.node.label || entry.node.name }}
|
|
8273
|
+
</strong>
|
|
8274
|
+
<span class="dpe-dialog__category">{{ categoryLabel() }}</span>
|
|
8275
|
+
<code class="dpe-dialog__name">{{ entry.node.name }}</code>
|
|
8276
|
+
<button
|
|
8277
|
+
type="button"
|
|
8278
|
+
class="dpe-dialog__btn dpe-dialog__btn--icon"
|
|
8279
|
+
aria-label="Chiudi la finestra di modifica"
|
|
8280
|
+
title="Chiudi (Esc). Le modifiche sono già nel documento."
|
|
8281
|
+
(click)="store.closeNodeEditor()"
|
|
8282
|
+
>
|
|
8283
|
+
✕
|
|
8284
|
+
</button>
|
|
8285
|
+
</header>
|
|
8286
|
+
|
|
8287
|
+
<div class="dpe-dialog__body">
|
|
8288
|
+
<!-- Colonna larga: i campi. E’ la ragione per cui questa finestra esiste. -->
|
|
8289
|
+
<div class="dpe-dialog__main">
|
|
8290
|
+
<dpe-node-inspector />
|
|
7977
8291
|
</div>
|
|
7978
|
-
<p class="dpe-field__hint">
|
|
7979
|
-
Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo
|
|
7980
|
-
calcoliamo qui.
|
|
7981
|
-
</p>
|
|
7982
|
-
}
|
|
7983
|
-
</section>
|
|
7984
8292
|
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
8293
|
+
<!--
|
|
8294
|
+
Colonna stretta: i fatti dal backend su **questo** nodo. Non entra cio’ che riguarda il
|
|
8295
|
+
grafo — diagnostiche di tutta la definizione, schema di tutti i nodi e SQL generato
|
|
8296
|
+
hanno gia’ le loro linguette nel pannello inferiore.
|
|
8297
|
+
-->
|
|
8298
|
+
<aside class="dpe-dialog__side">
|
|
8299
|
+
<dpe-node-facts [nodeName]="entry.node.name" />
|
|
8300
|
+
</aside>
|
|
8301
|
+
</div>
|
|
8302
|
+
|
|
8303
|
+
<footer class="dpe-dialog__foot">
|
|
8304
|
+
<p class="dpe-dialog__note">
|
|
8305
|
+
Le modifiche sono <strong>già nel documento</strong>: chiudere non le annulla. Si torna
|
|
8306
|
+
indietro con ↶ nella barra.
|
|
8307
|
+
</p>
|
|
8308
|
+
<button
|
|
8309
|
+
type="button"
|
|
8310
|
+
class="dpe-dialog__btn dpe-dialog__btn--danger"
|
|
8311
|
+
title="Elimina il nodo: i riferimenti a valle restano e diventano riferimenti rotti. Si annulla con ↶."
|
|
8312
|
+
(click)="store.removeNode(entry.node.name)"
|
|
8313
|
+
>
|
|
8314
|
+
Elimina il nodo
|
|
8315
|
+
</button>
|
|
8316
|
+
<button type="button" class="dpe-dialog__btn" (click)="store.closeNodeEditor()">
|
|
8317
|
+
Chiudi
|
|
8318
|
+
</button>
|
|
8319
|
+
</footer>
|
|
7998
8320
|
}
|
|
7999
|
-
|
|
8000
|
-
<p class="dpe-empty">Nessun nodo selezionato.</p>
|
|
8001
|
-
}
|
|
8321
|
+
</dialog>
|
|
8002
8322
|
`,
|
|
8003
8323
|
}]
|
|
8004
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
8324
|
+
}], ctorParameters: () => [], propDecorators: { dialogRef: [{ type: i0.ViewChild, args: ['dlg', { isSignal: true }] }] } });
|
|
8005
8325
|
|
|
8006
8326
|
/**
|
|
8007
8327
|
* La palette dei nodi.
|
|
@@ -8649,6 +8969,8 @@ class DpeBuilderComponent {
|
|
|
8649
8969
|
this.store.isDirty() &&
|
|
8650
8970
|
this.hasKey(), ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
8651
8971
|
nodeCount = computed(() => dpeAllNodes(this.store.definition()).length, ...(ngDevMode ? [{ debugName: "nodeCount" }] : []));
|
|
8972
|
+
/** I nomi selezionati: la striscia contestuale compare da due in su. */
|
|
8973
|
+
selectedNames = computed(() => this.store.selectedNodeNames(), ...(ngDevMode ? [{ debugName: "selectedNames" }] : []));
|
|
8652
8974
|
constructor() {
|
|
8653
8975
|
// Caricamento: si reagisce all'**identita'** dell'input, non al suo contenuto. Lo store
|
|
8654
8976
|
// diventa la fonte di verita' da qui in poi.
|
|
@@ -8709,6 +9031,28 @@ class DpeBuilderComponent {
|
|
|
8709
9031
|
// mouse subito dopo averlo rilasciato e' un passaggio inutile.
|
|
8710
9032
|
this.store.select(name);
|
|
8711
9033
|
}
|
|
9034
|
+
/**
|
|
9035
|
+
* Doppio clic sul nodo, o bottone `✎` sulla card: i due gesti che il canvas emetteva su
|
|
9036
|
+
* `nodeOpened` e che **nessuno ascoltava** — il bottone non produceva nemmeno la selezione.
|
|
9037
|
+
*
|
|
9038
|
+
* Due cose, in quest'ordine. Si apre la dialog (che collassa la selezione a quel nodo) e si
|
|
9039
|
+
* **inquadra** il nodo: sotto il velo non si vede, ma alla chiusura si torna guardando il nodo su
|
|
9040
|
+
* cui si e' lavorato invece del punto dov'era il grafo prima.
|
|
9041
|
+
*/
|
|
9042
|
+
onNodeOpened(name) {
|
|
9043
|
+
this.store.openNodeEditor(name);
|
|
9044
|
+
this.canvas()?.revealNode(name);
|
|
9045
|
+
}
|
|
9046
|
+
/**
|
|
9047
|
+
* Elimina i nodi selezionati (striscia della selezione multipla). Si itera su una **copia** dei
|
|
9048
|
+
* nomi: `removeNode` pota la selezione a ogni giro, e scorrere il signal mentre lo si svuota
|
|
9049
|
+
* salterebbe la meta' dei nodi.
|
|
9050
|
+
*/
|
|
9051
|
+
removeSelectedNodes() {
|
|
9052
|
+
for (const name of [...this.selectedNames()]) {
|
|
9053
|
+
this.store.removeNode(name);
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
8712
9056
|
/** Clic sulla palette (via tastiera compresa): il nodo nasce in un punto libero e prevedibile. */
|
|
8713
9057
|
onNodeRequested(collection) {
|
|
8714
9058
|
const existing = this.store.positions();
|
|
@@ -8762,18 +9106,18 @@ class DpeBuilderComponent {
|
|
|
8762
9106
|
this.store.markSaved();
|
|
8763
9107
|
}
|
|
8764
9108
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
9109
|
+
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 @if (selectedNames().length > 1) {\r\n <!--\r\n Striscia contestuale della **selezione multipla**. Sta qui, fra i banner di documento e il\r\n corpo, perche' e' un fatto che riguarda l\u2019insieme e non un nodo \u2014 ed e' dove l\u2019occhio guarda\r\n per i fatti dell\u2019insieme. Non mostra i campi di un nodo: mostrarli mentre N sono evidenziati\r\n e' la stessa bugia della doppia evidenziazione, detta piu' piano.\r\n -->\r\n <div class=\"dpe-shell__multi\">\r\n <div class=\"dpe-shell__multi-head\">\r\n <strong>{{ selectedNames().length }} nodi selezionati</strong>\r\n <ul class=\"dpe-multi__names\">\r\n @for (name of selectedNames(); track $index) {\r\n <li class=\"dpe-multi__name\">{{ name }}</li>\r\n }\r\n </ul>\r\n </div>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"removeSelectedNodes()\">\r\n Elimina i {{ selectedNames().length }} nodi\r\n </button>\r\n <!--\r\n Il riordino e\u2019 dichiaratamente **dell\u2019intero grafo**: dagre su un sottografo sarebbe una\r\n funzione nuova, e chiamarla \u00ABriordina i selezionati\u00BB mentre riordina tutto sarebbe una\r\n bugia \u2014 la stessa classe di difetto che questa striscia esiste per evitare.\r\n -->\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"runAutoLayout()\">\r\n Riordina il grafo\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-note dpe-note--warning\">\r\n L\u2019eliminazione lascia i riferimenti a valle cos\u00EC come sono: diventano riferimenti rotti, e\r\n il backend li segnala. Si annulla con \u21B6.\r\n </p>\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 (nodeOpened)=\"onNodeOpened($event)\"\r\n />\r\n </div>\r\n\r\n <!--\r\n **Una sola istanza**, fratello di `.dpe-shell__body`: non dentro il canvas (finirebbe nella\r\n trasformazione CSS di `f-canvas`) e non dentro il corpo (partecipererebbe alla griglia). Non\r\n partecipa al layout: `showModal()` la promuove nel top layer, quindi aprirla non produce\r\n nessun reflow del canvas e non c'e' nessuna inquadratura da rifare.\r\n -->\r\n <dpe-node-dialog />\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: DpeNodeDialogComponent, selector: "dpe-node-dialog" }, { 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 });
|
|
8766
9110
|
}
|
|
8767
9111
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DpeBuilderComponent, decorators: [{
|
|
8768
9112
|
type: Component,
|
|
8769
9113
|
args: [{ selector: 'dpe-builder', standalone: true, imports: [
|
|
8770
9114
|
DpeCanvasComponent,
|
|
8771
9115
|
DpeDiagnosticsPanelComponent,
|
|
8772
|
-
|
|
9116
|
+
DpeNodeDialogComponent,
|
|
8773
9117
|
DpeNodePaletteComponent,
|
|
8774
9118
|
DpeSchemaPanelComponent,
|
|
8775
9119
|
DpeSqlPreviewComponent,
|
|
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" }]
|
|
9120
|
+
], 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 @if (selectedNames().length > 1) {\r\n <!--\r\n Striscia contestuale della **selezione multipla**. Sta qui, fra i banner di documento e il\r\n corpo, perche' e' un fatto che riguarda l\u2019insieme e non un nodo \u2014 ed e' dove l\u2019occhio guarda\r\n per i fatti dell\u2019insieme. Non mostra i campi di un nodo: mostrarli mentre N sono evidenziati\r\n e' la stessa bugia della doppia evidenziazione, detta piu' piano.\r\n -->\r\n <div class=\"dpe-shell__multi\">\r\n <div class=\"dpe-shell__multi-head\">\r\n <strong>{{ selectedNames().length }} nodi selezionati</strong>\r\n <ul class=\"dpe-multi__names\">\r\n @for (name of selectedNames(); track $index) {\r\n <li class=\"dpe-multi__name\">{{ name }}</li>\r\n }\r\n </ul>\r\n </div>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"removeSelectedNodes()\">\r\n Elimina i {{ selectedNames().length }} nodi\r\n </button>\r\n <!--\r\n Il riordino e\u2019 dichiaratamente **dell\u2019intero grafo**: dagre su un sottografo sarebbe una\r\n funzione nuova, e chiamarla \u00ABriordina i selezionati\u00BB mentre riordina tutto sarebbe una\r\n bugia \u2014 la stessa classe di difetto che questa striscia esiste per evitare.\r\n -->\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"runAutoLayout()\">\r\n Riordina il grafo\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-note dpe-note--warning\">\r\n L\u2019eliminazione lascia i riferimenti a valle cos\u00EC come sono: diventano riferimenti rotti, e\r\n il backend li segnala. Si annulla con \u21B6.\r\n </p>\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 (nodeOpened)=\"onNodeOpened($event)\"\r\n />\r\n </div>\r\n\r\n <!--\r\n **Una sola istanza**, fratello di `.dpe-shell__body`: non dentro il canvas (finirebbe nella\r\n trasformazione CSS di `f-canvas`) e non dentro il corpo (partecipererebbe alla griglia). Non\r\n partecipa al layout: `showModal()` la promuove nel top layer, quindi aprirla non produce\r\n nessun reflow del canvas e non c'e' nessuna inquadratura da rifare.\r\n -->\r\n <dpe-node-dialog />\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" }]
|
|
8777
9121
|
}], 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 }] }] } });
|
|
8778
9122
|
|
|
8779
9123
|
/**
|
|
@@ -8939,5 +9283,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
8939
9283
|
* Generated bundle index. Do not edit.
|
|
8940
9284
|
*/
|
|
8941
9285
|
|
|
8942
|
-
export { DPE_API_ERROR_FALLBACK_MESSAGE, DPE_BUILDER_HTTP_CONFIG, DPE_DIALECT_LABELS, DPE_LAYOUT_KEY, DPE_NODE_CATEGORIES, DPE_NODE_COLLECTIONS, DPE_NODE_HEIGHT, DPE_NODE_ICON_PATHS, DPE_NODE_NOT_COMPILABLE, DPE_SOURCE_SLOTS, DpeApiError, DpeBuilderApi, DpeBuilderComponent, DpeCanvasComponent, DpeCatalogComponent, DpeCatalogStore, DpeColumnTypeEditorComponent, DpeDiagnosticsPanelComponent, DpeDocumentStore, DpeExpressionEditorComponent, DpeLayoutService, DpeMetadataStore, DpeNodeIconComponent, DpeNodeInspectorBase, DpeNodeInspectorComponent, DpeNodePaletteComponent, DpeSchemaPanelComponent, DpeSelectValueDirective, DpeSqlPreviewComponent, DpeValidationService, HttpDpeBuilderApi, diagnosticPathToString, dpeAllNodes, dpeBuildGraph, dpeCountReferences, dpeCreateCommandAvailability, dpeCreateNode, dpeDefinitionLevelDiagnostics, dpeDiagnosticField, dpeDiagnosticLocationLabel, dpeDiagnosticNodeName, dpeEdgeId, dpeEmptyDefinition, dpeFindNode, dpeGroupDiagnosticsByNode, dpeHasCompleteLayout, dpeInputPortId, dpeInputPorts, dpeIsDialectDiagnostic, dpeIsNotCompilable, dpeIsPrimitiveAvailable, dpeIsRectFullyVisible, dpeKnownNodeNames, dpeNodeCategory, dpeNodeIconFamily, dpeNodeIconFamilyClass, dpeNodeIconPaths, dpeNodeId, dpeNodeSourceReferences, dpeNodeSubtitle, dpeNodeWidth, dpeNodeWidthClass, dpeOutputPortId, dpeParseInputPortId, dpeParseNodeId, dpeParseOutputPortId, dpeProposeNodeName, dpeProposeWritebackSequence, dpeReadCapabilities, dpeReadNodePosition, dpeRenameNode, dpeWorstSeverity, dpeWriteNodePosition, dpeWriteSourceReference, provideDpeBuilderHttpApi };
|
|
9286
|
+
export { DPE_API_ERROR_FALLBACK_MESSAGE, DPE_BUILDER_HTTP_CONFIG, DPE_DIALECT_LABELS, DPE_LAYOUT_KEY, DPE_NODE_CATEGORIES, DPE_NODE_COLLECTIONS, DPE_NODE_HEIGHT, DPE_NODE_ICON_PATHS, DPE_NODE_NOT_COMPILABLE, DPE_SOURCE_SLOTS, DpeApiError, DpeBuilderApi, DpeBuilderComponent, DpeCanvasComponent, DpeCatalogComponent, DpeCatalogStore, DpeColumnTypeEditorComponent, DpeDiagnosticsPanelComponent, DpeDocumentStore, DpeExpressionEditorComponent, DpeLayoutService, DpeMetadataStore, DpeNodeDialogComponent, DpeNodeFactsComponent, DpeNodeIconComponent, DpeNodeInspectorBase, DpeNodeInspectorComponent, DpeNodePaletteComponent, DpeSchemaPanelComponent, DpeSelectValueDirective, DpeSqlPreviewComponent, DpeValidationService, HttpDpeBuilderApi, diagnosticPathToString, dpeAllNodes, dpeBuildGraph, dpeCountReferences, dpeCreateCommandAvailability, dpeCreateNode, dpeDefinitionLevelDiagnostics, dpeDiagnosticField, dpeDiagnosticLocationLabel, dpeDiagnosticNodeName, dpeEdgeId, dpeEmptyDefinition, dpeFindNode, dpeGroupDiagnosticsByNode, dpeHasCompleteLayout, dpeInputPortId, dpeInputPorts, dpeIsDialectDiagnostic, dpeIsNotCompilable, dpeIsPrimitiveAvailable, dpeIsRectFullyVisible, dpeKnownNodeNames, dpeNodeCategory, dpeNodeIconFamily, dpeNodeIconFamilyClass, dpeNodeIconPaths, dpeNodeId, dpeNodeSourceReferences, dpeNodeSubtitle, dpeNodeWidth, dpeNodeWidthClass, dpeOutputPortId, dpeParseInputPortId, dpeParseNodeId, dpeParseOutputPortId, dpeProposeNodeName, dpeProposeWritebackSequence, dpeReadCapabilities, dpeReadNodePosition, dpeRenameNode, dpeWorstSeverity, dpeWriteNodePosition, dpeWriteSourceReference, provideDpeBuilderHttpApi };
|
|
8943
9287
|
//# sourceMappingURL=esfaenza-dpe-builder.mjs.map
|