@myrmidon/gve-core 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -77
- package/esm2022/lib/components/animation-timeline/animation-timeline.component.mjs +50 -19
- package/esm2022/lib/components/animation-timeline-set/animation-timeline-set.component.mjs +32 -8
- package/esm2022/lib/components/animation-tween/animation-tween.component.mjs +48 -25
- package/esm2022/lib/components/base-text-char/base-text-char.component.mjs +13 -4
- package/esm2022/lib/components/base-text-editor/base-text-editor.component.mjs +10 -6
- package/esm2022/lib/components/base-text-view/base-text-view.component.mjs +39 -5
- package/esm2022/lib/components/chain-operation-editor/chain-operation-editor.component.mjs +97 -39
- package/esm2022/lib/components/chain-result-view/chain-result-view.component.mjs +44 -12
- package/esm2022/lib/components/feature-editor/feature-editor.component.mjs +27 -12
- package/esm2022/lib/components/feature-set-editor/feature-set-editor.component.mjs +32 -9
- package/esm2022/lib/components/feature-set-view/feature-set-view.component.mjs +28 -8
- package/esm2022/lib/components/ln-heights-editor/ln-heights-editor.component.mjs +38 -7
- package/esm2022/lib/components/operation-source-editor/operation-source-editor.component.mjs +29 -4
- package/esm2022/lib/components/simple-tree/simple-tree.component.mjs +20 -4
- package/esm2022/lib/components/snapshot-editor/snapshot-editor.component.mjs +419 -114
- package/esm2022/lib/components/steps-map/steps-map.component.mjs +31 -9
- package/esm2022/lib/services/gve-api.service.mjs +3 -3
- package/esm2022/lib/services/settings.service.mjs +6 -5
- package/esm2022/lib/validators/svg-validators.mjs +7 -1
- package/esm2022/public-api.mjs +1 -2
- package/fesm2022/myrmidon-gve-core.mjs +947 -388
- package/fesm2022/myrmidon-gve-core.mjs.map +1 -1
- package/lib/components/animation-timeline/animation-timeline.component.d.ts +31 -9
- package/lib/components/animation-timeline-set/animation-timeline-set.component.d.ts +28 -4
- package/lib/components/animation-tween/animation-tween.component.d.ts +25 -12
- package/lib/components/base-text-char/base-text-char.component.d.ts +16 -0
- package/lib/components/base-text-editor/base-text-editor.component.d.ts +5 -1
- package/lib/components/base-text-view/base-text-view.component.d.ts +37 -0
- package/lib/components/chain-operation-editor/chain-operation-editor.component.d.ts +53 -5
- package/lib/components/chain-result-view/chain-result-view.component.d.ts +17 -3
- package/lib/components/feature-editor/feature-editor.component.d.ts +20 -6
- package/lib/components/feature-set-editor/feature-set-editor.component.d.ts +28 -5
- package/lib/components/feature-set-view/feature-set-view.component.d.ts +23 -3
- package/lib/components/ln-heights-editor/ln-heights-editor.component.d.ts +22 -0
- package/lib/components/operation-source-editor/operation-source-editor.component.d.ts +31 -0
- package/lib/components/simple-tree/simple-tree.component.d.ts +19 -0
- package/lib/components/snapshot-editor/snapshot-editor.component.d.ts +178 -15
- package/lib/components/steps-map/steps-map.component.d.ts +20 -3
- package/lib/services/settings.service.d.ts +2 -1
- package/lib/validators/svg-validators.d.ts +6 -0
- package/package.json +4 -4
- package/public-api.d.ts +0 -1
- package/esm2022/lib/components/animation-vars/animation-vars.component.mjs +0 -141
- package/lib/components/animation-vars/animation-vars.component.d.ts +0 -30
|
@@ -6,13 +6,13 @@ import * as i2 from '@angular/common';
|
|
|
6
6
|
import { CommonModule } from '@angular/common';
|
|
7
7
|
import * as i3 from '@angular/material/button';
|
|
8
8
|
import { MatButtonModule } from '@angular/material/button';
|
|
9
|
-
import * as i4
|
|
9
|
+
import * as i4 from '@angular/material/checkbox';
|
|
10
10
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
11
|
-
import * as
|
|
11
|
+
import * as i8$1 from '@angular/material/expansion';
|
|
12
12
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
13
|
-
import * as
|
|
13
|
+
import * as i5 from '@angular/material/form-field';
|
|
14
14
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
15
|
-
import * as i5 from '@angular/material/icon';
|
|
15
|
+
import * as i5$1 from '@angular/material/icon';
|
|
16
16
|
import { MatIconModule } from '@angular/material/icon';
|
|
17
17
|
import * as i6 from '@angular/material/input';
|
|
18
18
|
import { MatInputModule } from '@angular/material/input';
|
|
@@ -24,151 +24,36 @@ import * as i10 from '@angular/material/tooltip';
|
|
|
24
24
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
25
25
|
import * as i2$1 from '@myrmidon/ng-tools';
|
|
26
26
|
import { NgToolsValidators, NgToolsModule, deepCopy } from '@myrmidon/ng-tools';
|
|
27
|
-
import { GveAnimationTweenType, SnapshotViewService, FeatureSetPolicy, OperationType, DEFAULT_SVG_BASE_TEXT_OPTIONS } from '@myrmidon/gve-snapshot-view';
|
|
28
27
|
import { debounceTime, distinctUntilChanged, catchError } from 'rxjs';
|
|
29
28
|
import * as i9 from '@angular/material/core';
|
|
30
29
|
import { MatRippleModule } from '@angular/material/core';
|
|
30
|
+
import { SnapshotViewService, FeatureSetPolicy, OperationType, DEFAULT_SVG_BASE_TEXT_OPTIONS } from '@myrmidon/gve-snapshot-view';
|
|
31
31
|
import * as i2$2 from '@myrmidon/ng-mat-tools';
|
|
32
32
|
import { NgMatToolsModule } from '@myrmidon/ng-mat-tools';
|
|
33
33
|
import { filter, debounceTime as debounceTime$1 } from 'rxjs/operators';
|
|
34
34
|
import * as i2$3 from '@angular/cdk/clipboard';
|
|
35
35
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
|
36
|
-
import * as
|
|
36
|
+
import * as i16 from '@cisstech/nge/monaco';
|
|
37
37
|
import { NgeMonacoModule } from '@cisstech/nge/monaco';
|
|
38
38
|
import { customAlphabet } from 'nanoid';
|
|
39
39
|
import * as i7 from '@angular/material/button-toggle';
|
|
40
40
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
41
41
|
import * as i12 from '@angular/material/progress-bar';
|
|
42
42
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
43
|
-
import * as i4$
|
|
43
|
+
import * as i4$1 from '@angular/material/snack-bar';
|
|
44
44
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
45
45
|
import * as i1$1 from '@angular/common/http';
|
|
46
46
|
|
|
47
|
-
class AnimationVarsComponent {
|
|
48
|
-
get vars() {
|
|
49
|
-
return this._vars;
|
|
50
|
-
}
|
|
51
|
-
set vars(value) {
|
|
52
|
-
if (this._vars === value) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
this._vars = value;
|
|
56
|
-
this.updateForm(value);
|
|
57
|
-
}
|
|
58
|
-
constructor(formBuilder) {
|
|
59
|
-
this._vars = {};
|
|
60
|
-
this.varsChange = new EventEmitter();
|
|
61
|
-
this.editedVars = [];
|
|
62
|
-
this.name = formBuilder.control('', {
|
|
63
|
-
nonNullable: true,
|
|
64
|
-
validators: [Validators.required, Validators.maxLength(100)],
|
|
65
|
-
});
|
|
66
|
-
this.value = formBuilder.control('', {
|
|
67
|
-
nonNullable: true,
|
|
68
|
-
validators: [Validators.required, Validators.maxLength(1000)],
|
|
69
|
-
});
|
|
70
|
-
this.form = formBuilder.group({
|
|
71
|
-
name: this.name,
|
|
72
|
-
value: this.value,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
updateForm(vars) {
|
|
76
|
-
this.editedVars = Object.entries(vars)
|
|
77
|
-
.map(([name, value]) => ({ name, value }))
|
|
78
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
79
|
-
}
|
|
80
|
-
getVars() {
|
|
81
|
-
return this.editedVars.reduce((acc, { name, value }) => ({ ...acc, [name]: value }), {});
|
|
82
|
-
}
|
|
83
|
-
addVar() {
|
|
84
|
-
this.form.reset();
|
|
85
|
-
// focus the name input
|
|
86
|
-
setTimeout(() => this.nameInput?.nativeElement.focus(), 0);
|
|
87
|
-
}
|
|
88
|
-
editVar(index) {
|
|
89
|
-
const { name, value } = this.editedVars[index];
|
|
90
|
-
this.form.setValue({ name, value });
|
|
91
|
-
this.form.markAsPristine();
|
|
92
|
-
// focus the name input
|
|
93
|
-
setTimeout(() => this.nameInput?.nativeElement.focus(), 0);
|
|
94
|
-
}
|
|
95
|
-
deleteVar(index) {
|
|
96
|
-
this.editedVars = this.editedVars.filter((_, i) => i !== index);
|
|
97
|
-
this.varsChange.emit(this.getVars());
|
|
98
|
-
}
|
|
99
|
-
parseValue(value) {
|
|
100
|
-
if (!value) {
|
|
101
|
-
return '';
|
|
102
|
-
}
|
|
103
|
-
if (value === 'true') {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
else if (value === 'false') {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
else if (/^\d+$/.test(value)) {
|
|
110
|
-
return parseInt(value, 10);
|
|
111
|
-
}
|
|
112
|
-
else if (/^\d+\.\d+$/.test(value)) {
|
|
113
|
-
return parseFloat(value);
|
|
114
|
-
}
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
117
|
-
saveVar() {
|
|
118
|
-
if (this.form.invalid) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const { name, value } = this.form.value;
|
|
122
|
-
// parse value assuming that if it looks like a boolean/number
|
|
123
|
-
// its value should be a boolean/number rather than a string
|
|
124
|
-
const v = this.parseValue(value);
|
|
125
|
-
const editedVars = [...this.editedVars];
|
|
126
|
-
// if name already exists, update the value; else add it
|
|
127
|
-
const existingIndex = this.editedVars.findIndex((vr) => vr.name === name);
|
|
128
|
-
if (existingIndex !== -1) {
|
|
129
|
-
editedVars[existingIndex] = { name, value: v };
|
|
130
|
-
this.editedVars = editedVars;
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
editedVars.push({ name, value: v });
|
|
135
|
-
this.editedVars = editedVars.sort((a, b) => a.name.localeCompare(b.name));
|
|
136
|
-
}
|
|
137
|
-
this.varsChange.emit(this.getVars());
|
|
138
|
-
setTimeout(() => {
|
|
139
|
-
this.nameInput?.nativeElement.focus();
|
|
140
|
-
this.form.reset();
|
|
141
|
-
this.form.markAsPristine();
|
|
142
|
-
this.form.updateValueAndValidity();
|
|
143
|
-
}, 0);
|
|
144
|
-
}
|
|
145
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: AnimationVarsComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
146
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.6", type: AnimationVarsComponent, isStandalone: true, selector: "gve-animation-vars", inputs: { vars: "vars" }, outputs: { varsChange: "varsChange" }, viewQueries: [{ propertyName: "nameInput", first: true, predicate: ["vname"], descendants: true, static: true }], ngImport: i0, template: "<div>\r\n <div>\r\n <button type=\"button\" mat-raised-button color=\"primary\" (click)=\"addVar()\">\r\n <mat-icon>add</mat-icon>\r\n property\r\n </button>\r\n </div>\r\n\r\n @if (editedVars.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>name</th>\r\n <th>value</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (v of editedVars; track v.name; let index=$index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editVar(index)\"\r\n matTooltip=\"Edit this property\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteVar(index)\"\r\n matTooltip=\"Delete this property\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ v.name }}</td>\r\n <td>{{ v.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n } @else {\r\n <p class=\"muted\">(no vars)</p>\r\n }\r\n\r\n <form [formGroup]=\"form\" (submit)=\"saveVar()\">\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- name -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <input matInput [formControl]=\"name\" #vname />\r\n <mat-error\r\n *ngIf=\"$any(name).errors?.required && (name.dirty || name.touched)\"\r\n >name required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(name).errors?.maxLength && (name.dirty || name.touched)\"\r\n >name too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- value -->\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(value).errors?.required && (value.dirty || value.touched)\r\n \"\r\n >value required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(value).errors?.maxLength && (value.dirty || value.touched)\r\n \"\r\n >value too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <button\r\n type=\"submit\"\r\n mat-icon-button\r\n color=\"primary\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">add_circle</mat-icon>\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}fieldset{border:1px solid silver;border-radius:4px;padding:8px;margin:8px 0}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
147
|
-
}
|
|
148
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: AnimationVarsComponent, decorators: [{
|
|
149
|
-
type: Component,
|
|
150
|
-
args: [{ selector: 'gve-animation-vars', standalone: true, imports: [
|
|
151
|
-
CommonModule,
|
|
152
|
-
ReactiveFormsModule,
|
|
153
|
-
MatButtonModule,
|
|
154
|
-
MatCheckboxModule,
|
|
155
|
-
MatFormFieldModule,
|
|
156
|
-
MatIconModule,
|
|
157
|
-
MatInputModule,
|
|
158
|
-
MatSelectModule,
|
|
159
|
-
MatTooltipModule,
|
|
160
|
-
], template: "<div>\r\n <div>\r\n <button type=\"button\" mat-raised-button color=\"primary\" (click)=\"addVar()\">\r\n <mat-icon>add</mat-icon>\r\n property\r\n </button>\r\n </div>\r\n\r\n @if (editedVars.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>name</th>\r\n <th>value</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (v of editedVars; track v.name; let index=$index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editVar(index)\"\r\n matTooltip=\"Edit this property\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteVar(index)\"\r\n matTooltip=\"Delete this property\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ v.name }}</td>\r\n <td>{{ v.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n } @else {\r\n <p class=\"muted\">(no vars)</p>\r\n }\r\n\r\n <form [formGroup]=\"form\" (submit)=\"saveVar()\">\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- name -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <input matInput [formControl]=\"name\" #vname />\r\n <mat-error\r\n *ngIf=\"$any(name).errors?.required && (name.dirty || name.touched)\"\r\n >name required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(name).errors?.maxLength && (name.dirty || name.touched)\"\r\n >name too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- value -->\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(value).errors?.required && (value.dirty || value.touched)\r\n \"\r\n >value required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(value).errors?.maxLength && (value.dirty || value.touched)\r\n \"\r\n >value too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <button\r\n type=\"submit\"\r\n mat-icon-button\r\n color=\"primary\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">add_circle</mat-icon>\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}fieldset{border:1px solid silver;border-radius:4px;padding:8px;margin:8px 0}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"] }]
|
|
161
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { vars: [{
|
|
162
|
-
type: Input
|
|
163
|
-
}], varsChange: [{
|
|
164
|
-
type: Output
|
|
165
|
-
}], nameInput: [{
|
|
166
|
-
type: ViewChild,
|
|
167
|
-
args: ['vname', { static: true }]
|
|
168
|
-
}] } });
|
|
169
|
-
|
|
170
47
|
/**
|
|
48
|
+
* 🔑 `gve-animation-tween`
|
|
49
|
+
*
|
|
171
50
|
* A component to edit an animation tween.
|
|
51
|
+
* Used by the `gve-animation-timeline` component.
|
|
52
|
+
*
|
|
53
|
+
* - ▶️ `tween` (`GveAnimationTween`): the tween to edit.
|
|
54
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected by the tween.
|
|
55
|
+
* - 🔥 `tweenChange` (`GveAnimationTween`): emitted when the tween is changed.
|
|
56
|
+
* - 🔥 `tweenCancel` (`void`): emitted when the user cancels the edit.
|
|
172
57
|
*/
|
|
173
58
|
class AnimationTweenComponent {
|
|
174
59
|
/**
|
|
@@ -185,25 +70,30 @@ class AnimationTweenComponent {
|
|
|
185
70
|
this.updateForm(this._tween);
|
|
186
71
|
}
|
|
187
72
|
constructor(formBuilder) {
|
|
73
|
+
/**
|
|
74
|
+
* Emitted when the tween is changed.
|
|
75
|
+
*/
|
|
188
76
|
this.tweenChange = new EventEmitter();
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
];
|
|
77
|
+
/**
|
|
78
|
+
* Emitted when the user cancels the edit.
|
|
79
|
+
*/
|
|
80
|
+
this.tweenCancel = new EventEmitter();
|
|
81
|
+
this.types = ['to', 'from', 'fromTo', 'set'];
|
|
195
82
|
this.label = formBuilder.control('', {
|
|
196
83
|
nonNullable: true,
|
|
197
84
|
validators: [Validators.required, Validators.maxLength(100)],
|
|
198
85
|
});
|
|
199
86
|
this.note = formBuilder.control(null);
|
|
200
|
-
this.type = formBuilder.control(
|
|
87
|
+
this.type = formBuilder.control('to', { nonNullable: true });
|
|
201
88
|
this.selector = formBuilder.control('', {
|
|
202
89
|
nonNullable: true,
|
|
203
90
|
validators: [Validators.required, Validators.maxLength(200)],
|
|
204
91
|
});
|
|
205
|
-
this.vars = formBuilder.control({}, {
|
|
206
|
-
|
|
92
|
+
this.vars = formBuilder.control('{}', {
|
|
93
|
+
nonNullable: true,
|
|
94
|
+
validators: [Validators.required, this.jsonValidator],
|
|
95
|
+
});
|
|
96
|
+
this.vars2 = formBuilder.control(null, this.jsonValidator);
|
|
207
97
|
this.position = formBuilder.control(null, Validators.maxLength(100));
|
|
208
98
|
this.form = formBuilder.group({
|
|
209
99
|
label: this.label,
|
|
@@ -240,7 +130,19 @@ class AnimationTweenComponent {
|
|
|
240
130
|
this.vars2.updateValueAndValidity();
|
|
241
131
|
}
|
|
242
132
|
close() {
|
|
243
|
-
this.
|
|
133
|
+
this.tweenCancel.emit();
|
|
134
|
+
}
|
|
135
|
+
jsonValidator(control) {
|
|
136
|
+
if (!control.value) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
JSON.parse(control.value);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
return { json: true };
|
|
145
|
+
}
|
|
244
146
|
}
|
|
245
147
|
updateForm(tween) {
|
|
246
148
|
if (!tween) {
|
|
@@ -251,7 +153,7 @@ class AnimationTweenComponent {
|
|
|
251
153
|
this.note.setValue(tween.note || null);
|
|
252
154
|
this.type.setValue(tween.type);
|
|
253
155
|
this.selector.setValue(tween.selector);
|
|
254
|
-
this.vars.setValue(tween.vars || {});
|
|
156
|
+
this.vars.setValue(tween.vars || '{}');
|
|
255
157
|
this.vars2.setValue(tween.vars2 || null);
|
|
256
158
|
this.position.setValue(tween.position || null);
|
|
257
159
|
this.form.markAsPristine();
|
|
@@ -262,13 +164,14 @@ class AnimationTweenComponent {
|
|
|
262
164
|
note: this.note.value?.trim() || undefined,
|
|
263
165
|
type: this.type.value,
|
|
264
166
|
selector: this.selector.value.trim(),
|
|
265
|
-
vars:
|
|
266
|
-
vars2: this.vars2.value
|
|
267
|
-
? undefined
|
|
268
|
-
: this.vars2.value || undefined,
|
|
167
|
+
vars: this.vars.value,
|
|
168
|
+
vars2: this.vars2.value || undefined,
|
|
269
169
|
position: this.position.value?.trim() || undefined,
|
|
270
170
|
};
|
|
271
171
|
}
|
|
172
|
+
openUrl(url) {
|
|
173
|
+
window.open(url, '_blank');
|
|
174
|
+
}
|
|
272
175
|
save() {
|
|
273
176
|
if (!this.form.valid) {
|
|
274
177
|
return;
|
|
@@ -276,10 +179,10 @@ class AnimationTweenComponent {
|
|
|
276
179
|
this._tween = this.getTween();
|
|
277
180
|
this.tweenChange.emit(this._tween);
|
|
278
181
|
}
|
|
279
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
280
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
182
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTweenComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
183
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: AnimationTweenComponent, isStandalone: true, selector: "gve-animation-tween", inputs: { tween: "tween", elementIds: "elementIds" }, outputs: { tweenChange: "tweenChange", tweenCancel: "tweenCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n <mat-error\r\n *ngIf=\"$any(label).errors?.required && (label.dirty || label.touched)\"\r\n >label required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(label).errors?.maxLength && (label.dirty || label.touched)\"\r\n >label too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option *ngFor=\"let t of types\" [value]=\"t\">{{ t }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n [attr.aria-label]=\"'Information about position'\"\r\n (click)=\"\r\n openUrl(\r\n 'https://gsap.com/docs/v3/GSAP/Timeline/#positioning-animations-in-a-timeline'\r\n )\r\n \"\r\n >\r\n <mat-icon>info</mat-icon>\r\n </button>\r\n <mat-hint>time label < > += -=</mat-hint>\r\n <mat-error\r\n *ngIf=\"\r\n $any(position).errors?.maxLength &&\r\n (position.dirty || position.touched)\r\n \"\r\n >position too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-hint>CSS selector</mat-hint>\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.required &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.maxLength &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if (elementIds?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n <mat-option *ngFor=\"let id of elementIds\" [value]=\"id\">{{\r\n id\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n <mat-error\r\n *ngIf=\"$any(note).errors?.maxLength && (note.dirty || note.touched)\"\r\n >note too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error\r\n *ngIf=\"$any(vars).errors?.required && (vars.dirty || vars.touched)\"\r\n >vars required</mat-error\r\n >\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 'fromTo') {\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>2nd vars</mat-label>\r\n <textarea matInput [formControl]=\"vars2\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error\r\n *ngIf=\"$any(vars2).errors?.required && (vars2.dirty || vars2.touched)\"\r\n >vars required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(vars2).errors?.json && (vars2.dirty || vars2.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
281
184
|
}
|
|
282
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTweenComponent, decorators: [{
|
|
283
186
|
type: Component,
|
|
284
187
|
args: [{ selector: 'gve-animation-tween', standalone: true, imports: [
|
|
285
188
|
CommonModule,
|
|
@@ -292,19 +195,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
292
195
|
MatSelectModule,
|
|
293
196
|
MatTabsModule,
|
|
294
197
|
MatTooltipModule,
|
|
295
|
-
|
|
296
|
-
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(label).errors?.required && (label.dirty || label.touched)\r\n \"\r\n >label required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(label).errors?.maxLength && (label.dirty || label.touched)\r\n \"\r\n >label too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option *ngFor=\"let t of types\" [value]=\"t.value\">{{\r\n t.label\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(position).errors?.maxLength &&\r\n (position.dirty || position.touched)\r\n \"\r\n >position too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.required &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.maxLength &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if (elementIds?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n <mat-option *ngFor=\"let id of elementIds\" [value]=\"id\">{{\r\n id\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n <mat-error\r\n *ngIf=\"$any(note).errors?.maxLength && (note.dirty || note.touched)\"\r\n >note too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n </mat-tab>\r\n\r\n <mat-tab label=\"properties\">\r\n <!-- vars -->\r\n <gve-animation-vars\r\n [vars]=\"vars.value\"\r\n (varsChange)=\"onVarsChange($event)\"\r\n />\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 2) {\r\n <gve-animation-vars\r\n [vars]=\"vars2.value || {}\"\r\n (varsChange)=\"onVars2Change($event)\"\r\n />\r\n }\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"] }]
|
|
198
|
+
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n <mat-error\r\n *ngIf=\"$any(label).errors?.required && (label.dirty || label.touched)\"\r\n >label required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(label).errors?.maxLength && (label.dirty || label.touched)\"\r\n >label too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option *ngFor=\"let t of types\" [value]=\"t\">{{ t }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n [attr.aria-label]=\"'Information about position'\"\r\n (click)=\"\r\n openUrl(\r\n 'https://gsap.com/docs/v3/GSAP/Timeline/#positioning-animations-in-a-timeline'\r\n )\r\n \"\r\n >\r\n <mat-icon>info</mat-icon>\r\n </button>\r\n <mat-hint>time label < > += -=</mat-hint>\r\n <mat-error\r\n *ngIf=\"\r\n $any(position).errors?.maxLength &&\r\n (position.dirty || position.touched)\r\n \"\r\n >position too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-hint>CSS selector</mat-hint>\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.required &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(selector).errors?.maxLength &&\r\n (selector.dirty || selector.touched)\r\n \"\r\n >selector too long</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if (elementIds?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n <mat-option *ngFor=\"let id of elementIds\" [value]=\"id\">{{\r\n id\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n <mat-error\r\n *ngIf=\"$any(note).errors?.maxLength && (note.dirty || note.touched)\"\r\n >note too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error\r\n *ngIf=\"$any(vars).errors?.required && (vars.dirty || vars.touched)\"\r\n >vars required</mat-error\r\n >\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 'fromTo') {\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>2nd vars</mat-label>\r\n <textarea matInput [formControl]=\"vars2\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error\r\n *ngIf=\"$any(vars2).errors?.required && (vars2.dirty || vars2.touched)\"\r\n >vars required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(vars2).errors?.json && (vars2.dirty || vars2.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"] }]
|
|
297
199
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { tween: [{
|
|
298
200
|
type: Input
|
|
299
201
|
}], elementIds: [{
|
|
300
202
|
type: Input
|
|
301
203
|
}], tweenChange: [{
|
|
302
204
|
type: Output
|
|
303
|
-
}],
|
|
205
|
+
}], tweenCancel: [{
|
|
304
206
|
type: Output
|
|
305
207
|
}] } });
|
|
306
208
|
|
|
209
|
+
/**
|
|
210
|
+
* 🔑 `gve-animation-timeline`
|
|
211
|
+
*
|
|
212
|
+
* A component to edit an animation timeline.
|
|
213
|
+
* Used by the `gve-animation-timeline-set` component.
|
|
214
|
+
*
|
|
215
|
+
* - ▶️ `timeline` (`GveAnimationTimeline`): the animation timeline to edit.
|
|
216
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected
|
|
217
|
+
* by the tween.
|
|
218
|
+
* - ▶️ `tags` (`string[]`): the tags that can be used by the timeline.
|
|
219
|
+
* - 🔥 `timelineChange` (`GveAnimationTimeline`): emitted when the timeline
|
|
220
|
+
* is changed.
|
|
221
|
+
* - 🔥 `timelineCancel` (`void`): emitted when the timeline editing is canceled.
|
|
222
|
+
*/
|
|
307
223
|
class AnimationTimelineComponent {
|
|
224
|
+
/**
|
|
225
|
+
* The animation timeline to edit.
|
|
226
|
+
*/
|
|
308
227
|
get timeline() {
|
|
309
228
|
return this._timeline;
|
|
310
229
|
}
|
|
@@ -313,15 +232,19 @@ class AnimationTimelineComponent {
|
|
|
313
232
|
this.updateForm(this._timeline);
|
|
314
233
|
}
|
|
315
234
|
constructor(formBuilder) {
|
|
235
|
+
/**
|
|
236
|
+
* The tags that can be used by the timeline.
|
|
237
|
+
*/
|
|
316
238
|
this.tags = [];
|
|
239
|
+
/**
|
|
240
|
+
* Emitted when the timeline is changed.
|
|
241
|
+
*/
|
|
317
242
|
this.timelineChange = new EventEmitter();
|
|
318
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Emitted when the timeline editing is canceled.
|
|
245
|
+
*/
|
|
246
|
+
this.timelineCancel = new EventEmitter();
|
|
319
247
|
this.editedTweenIndex = -1;
|
|
320
|
-
this.selectors = {
|
|
321
|
-
0: 'to',
|
|
322
|
-
1: 'from',
|
|
323
|
-
2: 'fromTo',
|
|
324
|
-
};
|
|
325
248
|
this.tag = formBuilder.control('', {
|
|
326
249
|
nonNullable: true,
|
|
327
250
|
validators: [Validators.required, Validators.maxLength(100)],
|
|
@@ -330,13 +253,25 @@ class AnimationTimelineComponent {
|
|
|
330
253
|
nonNullable: true,
|
|
331
254
|
validators: NgToolsValidators.strictMinLengthValidator(1),
|
|
332
255
|
});
|
|
333
|
-
this.vars = formBuilder.control(
|
|
256
|
+
this.vars = formBuilder.control(null, this.jsonValidator);
|
|
334
257
|
this.form = formBuilder.group({
|
|
335
258
|
tag: this.tag,
|
|
336
259
|
tweens: this.tweens,
|
|
337
260
|
vars: this.vars,
|
|
338
261
|
});
|
|
339
262
|
}
|
|
263
|
+
jsonValidator(control) {
|
|
264
|
+
if (!control.value) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
JSON.parse(control.value);
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
catch (e) {
|
|
272
|
+
return { json: true };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
340
275
|
updateForm(timeline) {
|
|
341
276
|
if (!timeline) {
|
|
342
277
|
this.form.reset();
|
|
@@ -344,14 +279,14 @@ class AnimationTimelineComponent {
|
|
|
344
279
|
}
|
|
345
280
|
this.tag.setValue(timeline.tag);
|
|
346
281
|
this.tweens.setValue(timeline.tweens);
|
|
347
|
-
this.vars.setValue(timeline.vars ||
|
|
282
|
+
this.vars.setValue(timeline.vars || null);
|
|
348
283
|
this.form.markAsPristine();
|
|
349
284
|
}
|
|
350
285
|
addTween() {
|
|
351
286
|
this.editedTweenIndex = -1;
|
|
352
287
|
this.editedTween = {
|
|
353
288
|
label: 'tween #' + (this.tweens.value.length + 1),
|
|
354
|
-
type:
|
|
289
|
+
type: 'to',
|
|
355
290
|
selector: '',
|
|
356
291
|
};
|
|
357
292
|
}
|
|
@@ -407,11 +342,11 @@ class AnimationTimelineComponent {
|
|
|
407
342
|
return {
|
|
408
343
|
tag: this.tag.value || '',
|
|
409
344
|
tweens: this.tweens.value,
|
|
410
|
-
vars:
|
|
345
|
+
vars: this.vars.value || undefined,
|
|
411
346
|
};
|
|
412
347
|
}
|
|
413
348
|
close() {
|
|
414
|
-
this.
|
|
349
|
+
this.timelineCancel.emit();
|
|
415
350
|
}
|
|
416
351
|
save() {
|
|
417
352
|
if (this.form.invalid) {
|
|
@@ -421,10 +356,10 @@ class AnimationTimelineComponent {
|
|
|
421
356
|
this.timelineChange.emit(this._timeline);
|
|
422
357
|
this.form.markAsPristine();
|
|
423
358
|
}
|
|
424
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
425
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.6", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: "timeline", elementIds: "elementIds", tags: "tags" }, outputs: { timelineChange: "timelineChange", cancel: "cancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector | flatLookup : selectors }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <mat-expansion-panel [expanded]=\"editedTween\" [disabled]=\"!editedTween\">\r\n @if (editedTween) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween {{ editedTween.label }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds\"\r\n [tween]=\"editedTween\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (cancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "cancel"] }] }); }
|
|
359
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
360
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: "timeline", elementIds: "elementIds", tags: "tags" }, outputs: { timelineChange: "timelineChange", timelineCancel: "timelineCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n <mat-expansion-panel [expanded]=\"editedTween\" [disabled]=\"!editedTween\">\r\n @if (editedTween) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween {{ editedTween.label }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds\"\r\n [tween]=\"editedTween\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i8$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i8$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i8$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "tweenCancel"] }] }); }
|
|
426
361
|
}
|
|
427
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
362
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
|
|
428
363
|
type: Component,
|
|
429
364
|
args: [{ selector: 'gve-animation-timeline', standalone: true, imports: [
|
|
430
365
|
CommonModule,
|
|
@@ -440,7 +375,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
440
375
|
MatTooltipModule,
|
|
441
376
|
NgToolsModule,
|
|
442
377
|
AnimationTweenComponent,
|
|
443
|
-
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector
|
|
378
|
+
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n <mat-expansion-panel [expanded]=\"editedTween\" [disabled]=\"!editedTween\">\r\n @if (editedTween) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween {{ editedTween.label }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds\"\r\n [tween]=\"editedTween\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"] }]
|
|
444
379
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timeline: [{
|
|
445
380
|
type: Input
|
|
446
381
|
}], elementIds: [{
|
|
@@ -449,20 +384,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
449
384
|
type: Input
|
|
450
385
|
}], timelineChange: [{
|
|
451
386
|
type: Output
|
|
452
|
-
}],
|
|
387
|
+
}], timelineCancel: [{
|
|
453
388
|
type: Output
|
|
454
389
|
}] } });
|
|
455
390
|
|
|
456
391
|
/**
|
|
392
|
+
* 🔑 `gve-base-text-char`
|
|
393
|
+
*
|
|
457
394
|
* A component that displays a single character from a base text.
|
|
395
|
+
* Used by `gve-base-text-view`.
|
|
458
396
|
*/
|
|
459
397
|
class BaseTextCharComponent {
|
|
460
398
|
constructor() {
|
|
461
399
|
this.defaultColor = '#D8D8D8';
|
|
462
400
|
this.defaultBorderColor = '#D8D8D8';
|
|
463
401
|
this.defaultEmSize = 1.5;
|
|
402
|
+
/**
|
|
403
|
+
* Emitted when the character is clicked.
|
|
404
|
+
*/
|
|
464
405
|
this.charPick = new EventEmitter();
|
|
465
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* The character to display.
|
|
409
|
+
*/
|
|
466
410
|
get char() {
|
|
467
411
|
return this._char;
|
|
468
412
|
}
|
|
@@ -472,10 +416,10 @@ class BaseTextCharComponent {
|
|
|
472
416
|
onCharClick(event) {
|
|
473
417
|
this.charPick.emit({ char: this._char, event });
|
|
474
418
|
}
|
|
475
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
476
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
419
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextCharComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
420
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: BaseTextCharComponent, isStandalone: true, selector: "gve-base-text-char", inputs: { char: "char" }, outputs: { charPick: "charPick" }, ngImport: i0, template: "@if (char) {\r\n<div matRipple id=\"container\" (click)=\"onCharClick($event)\">\r\n <div\r\n id=\"c-label\"\r\n [style.fontSize]=\"char.emSize + 'em'\"\r\n [style.borderColor]=\"char.borderColor\"\r\n >\r\n {{ char.label }}\r\n </div>\r\n <div\r\n id=\"c-id\"\r\n [style.fontSize]=\"char.emSize / 2 + 'em'\"\r\n [style.color]=\"char.color | colorToContrast\"\r\n [style.borderColor]=\"char.borderColor\"\r\n [style.backgroundColor]=\"char.color\"\r\n >\r\n {{ char.id }}\r\n </div>\r\n</div>\r\n}\r\n", styles: ["div#container{cursor:pointer;flex-direction:column;align-items:center}div#c-label{border:1px solid silver;border-radius:6px;padding:6px;height:1.5em;align-items:center;justify-content:center;text-align:center}div#c-id{margin-top:4px;margin-bottom:16px;border:1px solid silver;border-radius:6px;padding:6px;align-items:center;justify-content:center;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i9.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.ColorToContrastPipe, name: "colorToContrast" }] }); }
|
|
477
421
|
}
|
|
478
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
422
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextCharComponent, decorators: [{
|
|
479
423
|
type: Component,
|
|
480
424
|
args: [{ selector: 'gve-base-text-char', standalone: true, imports: [MatRippleModule, NgToolsModule], template: "@if (char) {\r\n<div matRipple id=\"container\" (click)=\"onCharClick($event)\">\r\n <div\r\n id=\"c-label\"\r\n [style.fontSize]=\"char.emSize + 'em'\"\r\n [style.borderColor]=\"char.borderColor\"\r\n >\r\n {{ char.label }}\r\n </div>\r\n <div\r\n id=\"c-id\"\r\n [style.fontSize]=\"char.emSize / 2 + 'em'\"\r\n [style.color]=\"char.color | colorToContrast\"\r\n [style.borderColor]=\"char.borderColor\"\r\n [style.backgroundColor]=\"char.color\"\r\n >\r\n {{ char.id }}\r\n </div>\r\n</div>\r\n}\r\n", styles: ["div#container{cursor:pointer;flex-direction:column;align-items:center}div#c-label{border:1px solid silver;border-radius:6px;padding:6px;height:1.5em;align-items:center;justify-content:center;text-align:center}div#c-id{margin-top:4px;margin-bottom:16px;border:1px solid silver;border-radius:6px;padding:6px;align-items:center;justify-content:center;text-align:center}\n"] }]
|
|
481
425
|
}], propDecorators: { char: [{
|
|
@@ -485,18 +429,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
485
429
|
}] } });
|
|
486
430
|
|
|
487
431
|
/**
|
|
432
|
+
* 🔑 `gve-base-text-view`
|
|
433
|
+
*
|
|
488
434
|
* A component to display a selectable base text.
|
|
435
|
+
* Used by the chain result view component and the base text editor component.
|
|
436
|
+
*
|
|
437
|
+
* - ▶️ `defaultColor` (`string`): the default color for the text.
|
|
438
|
+
* - ▶️ `defaultBorderColor` (`string`): the default border color for the text.
|
|
439
|
+
* - ▶️ `selectionColor` (`string`): the color for the selected text.
|
|
440
|
+
* - ▶️ `hasLineNumber` (`boolean`): true if line numbers should be displayed next to each line.
|
|
441
|
+
* - ▶️ `text` (`string` | `CharNode[]`): the text to display.
|
|
442
|
+
* - 🔥 `charPick` (`BaseTextCharEvent`): emitted when a character is picked.
|
|
443
|
+
* - 🔥 `rangePick` (`VarBaseTextRange`): emitted when a range is picked.
|
|
489
444
|
*/
|
|
490
445
|
class BaseTextViewComponent {
|
|
491
446
|
constructor() {
|
|
447
|
+
/**
|
|
448
|
+
* The default color for the text.
|
|
449
|
+
*/
|
|
492
450
|
this.defaultColor = '#DBDBDB';
|
|
451
|
+
/**
|
|
452
|
+
* The default border color for the text.
|
|
453
|
+
*/
|
|
493
454
|
this.defaultBorderColor = '#DBDBDB';
|
|
455
|
+
/**
|
|
456
|
+
* The color for the selected text.
|
|
457
|
+
*/
|
|
494
458
|
this.selectionColor = '#3E92CC';
|
|
459
|
+
/**
|
|
460
|
+
* True if line numbers should be displayed next to each line.
|
|
461
|
+
*/
|
|
495
462
|
this.hasLineNumber = false;
|
|
463
|
+
/**
|
|
464
|
+
* Emitted when a character is picked.
|
|
465
|
+
*/
|
|
496
466
|
this.charPick = new EventEmitter();
|
|
467
|
+
/**
|
|
468
|
+
* Emitted when a range is picked. This is preceded by a character pick event.
|
|
469
|
+
* The range is defined by the starting character and the number of characters.
|
|
470
|
+
* The range is inclusive.
|
|
471
|
+
*/
|
|
497
472
|
this.rangePick = new EventEmitter();
|
|
498
473
|
this.lines = [];
|
|
499
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* The text to display.
|
|
477
|
+
*/
|
|
500
478
|
get text() {
|
|
501
479
|
return this._text;
|
|
502
480
|
}
|
|
@@ -582,12 +560,12 @@ class BaseTextViewComponent {
|
|
|
582
560
|
}
|
|
583
561
|
this._lastSelectedChar = event.char;
|
|
584
562
|
}
|
|
585
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
586
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
563
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: BaseTextViewComponent, isStandalone: true, selector: "gve-base-text-view", inputs: { defaultColor: "defaultColor", defaultBorderColor: "defaultBorderColor", selectionColor: "selectionColor", hasLineNumber: "hasLineNumber", text: "text" }, outputs: { charPick: "charPick", rangePick: "rangePick" }, ngImport: i0, template: "<div id=\"text\">\r\n @for (line of lines; track $index) {\r\n <div class=\"line\">\r\n @if (hasLineNumber) {\r\n <div class=\"nr\">\r\n {{ lines.indexOf(line) + 1 }}\r\n </div>\r\n } @for (c of line; track c.id) {\r\n <gve-base-text-char [char]=\"c\" (charPick)=\"onCharPick($event)\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.line *{flex:0 0 auto}.nr{font-size:.8em;font-weight:700;color:silver;margin:0 4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BaseTextCharComponent, selector: "gve-base-text-char", inputs: ["char"], outputs: ["charPick"] }] }); }
|
|
587
565
|
}
|
|
588
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
566
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextViewComponent, decorators: [{
|
|
589
567
|
type: Component,
|
|
590
|
-
args: [{ selector: 'gve-base-text-view', standalone: true, imports: [CommonModule, BaseTextCharComponent], template: "<div id=\"text\">\r\n @for (line of lines; track
|
|
568
|
+
args: [{ selector: 'gve-base-text-view', standalone: true, imports: [CommonModule, BaseTextCharComponent], template: "<div id=\"text\">\r\n @for (line of lines; track $index) {\r\n <div class=\"line\">\r\n @if (hasLineNumber) {\r\n <div class=\"nr\">\r\n {{ lines.indexOf(line) + 1 }}\r\n </div>\r\n } @for (c of line; track c.id) {\r\n <gve-base-text-char [char]=\"c\" (charPick)=\"onCharPick($event)\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.line *{flex:0 0 auto}.nr{font-size:.8em;font-weight:700;color:silver;margin:0 4px}\n"] }]
|
|
591
569
|
}], propDecorators: { defaultColor: [{
|
|
592
570
|
type: Input
|
|
593
571
|
}], defaultBorderColor: [{
|
|
@@ -605,12 +583,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
605
583
|
}] } });
|
|
606
584
|
|
|
607
585
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
586
|
+
* 🔑 `gve-feature-editor`
|
|
587
|
+
*
|
|
588
|
+
* Component for editing a single feature, whose model is a name=value
|
|
589
|
+
* pair, plus a set policy value which defines the desired behavior
|
|
590
|
+
* when adding that feature to a set.
|
|
591
|
+
* Used by `gve-feature-set-editor`.
|
|
592
|
+
*
|
|
593
|
+
* - ▶️ `feature` (`Feature`): the feature to edit.
|
|
594
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
595
|
+
* in the _name_ selection. This is used when you have a closed list of
|
|
596
|
+
* features. Each item in the list is an object with _id_ and _label_
|
|
597
|
+
* properties.
|
|
598
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When
|
|
599
|
+
* specified and the user selects a feature name present in the map keys,
|
|
600
|
+
* the corresponding values will be used to populate the value selection.
|
|
601
|
+
* - 🔥 `featureChange` (`Feature`): emitted when feature has changed.
|
|
602
|
+
* - 🔥 `featureCancel`: emitted when the user cancels the feature editing.
|
|
614
603
|
*/
|
|
615
604
|
class FeatureEditorComponent {
|
|
616
605
|
/**
|
|
@@ -683,8 +672,10 @@ class FeatureEditorComponent {
|
|
|
683
672
|
this._sub = this.name.valueChanges
|
|
684
673
|
.pipe(debounceTime(300), distinctUntilChanged())
|
|
685
674
|
.subscribe((name) => {
|
|
686
|
-
this.
|
|
687
|
-
|
|
675
|
+
if (!this._frozen && this.featNames?.length) {
|
|
676
|
+
this.value.reset();
|
|
677
|
+
this.nameIds = this.getLabeledIdsFor(name);
|
|
678
|
+
}
|
|
688
679
|
});
|
|
689
680
|
}
|
|
690
681
|
ngOnDestroy() {
|
|
@@ -701,12 +692,14 @@ class FeatureEditorComponent {
|
|
|
701
692
|
this.form.reset();
|
|
702
693
|
}
|
|
703
694
|
else {
|
|
695
|
+
this._frozen = true;
|
|
704
696
|
this.name.setValue(feature.name);
|
|
705
697
|
this.value.setValue(feature.value);
|
|
706
698
|
this.setPolicy.setValue(feature.setPolicy || FeatureSetPolicy.multiple);
|
|
707
699
|
this.isNegated.setValue(feature.isNegated || false);
|
|
708
700
|
this.isGlobal.setValue(feature.isGlobal || false);
|
|
709
701
|
this.isShortLived.setValue(feature.isShortLived || false);
|
|
702
|
+
this._frozen = false;
|
|
710
703
|
}
|
|
711
704
|
}
|
|
712
705
|
cancel() {
|
|
@@ -732,10 +725,10 @@ class FeatureEditorComponent {
|
|
|
732
725
|
};
|
|
733
726
|
this.featureChange.emit(this.feature);
|
|
734
727
|
}
|
|
735
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
736
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
728
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
729
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: FeatureEditorComponent, isStandalone: true, selector: "gve-feature-editor", inputs: { featNames: "featNames", featValues: "featValues", feature: "feature", isVar: "isVar" }, outputs: { featureCancel: "featureCancel", featureChange: "featureChange" }, viewQueries: [{ propertyName: "nameControl", first: true, predicate: ["nameCtl"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n @if (featNames?.length) {\r\n <!-- name (bound) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <mat-select #nameCtl [formControl]=\"name\">\r\n <mat-option *ngFor=\"let i of featNames\" [value]=\"i.id\">{{\r\n i.label\r\n }}</mat-option>\r\n </mat-select>\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error>name required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- name (free) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <input #nameCtl matInput [formControl]=\"name\" />\r\n <mat-error\r\n *ngIf=\"$any(name).errors?.required && (name.dirty || name.touched)\"\r\n >name required</mat-error\r\n >\r\n @if ($any(name).errors?.maxLength && (name.dirty || name.touched)) {\r\n <mat-error>name too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- value (bound) -->\r\n @if (nameIds?.length) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <mat-select [formControl]=\"value\">\r\n <mat-option *ngFor=\"let e of nameIds\" [value]=\"e.id\">{{\r\n e.label\r\n }}</mat-option>\r\n </mat-select>\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- value (free) -->\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n } @if ($any(value).errors?.maxLength && (value.dirty || value.touched)) {\r\n <mat-error>value too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- setPolicy -->\r\n <mat-form-field>\r\n <mat-label>set policy</mat-label>\r\n <mat-select [formControl]=\"setPolicy\">\r\n <mat-option [value]=\"0\">multiple</mat-option>\r\n <mat-option [value]=\"1\">single</mat-option>\r\n <mat-option [value]=\"2\">single first</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n </div>\r\n\r\n @if (isVar) {\r\n <div class=\"form-row\">\r\n <!-- isNegated -->\r\n <mat-checkbox [formControl]=\"isNegated\">negated</mat-checkbox>\r\n\r\n <!-- isShortLived -->\r\n <mat-checkbox [formControl]=\"isShortLived\">short-lived</mat-checkbox>\r\n\r\n <!-- isGlobal -->\r\n <mat-checkbox [formControl]=\"isGlobal\">global</mat-checkbox>\r\n </div>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
737
730
|
}
|
|
738
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
731
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureEditorComponent, decorators: [{
|
|
739
732
|
type: Component,
|
|
740
733
|
args: [{ selector: 'gve-feature-editor', standalone: true, imports: [
|
|
741
734
|
CommonModule,
|
|
@@ -766,7 +759,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
766
759
|
}] } });
|
|
767
760
|
|
|
768
761
|
/**
|
|
769
|
-
*
|
|
762
|
+
* 🔑 `gve-feature-set-editor`
|
|
763
|
+
*
|
|
764
|
+
* Component for editing a set of features. It shows a list of features,
|
|
765
|
+
* allowing to edit each of them or adding new ones. Optionally you can
|
|
766
|
+
* specify a list of feature names and values to use in the selection.
|
|
767
|
+
* Used by base text editor and chain operation editor components.
|
|
768
|
+
*
|
|
769
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
770
|
+
* in the _name_ selection. This is used when you have a closed list of features.
|
|
771
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When specified
|
|
772
|
+
* and the user selects a feature name present in the map keys, the corresponding
|
|
773
|
+
* values will be used to populate the value selection.
|
|
774
|
+
* - ▶️ `filterThreshold` (`number`): the threshold at which the features filter
|
|
775
|
+
* should become visible. If set to 0, the filter is always visible; if set to -1,
|
|
776
|
+
* it is always invisible; otherwise, it gets visible when the number of features
|
|
777
|
+
* is greater than the threshold. Default is 5.
|
|
778
|
+
* - ▶️ `features` (`Feature[]`): the features to edit.
|
|
779
|
+
* - ▶️ `isVar`: true if the feature is a variant operation feature, which
|
|
780
|
+
* has additional properties like negation, global, and short-lived.
|
|
781
|
+
* - 🔥 `featuresChange` (`Feature[]`): emitted when features have changed.
|
|
770
782
|
*/
|
|
771
783
|
class FeatureSetEditorComponent {
|
|
772
784
|
/**
|
|
@@ -783,14 +795,18 @@ class FeatureSetEditorComponent {
|
|
|
783
795
|
this.applyFeatureFilter(this.filter.value);
|
|
784
796
|
}
|
|
785
797
|
constructor(formBuilder) {
|
|
798
|
+
this.POLICIES = ['M', 'S', 'SF'];
|
|
786
799
|
/**
|
|
787
800
|
* True if the features are variable features.
|
|
788
801
|
*/
|
|
789
802
|
this.isVar = false;
|
|
790
803
|
/**
|
|
791
|
-
*
|
|
804
|
+
* The threshold at which the features filter should become visible.
|
|
805
|
+
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
806
|
+
* invisible; otherwise, it gets visible when the number of features
|
|
807
|
+
* is greater than the threshold. Default is 5.
|
|
792
808
|
*/
|
|
793
|
-
this.
|
|
809
|
+
this.filterThreshold = 5;
|
|
794
810
|
/**
|
|
795
811
|
* Emitted when the features change.
|
|
796
812
|
*/
|
|
@@ -862,10 +878,10 @@ class FeatureSetEditorComponent {
|
|
|
862
878
|
this.editedFeature = undefined;
|
|
863
879
|
this._editedFeatureIndex = -1;
|
|
864
880
|
}
|
|
865
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
866
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
881
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureSetEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
882
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: FeatureSetEditorComponent, isStandalone: true, selector: "gve-feature-set-editor", inputs: { isVar: "isVar", featNames: "featNames", featValues: "featValues", filterThreshold: "filterThreshold", features: "features" }, outputs: { featuresChange: "featuresChange" }, ngImport: i0, template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold === 0 || features.length > filterThreshold) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold === 0 || features.length > filterThreshold\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar) {\r\n <th>negated</th>\r\n <th>global</th>\r\n <th>short-lived</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures; track feature) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues) {\r\n <span>{{\r\n feature.value | flatLookup : featValues[feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar) {\r\n <td>\r\n <span>{{ $any(feature).isNegated ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isGlobal ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isShortLived ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel [disabled]=\"!editedFeature\" [expanded]=\"editedFeature\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar\"\r\n [featNames]=\"featNames\"\r\n [featValues]=\"featValues\"\r\n [feature]=\"editedFeature\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse;margin-top:8px}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i8$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i8$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i8$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: FeatureEditorComponent, selector: "gve-feature-editor", inputs: ["featNames", "featValues", "feature", "isVar"], outputs: ["featureCancel", "featureChange"] }] }); }
|
|
867
883
|
}
|
|
868
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
884
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureSetEditorComponent, decorators: [{
|
|
869
885
|
type: Component,
|
|
870
886
|
args: [{ selector: 'gve-feature-set-editor', standalone: true, imports: [
|
|
871
887
|
CommonModule,
|
|
@@ -879,14 +895,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
879
895
|
MatTooltipModule,
|
|
880
896
|
NgToolsModule,
|
|
881
897
|
FeatureEditorComponent,
|
|
882
|
-
], template: "<div>\r\n <div class=\"form-row\">\r\n @if (
|
|
898
|
+
], template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold === 0 || features.length > filterThreshold) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold === 0 || features.length > filterThreshold\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar) {\r\n <th>negated</th>\r\n <th>global</th>\r\n <th>short-lived</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures; track feature) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues) {\r\n <span>{{\r\n feature.value | flatLookup : featValues[feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar) {\r\n <td>\r\n <span>{{ $any(feature).isNegated ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isGlobal ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isShortLived ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel [disabled]=\"!editedFeature\" [expanded]=\"editedFeature\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar\"\r\n [featNames]=\"featNames\"\r\n [featValues]=\"featValues\"\r\n [feature]=\"editedFeature\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse;margin-top:8px}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"] }]
|
|
883
899
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { isVar: [{
|
|
884
900
|
type: Input
|
|
885
901
|
}], featNames: [{
|
|
886
902
|
type: Input
|
|
887
903
|
}], featValues: [{
|
|
888
904
|
type: Input
|
|
889
|
-
}],
|
|
905
|
+
}], filterThreshold: [{
|
|
890
906
|
type: Input
|
|
891
907
|
}], features: [{
|
|
892
908
|
type: Input
|
|
@@ -895,9 +911,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
895
911
|
}] } });
|
|
896
912
|
|
|
897
913
|
/**
|
|
914
|
+
* 🔑 `gve-base-text-editor`
|
|
915
|
+
*
|
|
898
916
|
* Editor for snapshot's base text. This can receive a string or an array
|
|
899
917
|
* of `CharNode` objects, and lets the user edit the text, character by
|
|
900
918
|
* character, optionally also setting its features.
|
|
919
|
+
*
|
|
920
|
+
* Used by the `gve-snapshot-editor` component.
|
|
901
921
|
*/
|
|
902
922
|
class BaseTextEditorComponent {
|
|
903
923
|
/**
|
|
@@ -925,7 +945,7 @@ class BaseTextEditorComponent {
|
|
|
925
945
|
this._dialogService = _dialogService;
|
|
926
946
|
this._text = [];
|
|
927
947
|
/**
|
|
928
|
-
*
|
|
948
|
+
* Emitted for the edited text as an array of `CharNode`'s whenever it changes.
|
|
929
949
|
*/
|
|
930
950
|
this.textChange = new EventEmitter();
|
|
931
951
|
this.userText = formBuilder.control('', {
|
|
@@ -964,10 +984,10 @@ class BaseTextEditorComponent {
|
|
|
964
984
|
}
|
|
965
985
|
});
|
|
966
986
|
}
|
|
967
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
968
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
987
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
988
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: BaseTextEditorComponent, isStandalone: true, selector: "gve-base-text-editor", inputs: { text: "text" }, outputs: { textChange: "textChange" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.required &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.maxLength &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch: TODO -->\r\n <!-- <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button> -->\r\n </div>\r\n\r\n <!-- base text -->\r\n <div id=\"text-view\">\r\n <gve-base-text-view\r\n [text]=\"text\"\r\n (charPick)=\"onSelectedChar($event)\"\r\n (rangePick)=\"textRange = $event\"\r\n />\r\n\r\n <!-- text range -->\r\n @if (textRange) {\r\n <div id=\"text-range\">{{ textRange.at }} \u00D7 {{ textRange.run }}</div>\r\n }\r\n </div>\r\n\r\n <!-- char features -->\r\n @if (selectedChar) {\r\n <fieldset>\r\n <legend>features</legend>\r\n <gve-feature-set-editor\r\n [features]=\"selectedChar.features || []\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n }\r\n</form>\r\n", styles: [".full-width{width:100%}#text-view{margin:8px 0}fieldset{border:1px solid #ccc;padding:10px;margin:10px 0;border-radius:5px}legend{color:silver}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }] }); }
|
|
969
989
|
}
|
|
970
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: BaseTextEditorComponent, decorators: [{
|
|
971
991
|
type: Component,
|
|
972
992
|
args: [{ selector: 'gve-base-text-editor', standalone: true, imports: [
|
|
973
993
|
CommonModule,
|
|
@@ -979,14 +999,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
979
999
|
MatTooltipModule,
|
|
980
1000
|
BaseTextViewComponent,
|
|
981
1001
|
FeatureSetEditorComponent,
|
|
982
|
-
], template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.required &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.maxLength &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button
|
|
1002
|
+
], template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.required &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"\r\n $any(userText).errors?.maxLength &&\r\n (userText.dirty || userText.touched)\r\n \"\r\n >text too long</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch: TODO -->\r\n <!-- <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button> -->\r\n </div>\r\n\r\n <!-- base text -->\r\n <div id=\"text-view\">\r\n <gve-base-text-view\r\n [text]=\"text\"\r\n (charPick)=\"onSelectedChar($event)\"\r\n (rangePick)=\"textRange = $event\"\r\n />\r\n\r\n <!-- text range -->\r\n @if (textRange) {\r\n <div id=\"text-range\">{{ textRange.at }} \u00D7 {{ textRange.run }}</div>\r\n }\r\n </div>\r\n\r\n <!-- char features -->\r\n @if (selectedChar) {\r\n <fieldset>\r\n <legend>features</legend>\r\n <gve-feature-set-editor\r\n [features]=\"selectedChar.features || []\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n }\r\n</form>\r\n", styles: [".full-width{width:100%}#text-view{margin:8px 0}fieldset{border:1px solid #ccc;padding:10px;margin:10px 0;border-radius:5px}legend{color:silver}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
|
|
983
1003
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.DialogService }], propDecorators: { text: [{
|
|
984
1004
|
type: Input
|
|
985
1005
|
}], textChange: [{
|
|
986
1006
|
type: Output
|
|
987
1007
|
}] } });
|
|
988
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* 🔑 `gve-operation-source-editor`
|
|
1011
|
+
*
|
|
1012
|
+
* A component to edit an operation source.
|
|
1013
|
+
* Used by chain operation editor component.
|
|
1014
|
+
*
|
|
1015
|
+
* - ▶️ `source` (`OperationSource`): the source to edit.
|
|
1016
|
+
* - ▶️ `ids` (`LabeledId[]`): the list of source IDs, when using a closed
|
|
1017
|
+
* list for them.
|
|
1018
|
+
* - ▶️ `types` (`LabeledId[]`): the list of source types, when using a closed
|
|
1019
|
+
* list for them.
|
|
1020
|
+
* - 🔥 `sourceChange` (`EventEmitter<OperationSource>`): the event emitted
|
|
1021
|
+
* when the source changes.
|
|
1022
|
+
* - 🔥 `sourceCancel` (`EventEmitter<void>`): the event emitted when the
|
|
1023
|
+
* edit is canceled.
|
|
1024
|
+
*/
|
|
989
1025
|
class OperationSourceEditorComponent {
|
|
1026
|
+
/**
|
|
1027
|
+
* The source to edit.
|
|
1028
|
+
*/
|
|
990
1029
|
get source() {
|
|
991
1030
|
return this._source;
|
|
992
1031
|
}
|
|
@@ -998,7 +1037,13 @@ class OperationSourceEditorComponent {
|
|
|
998
1037
|
this.updateForm(this._source);
|
|
999
1038
|
}
|
|
1000
1039
|
constructor(formBuilder) {
|
|
1040
|
+
/**
|
|
1041
|
+
* The event emitted when the source changes.
|
|
1042
|
+
*/
|
|
1001
1043
|
this.sourceChange = new EventEmitter();
|
|
1044
|
+
/**
|
|
1045
|
+
* The event emitted when the edit is canceled.
|
|
1046
|
+
*/
|
|
1002
1047
|
this.sourceCancel = new EventEmitter();
|
|
1003
1048
|
this.id = new FormControl('', {
|
|
1004
1049
|
validators: [Validators.required, Validators.maxLength(50)],
|
|
@@ -1047,10 +1092,10 @@ class OperationSourceEditorComponent {
|
|
|
1047
1092
|
};
|
|
1048
1093
|
this.sourceChange.emit(this._source);
|
|
1049
1094
|
}
|
|
1050
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1051
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1095
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: OperationSourceEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1096
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: OperationSourceEditorComponent, isStandalone: true, selector: "gve-operation-source-editor", inputs: { source: "source", ids: "ids", types: "types" }, outputs: { sourceChange: "sourceChange", sourceCancel: "sourceCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <!-- id (bound) -->\r\n @if (ids?.length) {\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <mat-select [formControl]=\"id\">\r\n @for (i of ids; track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- id (free) -->\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <input matInput [formControl]=\"id\" />\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n } @if ($any(id.errors)?.maxLength && (id.dirty || id.touched)) {\r\n <mat-error>id too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- type (bound) -->\r\n @if (types?.length) {\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (i of types; track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- type (free) -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <input matInput [formControl]=\"type\" />\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n } @if ($any(type.errors)?.maxLength && (type.dirty || type.touched)) {\r\n <mat-error>type too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n </mat-form-field>\r\n </div>\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea matInput [formControl]=\"note\" class=\"long-text\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".long-text{width:100%;max-width:800px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.nr{width:5em}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
1052
1097
|
}
|
|
1053
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1098
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: OperationSourceEditorComponent, decorators: [{
|
|
1054
1099
|
type: Component,
|
|
1055
1100
|
args: [{ selector: 'gve-operation-source-editor', standalone: true, imports: [
|
|
1056
1101
|
CommonModule,
|
|
@@ -1074,7 +1119,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1074
1119
|
type: Output
|
|
1075
1120
|
}] } });
|
|
1076
1121
|
|
|
1122
|
+
/**
|
|
1123
|
+
* Validators for SVG.
|
|
1124
|
+
*/
|
|
1077
1125
|
class SvgValidators {
|
|
1126
|
+
/**
|
|
1127
|
+
* Validator for a valid SVG `g` root element in GVE.
|
|
1128
|
+
*/
|
|
1078
1129
|
static rootGValidator() {
|
|
1079
1130
|
return (control) => {
|
|
1080
1131
|
if (!control.value) {
|
|
@@ -1102,11 +1153,27 @@ class SvgValidators {
|
|
|
1102
1153
|
}
|
|
1103
1154
|
}
|
|
1104
1155
|
|
|
1156
|
+
/**
|
|
1157
|
+
* 🔑 `gve-simple-tree`
|
|
1158
|
+
*
|
|
1159
|
+
* A simple tree component.
|
|
1160
|
+
*
|
|
1161
|
+
* - ▶️ `node` (`TreeNode<any>`): the node to display.
|
|
1162
|
+
* - ▶️ `selectedNode` (`TreeNode<any> | null`): the selected node.
|
|
1163
|
+
* - 🔥 `selectedNodeChange` (`EventEmitter<TreeNode<any> | null>`): the event
|
|
1164
|
+
* emitted when the selected node changes.
|
|
1165
|
+
*/
|
|
1105
1166
|
class SimpleTreeComponent {
|
|
1106
1167
|
constructor() {
|
|
1168
|
+
/**
|
|
1169
|
+
* The event emitted when the selected node changes.
|
|
1170
|
+
*/
|
|
1107
1171
|
this.selectedNodeChange = new EventEmitter();
|
|
1108
1172
|
this.level = 0;
|
|
1109
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* The node to display.
|
|
1176
|
+
*/
|
|
1110
1177
|
get node() {
|
|
1111
1178
|
return this._node;
|
|
1112
1179
|
}
|
|
@@ -1135,10 +1202,10 @@ class SimpleTreeComponent {
|
|
|
1135
1202
|
this.selectedNode = node;
|
|
1136
1203
|
this.selectedNodeChange.emit(node);
|
|
1137
1204
|
}
|
|
1138
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1139
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1205
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SimpleTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1206
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: SimpleTreeComponent, isStandalone: true, selector: "gve-simple-tree", inputs: { node: "node", selectedNode: "selectedNode" }, outputs: { selectedNodeChange: "selectedNodeChange" }, ngImport: i0, template: "@if (node) {\r\n<div [style.margin-left]=\"level * 8 + 'px'\">\r\n <ng-template\r\n #tree\r\n let-node\r\n let-parentLevel=\"level\"\r\n let-selectedNode=\"selectedNode\"\r\n >\r\n <div\r\n [style.margin-left]=\"parentLevel * 8 + 'px'\"\r\n [class.selected]=\"node === selectedNode\"\r\n >\r\n <div class=\"toolbar-row\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"node.isExpanded = !node.isExpanded\"\r\n >\r\n <mat-icon>{{\r\n node.isExpanded ? \"expand_more\" : \"chevron_right\"\r\n }}</mat-icon>\r\n </button>\r\n <span class=\"label\" (click)=\"selectNode(node)\">\r\n {{ node.label }}\r\n </span>\r\n </div>\r\n <div *ngIf=\"node.children && node.isExpanded\">\r\n <ng-container *ngFor=\"let child of node.children\">\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n tree;\r\n context: {\r\n $implicit: child,\r\n level: parentLevel + 1,\r\n selectedNode: selectedNode\r\n }\r\n \"\r\n ></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- $implicit is the source of let-node -->\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n tree;\r\n context: { $implicit: node, level: level, selectedNode: selectedNode }\r\n \"\r\n ></ng-container>\r\n</div>\r\n}\r\n", styles: [".label{cursor:pointer}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }] }); }
|
|
1140
1207
|
}
|
|
1141
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1208
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SimpleTreeComponent, decorators: [{
|
|
1142
1209
|
type: Component,
|
|
1143
1210
|
args: [{ selector: 'gve-simple-tree', standalone: true, imports: [CommonModule, MatButtonModule, MatIconModule, MatTooltipModule], template: "@if (node) {\r\n<div [style.margin-left]=\"level * 8 + 'px'\">\r\n <ng-template\r\n #tree\r\n let-node\r\n let-parentLevel=\"level\"\r\n let-selectedNode=\"selectedNode\"\r\n >\r\n <div\r\n [style.margin-left]=\"parentLevel * 8 + 'px'\"\r\n [class.selected]=\"node === selectedNode\"\r\n >\r\n <div class=\"toolbar-row\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"node.isExpanded = !node.isExpanded\"\r\n >\r\n <mat-icon>{{\r\n node.isExpanded ? \"expand_more\" : \"chevron_right\"\r\n }}</mat-icon>\r\n </button>\r\n <span class=\"label\" (click)=\"selectNode(node)\">\r\n {{ node.label }}\r\n </span>\r\n </div>\r\n <div *ngIf=\"node.children && node.isExpanded\">\r\n <ng-container *ngFor=\"let child of node.children\">\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n tree;\r\n context: {\r\n $implicit: child,\r\n level: parentLevel + 1,\r\n selectedNode: selectedNode\r\n }\r\n \"\r\n ></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- $implicit is the source of let-node -->\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n tree;\r\n context: { $implicit: node, level: level, selectedNode: selectedNode }\r\n \"\r\n ></ng-container>\r\n</div>\r\n}\r\n", styles: [".label{cursor:pointer}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}\n"] }]
|
|
1144
1211
|
}], propDecorators: { node: [{
|
|
@@ -1150,7 +1217,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1150
1217
|
}] } });
|
|
1151
1218
|
|
|
1152
1219
|
/**
|
|
1153
|
-
* Service to store and retrieve dynamic settings.
|
|
1220
|
+
* Service to store and retrieve dynamic settings. On demand, settings can
|
|
1221
|
+
* be persisted in the local storage.
|
|
1154
1222
|
*/
|
|
1155
1223
|
class SettingsService {
|
|
1156
1224
|
constructor() {
|
|
@@ -1223,10 +1291,10 @@ class SettingsService {
|
|
|
1223
1291
|
this._cache.delete(key);
|
|
1224
1292
|
localStorage.removeItem(key);
|
|
1225
1293
|
}
|
|
1226
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1227
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.
|
|
1294
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1295
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
|
|
1228
1296
|
}
|
|
1229
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SettingsService, decorators: [{
|
|
1230
1298
|
type: Injectable,
|
|
1231
1299
|
args: [{
|
|
1232
1300
|
providedIn: 'root',
|
|
@@ -1234,7 +1302,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1234
1302
|
}] });
|
|
1235
1303
|
|
|
1236
1304
|
/**
|
|
1305
|
+
* 🔑 `gve-chain-operation-editor`
|
|
1306
|
+
*
|
|
1237
1307
|
* A component for editing a variant generation operation.
|
|
1308
|
+
* Used by the `gve-snapshot-editor` component.
|
|
1309
|
+
* - ▶️ `operation` (`CharChainOperation`): the operation to edit.
|
|
1310
|
+
* - ▶️ `snapshot` (`SnapshotBase`): the snapshot the operation refers to.
|
|
1311
|
+
* - ▶️ `hidePreview` (`boolean`): whether to hide the preview request button.
|
|
1312
|
+
* - 🔥 `operationChange` (`CharChainOperation`): emitted when the operation
|
|
1313
|
+
* is changed.
|
|
1314
|
+
* - 🔥 `operationPreview` (`CharChainOperation`): emitted when the operation
|
|
1315
|
+
* preview is requested.
|
|
1316
|
+
* - 🔥 `operationCancel` (`void`): emitted when operation editing is cancelled.
|
|
1317
|
+
*
|
|
1318
|
+
* The editor is composed of many sections:
|
|
1319
|
+
* - **general**:
|
|
1320
|
+
* - ID: each operation has an ID, automatically assigned. This is not used in
|
|
1321
|
+
* the context of the operation or of its transformations, but is provided as a
|
|
1322
|
+
* reference.
|
|
1323
|
+
* - at and run: coordinates for the selection of the base text targeted by
|
|
1324
|
+
* this operation. Check `idx` when `at` is index-based rather than ID-based.
|
|
1325
|
+
* - to and to-run: a second coordinate, used by some operations.
|
|
1326
|
+
* - value: the text value argument, for those operations requiring it.
|
|
1327
|
+
* - rank and group ID.
|
|
1328
|
+
* - input and output tags.
|
|
1329
|
+
* - features. These are metadata attached to this operation, and can be any
|
|
1330
|
+
* type of name=value pairs.
|
|
1331
|
+
* - **sources**: sources are a special type of metadata used when your text
|
|
1332
|
+
* versions come from more than a single source. In this case, you can specify
|
|
1333
|
+
* the source(s) for each operation, just like in a traditional apparatus you
|
|
1334
|
+
* specify witnesses for each variant. Each source has an ID, a type, a
|
|
1335
|
+
* probability rank (0=not specified, else 1-N), and an optional free text note.
|
|
1336
|
+
* - **diplomatic**: SVG `g` code. The diplomatic section is the approximate
|
|
1337
|
+
* diplomatic representation of the layout of the annotated text. It essentially
|
|
1338
|
+
* consists in a freely editable SVG code, always grouped into a single root `g`
|
|
1339
|
+
* element, encoding all the graphical traits representing this operation.
|
|
1340
|
+
* The visual part of each operation can be placed everywhere on the snapshot,
|
|
1341
|
+
* as specified by its coordinates.
|
|
1342
|
+
* - **elements**: features of any of the SVG code elements having an ID.
|
|
1343
|
+
* The elements section lists all the SVG elements having an `id` attribute in
|
|
1344
|
+
* the SVG code of the diplomatic section. The ID allows you to attach
|
|
1345
|
+
* any number of features (metadata modeled as name=value pairs) to each of
|
|
1346
|
+
* these elements. Among these features, you may use `style` or `class` for
|
|
1347
|
+
* renditional purposes, and any other name for your own metadata. Note that
|
|
1348
|
+
* element IDs will be required also when dealing with animations. So, ensure
|
|
1349
|
+
* to assign an ID to each element you want to animate.
|
|
1350
|
+
* - **dp feats**: diplomatic features: these are features (metadata modeled
|
|
1351
|
+
* as name=value pairs) for the diplomatic representation of this operation
|
|
1352
|
+
* as a whole.
|
|
1238
1353
|
*/
|
|
1239
1354
|
class ChainOperationEditorComponent {
|
|
1240
1355
|
/**
|
|
@@ -1267,23 +1382,24 @@ class ChainOperationEditorComponent {
|
|
|
1267
1382
|
this._editorModel?.setValue(this.svg.value || '');
|
|
1268
1383
|
}
|
|
1269
1384
|
}
|
|
1270
|
-
constructor(formBuilder, _clipboard, _settings) {
|
|
1385
|
+
constructor(formBuilder, _clipboard, _settings, _dialogService) {
|
|
1271
1386
|
this._clipboard = _clipboard;
|
|
1272
1387
|
this._settings = _settings;
|
|
1388
|
+
this._dialogService = _dialogService;
|
|
1273
1389
|
// monaco
|
|
1274
1390
|
this._disposables = [];
|
|
1275
1391
|
this._nanoid = customAlphabet('1234567890abcdef', 10);
|
|
1276
1392
|
this._editedSourceIndex = -1;
|
|
1277
1393
|
/**
|
|
1278
|
-
*
|
|
1394
|
+
* Emitted when the operation is changed.
|
|
1279
1395
|
*/
|
|
1280
1396
|
this.operationChange = new EventEmitter();
|
|
1281
1397
|
/**
|
|
1282
|
-
*
|
|
1398
|
+
* Emitted when the operation preview is requested.
|
|
1283
1399
|
*/
|
|
1284
1400
|
this.operationPreview = new EventEmitter();
|
|
1285
1401
|
/**
|
|
1286
|
-
*
|
|
1402
|
+
* Emitted when operation editing is cancelled.
|
|
1287
1403
|
*/
|
|
1288
1404
|
this.operationCancel = new EventEmitter();
|
|
1289
1405
|
this.tabIndex = 0;
|
|
@@ -1460,7 +1576,9 @@ class ChainOperationEditorComponent {
|
|
|
1460
1576
|
window.URL.revokeObjectURL(url);
|
|
1461
1577
|
}
|
|
1462
1578
|
openSvgEditor() {
|
|
1463
|
-
const url = this._settings.get('svg-editor', 'https://editor.method.ac/'
|
|
1579
|
+
const url = this._settings.get('svg-editor', 'https://editor.method.ac/'
|
|
1580
|
+
// 'https://boxy-svg.com/app'
|
|
1581
|
+
);
|
|
1464
1582
|
if (url) {
|
|
1465
1583
|
if (this.svg.value) {
|
|
1466
1584
|
this._clipboard.copy(this.svg.value);
|
|
@@ -1534,11 +1652,20 @@ class ChainOperationEditorComponent {
|
|
|
1534
1652
|
deleteElementFeatures(element) {
|
|
1535
1653
|
const id = element.id;
|
|
1536
1654
|
const elementFeatures = { ...this.elementFeatures.value };
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (this.editedElementId === id) {
|
|
1540
|
-
this.editedElementId = undefined;
|
|
1655
|
+
if (!elementFeatures[id]) {
|
|
1656
|
+
return;
|
|
1541
1657
|
}
|
|
1658
|
+
this._dialogService
|
|
1659
|
+
.confirm('Confirm', 'Delete element features?')
|
|
1660
|
+
.subscribe((yes) => {
|
|
1661
|
+
if (yes) {
|
|
1662
|
+
delete elementFeatures[id];
|
|
1663
|
+
this.elementFeatures.setValue(elementFeatures);
|
|
1664
|
+
if (this.editedElementId === id) {
|
|
1665
|
+
this.editedElementId = undefined;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1542
1669
|
}
|
|
1543
1670
|
onElementFeaturesChange(features) {
|
|
1544
1671
|
if (this.editedElementId) {
|
|
@@ -1612,8 +1739,6 @@ class ChainOperationEditorComponent {
|
|
|
1612
1739
|
this.elementFeatures.setValue(operation.diplomatics?.elementFeatures || {});
|
|
1613
1740
|
this.form.markAsPristine();
|
|
1614
1741
|
this.elements = this.parseSvg(operation.diplomatics?.g);
|
|
1615
|
-
// SVG
|
|
1616
|
-
this.requestPreview();
|
|
1617
1742
|
this.updateArgsUI();
|
|
1618
1743
|
}
|
|
1619
1744
|
cancel() {
|
|
@@ -1621,9 +1746,9 @@ class ChainOperationEditorComponent {
|
|
|
1621
1746
|
}
|
|
1622
1747
|
getOperation() {
|
|
1623
1748
|
return {
|
|
1624
|
-
rank: this.rank.value,
|
|
1749
|
+
rank: this.rank.value || undefined,
|
|
1625
1750
|
groupId: this.groupId.value || undefined,
|
|
1626
|
-
features: this.features.value,
|
|
1751
|
+
features: this.features.value?.length ? this.features.value : undefined,
|
|
1627
1752
|
sources: this.sources.value?.length ? this.sources.value : undefined,
|
|
1628
1753
|
diplomatics: {
|
|
1629
1754
|
g: this.svg.value,
|
|
@@ -1633,13 +1758,13 @@ class ChainOperationEditorComponent {
|
|
|
1633
1758
|
id: this.id,
|
|
1634
1759
|
type: this.type.value,
|
|
1635
1760
|
at: this.at.value,
|
|
1636
|
-
atAsIndex: this.atAsIndex.value,
|
|
1637
|
-
to: this.to.value,
|
|
1638
|
-
toAsIndex: this.toAsIndex.value,
|
|
1761
|
+
atAsIndex: this.atAsIndex.value || undefined,
|
|
1762
|
+
to: this.to.value || undefined,
|
|
1763
|
+
toAsIndex: this.toAsIndex.value || undefined,
|
|
1639
1764
|
inputTag: this.inputTag.value || undefined,
|
|
1640
1765
|
outputTag: this.outputTag.value || undefined,
|
|
1641
1766
|
run: this.run.value,
|
|
1642
|
-
toRun: this.toRun.value,
|
|
1767
|
+
toRun: this.toRun.value || undefined,
|
|
1643
1768
|
value: this.value.value || undefined,
|
|
1644
1769
|
};
|
|
1645
1770
|
}
|
|
@@ -1653,16 +1778,16 @@ class ChainOperationEditorComponent {
|
|
|
1653
1778
|
this._operation = this.getOperation();
|
|
1654
1779
|
this.operationChange.emit(this._operation);
|
|
1655
1780
|
}
|
|
1656
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1657
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.6", type: ChainOperationEditorComponent, isStandalone: true, selector: "gve-chain-operation-editor", inputs: { operation: "operation", snapshot: "snapshot", hidePreview: "hidePreview" }, outputs: { operationChange: "operationChange", operationPreview: "operationPreview", operationCancel: "operationCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\">replace</mat-option>\r\n <mat-option [value]=\"1\">delete</mat-option>\r\n <mat-option [value]=\"2\">add-before</mat-option>\r\n <mat-option [value]=\"3\">add-after</mat-option>\r\n <mat-option [value]=\"4\">move-before</mat-option>\r\n <mat-option [value]=\"5\">move-after</mat-option>\r\n <mat-option [value]=\"6\">swap</mat-option>\r\n <mat-option [value]=\"7\">annotate</mat-option>\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(type).errors?.required && (type.dirty || type.touched)\"\r\n >type required</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(at).errors?.required && (at.dirty || at.touched)\"\r\n >at required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(run).errors?.required && (run.dirty || run.touched)\"\r\n >run required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(to).errors?.required && (to.dirty || to.touched)\"\r\n >to required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(toRun).errors?.required && (toRun.dirty || toRun.touched)\r\n \"\r\n >to run required</mat-error\r\n >\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(rank).errors?.required && (rank.dirty || rank.touched)\"\r\n >rank required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(groupId).errors?.required &&\r\n (groupId.dirty || groupId.touched)\r\n \"\r\n >group ID required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- source editor -->\r\n <mat-expansion-panel [disabled]=\"!editedSource\" [expanded]=\"editedSource\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements; track e.id) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ e.id }}</td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId\"\r\n [expanded]=\"editedElementId\"\r\n >\r\n <mat-expansion-panel-header>element</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [features]=\"elementFeatures.value[editedElementId!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <gve-feature-set-editor\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse;margin:8px 0}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr{border-bottom:1px solid silver}th,td{text-align:center}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type:
|
|
1781
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: ChainOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$3.Clipboard }, { token: SettingsService }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1782
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: ChainOperationEditorComponent, isStandalone: true, selector: "gve-chain-operation-editor", inputs: { operation: "operation", snapshot: "snapshot", hidePreview: "hidePreview" }, outputs: { operationChange: "operationChange", operationPreview: "operationPreview", operationCancel: "operationCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\"><mat-icon>layers</mat-icon> replace</mat-option>\r\n <mat-option [value]=\"1\"><mat-icon>close</mat-icon>delete</mat-option>\r\n <mat-option [value]=\"2\"><mat-icon>last_page</mat-icon>add-before</mat-option>\r\n <mat-option [value]=\"3\"><mat-icon>first_page</mat-icon>add-after</mat-option>\r\n <mat-option [value]=\"4\"><mat-icon>login</mat-icon>move-before</mat-option>\r\n <mat-option [value]=\"5\"><mat-icon>logout</mat-icon>move-after</mat-option>\r\n <mat-option [value]=\"6\"><mat-icon>compare_arrows</mat-icon>swap</mat-option>\r\n <mat-option [value]=\"7\"><mat-icon>edit_note</mat-icon>annotate</mat-option>\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(type).errors?.required && (type.dirty || type.touched)\"\r\n >type required</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(at).errors?.required && (at.dirty || at.touched)\"\r\n >at required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(run).errors?.required && (run.dirty || run.touched)\"\r\n >run required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(to).errors?.required && (to.dirty || to.touched)\"\r\n >to required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(toRun).errors?.required && (toRun.dirty || toRun.touched)\r\n \"\r\n >to run required</mat-error\r\n >\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(rank).errors?.required && (rank.dirty || rank.touched)\"\r\n >rank required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(groupId).errors?.required &&\r\n (groupId.dirty || groupId.touched)\r\n \"\r\n >group ID required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- source editor -->\r\n <mat-expansion-panel [disabled]=\"!editedSource\" [expanded]=\"editedSource\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements; track e.id) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Edit element features\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n matTooltip=\"Delete all element features\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n [disabled]=\"!$any(elementFeatures.value[e.id])?.length\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <span>{{$any(elementFeatures.value[e.id])?.length || 0}}</span>\r\n </td>\r\n <td class=\"feat-count\">{{ e.id }}</td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId\"\r\n [expanded]=\"editedElementId\"\r\n >\r\n <mat-expansion-panel-header>element</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [features]=\"elementFeatures.value[editedElementId!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <gve-feature-set-editor\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse;margin:8px 0}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}td.feat-count{vertical-align:super;font-size:smaller}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr{border-bottom:1px solid silver}th,td{text-align:center}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type:
|
|
1658
1783
|
// material
|
|
1659
|
-
ClipboardModule }, { kind: "directive", type: i2$3.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4
|
|
1784
|
+
ClipboardModule }, { kind: "directive", type: i2$3.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i8$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i8$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i8$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i13.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i13.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type:
|
|
1660
1785
|
// monaco
|
|
1661
|
-
NgeMonacoModule }, { kind: "component", type:
|
|
1786
|
+
NgeMonacoModule }, { kind: "component", type: i16.NgeMonacoEditorComponent, selector: "nge-monaco-editor", inputs: ["autoLayout", "options"], outputs: ["ready"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.SafeHtmlPipe, name: "safeHtml" }, { kind: "ngmodule", type: NgMatToolsModule }, { kind: "component", type:
|
|
1662
1787
|
// myrmex
|
|
1663
|
-
FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "
|
|
1788
|
+
FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }, { kind: "component", type: OperationSourceEditorComponent, selector: "gve-operation-source-editor", inputs: ["source", "ids", "types"], outputs: ["sourceChange", "sourceCancel"] }] }); }
|
|
1664
1789
|
}
|
|
1665
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1790
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: ChainOperationEditorComponent, decorators: [{
|
|
1666
1791
|
type: Component,
|
|
1667
1792
|
args: [{ selector: 'gve-chain-operation-editor', standalone: true, imports: [
|
|
1668
1793
|
CommonModule,
|
|
@@ -1686,8 +1811,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1686
1811
|
FeatureSetEditorComponent,
|
|
1687
1812
|
SimpleTreeComponent,
|
|
1688
1813
|
OperationSourceEditorComponent,
|
|
1689
|
-
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\">replace</mat-option>\r\n <mat-option [value]=\"1\">delete</mat-option>\r\n <mat-option [value]=\"2\">add-before</mat-option>\r\n <mat-option [value]=\"3\">add-after</mat-option>\r\n <mat-option [value]=\"4\">move-before</mat-option>\r\n <mat-option [value]=\"5\">move-after</mat-option>\r\n <mat-option [value]=\"6\">swap</mat-option>\r\n <mat-option [value]=\"7\">annotate</mat-option>\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(type).errors?.required && (type.dirty || type.touched)\"\r\n >type required</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(at).errors?.required && (at.dirty || at.touched)\"\r\n >at required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(run).errors?.required && (run.dirty || run.touched)\"\r\n >run required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(to).errors?.required && (to.dirty || to.touched)\"\r\n >to required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(toRun).errors?.required && (toRun.dirty || toRun.touched)\r\n \"\r\n >to run required</mat-error\r\n >\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(rank).errors?.required && (rank.dirty || rank.touched)\"\r\n >rank required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(groupId).errors?.required &&\r\n (groupId.dirty || groupId.touched)\r\n \"\r\n >group ID required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- source editor -->\r\n <mat-expansion-panel [disabled]=\"!editedSource\" [expanded]=\"editedSource\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements; track e.id) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ e.id }}</td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId\"\r\n [expanded]=\"editedElementId\"\r\n >\r\n <mat-expansion-panel-header>element</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [features]=\"elementFeatures.value[editedElementId!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <gve-feature-set-editor\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse;margin:8px 0}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr{border-bottom:1px solid silver}th,td{text-align:center}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"] }]
|
|
1690
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$3.Clipboard }, { type: SettingsService }], propDecorators: { operation: [{
|
|
1814
|
+
], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\"><mat-icon>layers</mat-icon> replace</mat-option>\r\n <mat-option [value]=\"1\"><mat-icon>close</mat-icon>delete</mat-option>\r\n <mat-option [value]=\"2\"><mat-icon>last_page</mat-icon>add-before</mat-option>\r\n <mat-option [value]=\"3\"><mat-icon>first_page</mat-icon>add-after</mat-option>\r\n <mat-option [value]=\"4\"><mat-icon>login</mat-icon>move-before</mat-option>\r\n <mat-option [value]=\"5\"><mat-icon>logout</mat-icon>move-after</mat-option>\r\n <mat-option [value]=\"6\"><mat-icon>compare_arrows</mat-icon>swap</mat-option>\r\n <mat-option [value]=\"7\"><mat-icon>edit_note</mat-icon>annotate</mat-option>\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(type).errors?.required && (type.dirty || type.touched)\"\r\n >type required</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(at).errors?.required && (at.dirty || at.touched)\"\r\n >at required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(run).errors?.required && (run.dirty || run.touched)\"\r\n >run required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(to).errors?.required && (to.dirty || to.touched)\"\r\n >to required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(toRun).errors?.required && (toRun.dirty || toRun.touched)\r\n \"\r\n >to run required</mat-error\r\n >\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n <mat-error\r\n *ngIf=\"$any(rank).errors?.required && (rank.dirty || rank.touched)\"\r\n >rank required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n <mat-error\r\n *ngIf=\"\r\n $any(groupId).errors?.required &&\r\n (groupId.dirty || groupId.touched)\r\n \"\r\n >group ID required</mat-error\r\n >\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- source editor -->\r\n <mat-expansion-panel [disabled]=\"!editedSource\" [expanded]=\"editedSource\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements; track e.id) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Edit element features\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n matTooltip=\"Delete all element features\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n [disabled]=\"!$any(elementFeatures.value[e.id])?.length\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <span>{{$any(elementFeatures.value[e.id])?.length || 0}}</span>\r\n </td>\r\n <td class=\"feat-count\">{{ e.id }}</td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId\"\r\n [expanded]=\"editedElementId\"\r\n >\r\n <mat-expansion-panel-header>element</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [features]=\"elementFeatures.value[editedElementId!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <gve-feature-set-editor\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n ></gve-feature-set-editor>\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse;margin:8px 0}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}td.feat-count{vertical-align:super;font-size:smaller}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr{border-bottom:1px solid silver}th,td{text-align:center}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"] }]
|
|
1815
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$3.Clipboard }, { type: SettingsService }, { type: i2$2.DialogService }], propDecorators: { operation: [{
|
|
1691
1816
|
type: Input
|
|
1692
1817
|
}], snapshot: [{
|
|
1693
1818
|
type: Input
|
|
@@ -1701,6 +1826,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1701
1826
|
type: Output
|
|
1702
1827
|
}] } });
|
|
1703
1828
|
|
|
1829
|
+
/**
|
|
1830
|
+
* 🔑 `gve-feature-set-view`
|
|
1831
|
+
*
|
|
1832
|
+
* A component to display a list of features.
|
|
1833
|
+
* Used by the chain result view component.
|
|
1834
|
+
*
|
|
1835
|
+
* - ▶️ `features` (`Feature[]`): the features to display.
|
|
1836
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
1837
|
+
* in the _name_ selection. This is used when you have a closed list of features.
|
|
1838
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When specified
|
|
1839
|
+
* and the user selects a feature name present in the map keys, the corresponding
|
|
1840
|
+
* values will be used to populate the value selection.
|
|
1841
|
+
* - ▶️ `filterThreshold` (`number`): the threshold at which the features filter
|
|
1842
|
+
* should become visible. If set to 0, the filter is always visible; if set to -1,
|
|
1843
|
+
* it is always invisible; otherwise, it gets visible when the number of features
|
|
1844
|
+
* is greater than the threshold. Default is 5.
|
|
1845
|
+
*/
|
|
1704
1846
|
class FeatureSetViewComponent {
|
|
1705
1847
|
/**
|
|
1706
1848
|
* The features.
|
|
@@ -1719,9 +1861,12 @@ class FeatureSetViewComponent {
|
|
|
1719
1861
|
constructor(formBuilder) {
|
|
1720
1862
|
this._features = [];
|
|
1721
1863
|
/**
|
|
1722
|
-
* The
|
|
1864
|
+
* The threshold at which the features filter should become visible.
|
|
1865
|
+
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
1866
|
+
* invisible; otherwise, it gets visible when the number of features
|
|
1867
|
+
* is greater than the threshold. Default is 5.
|
|
1723
1868
|
*/
|
|
1724
|
-
this.
|
|
1869
|
+
this.filterThreshold = 5;
|
|
1725
1870
|
this.filteredFeatures = [];
|
|
1726
1871
|
this.filter = formBuilder.control(null);
|
|
1727
1872
|
}
|
|
@@ -1741,10 +1886,10 @@ class FeatureSetViewComponent {
|
|
|
1741
1886
|
ngOnDestroy() {
|
|
1742
1887
|
this._sub?.unsubscribe();
|
|
1743
1888
|
}
|
|
1744
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1745
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1889
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureSetViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1890
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: FeatureSetViewComponent, isStandalone: true, selector: "gve-feature-set-view", inputs: { features: "features", featNames: "featNames", featValues: "featValues", filterThreshold: "filterThreshold" }, ngImport: i0, template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold === 0 || features.length > filterThreshold) {\r\n <div class=\"form-row\">\r\n <mat-form-field id=\"filter\">\r\n <mat-label>filter</mat-label>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n <span>{{ filteredFeatures.length }}</span>\r\n </div>\r\n }\r\n\r\n <!-- list -->\r\n @if (filteredFeatures.length) {\r\n <table>\r\n <tbody>\r\n @for (feature of filteredFeatures; track $index) {\r\n <tr>\r\n <th>\r\n @if (featNames?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </th>\r\n <td>\r\n @if (featValues) {\r\n <span>{{\r\n feature.value | flatLookup : featValues[feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}th{text-align:left;background-color:#c8d9eb;color:#333;font-weight:400}th,td{border:1px solid silver;padding:4px}tr:nth-child(2n){background-color:#dfdfdf}#filter{width:7em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.FlatLookupPipe, name: "flatLookup" }] }); }
|
|
1746
1891
|
}
|
|
1747
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1892
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: FeatureSetViewComponent, decorators: [{
|
|
1748
1893
|
type: Component,
|
|
1749
1894
|
args: [{ selector: 'gve-feature-set-view', standalone: true, imports: [
|
|
1750
1895
|
CommonModule,
|
|
@@ -1754,28 +1899,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1754
1899
|
MatIconModule,
|
|
1755
1900
|
MatInputModule,
|
|
1756
1901
|
NgToolsModule,
|
|
1757
|
-
], template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (
|
|
1902
|
+
], template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold === 0 || features.length > filterThreshold) {\r\n <div class=\"form-row\">\r\n <mat-form-field id=\"filter\">\r\n <mat-label>filter</mat-label>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n <span>{{ filteredFeatures.length }}</span>\r\n </div>\r\n }\r\n\r\n <!-- list -->\r\n @if (filteredFeatures.length) {\r\n <table>\r\n <tbody>\r\n @for (feature of filteredFeatures; track $index) {\r\n <tr>\r\n <th>\r\n @if (featNames?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </th>\r\n <td>\r\n @if (featValues) {\r\n <span>{{\r\n feature.value | flatLookup : featValues[feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}th{text-align:left;background-color:#c8d9eb;color:#333;font-weight:400}th,td{border:1px solid silver;padding:4px}tr:nth-child(2n){background-color:#dfdfdf}#filter{width:7em}\n"] }]
|
|
1758
1903
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { features: [{
|
|
1759
1904
|
type: Input
|
|
1760
1905
|
}], featNames: [{
|
|
1761
1906
|
type: Input
|
|
1762
1907
|
}], featValues: [{
|
|
1763
1908
|
type: Input
|
|
1764
|
-
}],
|
|
1909
|
+
}], filterThreshold: [{
|
|
1765
1910
|
type: Input
|
|
1766
1911
|
}] } });
|
|
1767
1912
|
|
|
1768
1913
|
/**
|
|
1769
|
-
*
|
|
1914
|
+
* 🔑 `gve-steps-map`
|
|
1915
|
+
*
|
|
1916
|
+
* A map of steps in a chain operation context. This shows the list of
|
|
1917
|
+
* steps in the execution of a set of operations, and allows the user to
|
|
1918
|
+
* pick a step to view.
|
|
1919
|
+
*
|
|
1920
|
+
* Used by the `gve-chain-result-view` component.
|
|
1921
|
+
*
|
|
1922
|
+
* - ▶️ `steps` (`ChainOperationContextStep[]`): the steps to display.
|
|
1923
|
+
* - ▶️ `selectedStep` (`ChainOperationContextStep`): the step that is
|
|
1924
|
+
* currently selected.
|
|
1925
|
+
* - ▶️ `textFontSize` (`string`): the font size of the steps text.
|
|
1926
|
+
* - 🔥 `selectedStepChange` (`ChainOperationContextStep`): emitted when
|
|
1927
|
+
* the selectd step has been changed by the user. This is not emitted
|
|
1928
|
+
* when the selected step is changed programmatically, unless this is
|
|
1929
|
+
* the first time the steps are set. In that case, the last step is
|
|
1930
|
+
* automatically selected.
|
|
1770
1931
|
*/
|
|
1771
1932
|
class StepsMapComponent {
|
|
1772
1933
|
constructor() {
|
|
1773
1934
|
/**
|
|
1774
1935
|
* The font size of the steps text.
|
|
1775
1936
|
*/
|
|
1776
|
-
this.textFontSize = '0.
|
|
1937
|
+
this.textFontSize = '0.6em';
|
|
1777
1938
|
/**
|
|
1778
|
-
* Emitted when the
|
|
1939
|
+
* Emitted when the selected step has changed by user, or when
|
|
1940
|
+
* the steps are set for the first time.
|
|
1779
1941
|
*/
|
|
1780
1942
|
this.selectedStepChange = new EventEmitter();
|
|
1781
1943
|
}
|
|
@@ -1789,19 +1951,24 @@ class StepsMapComponent {
|
|
|
1789
1951
|
if (this._steps === value) {
|
|
1790
1952
|
return;
|
|
1791
1953
|
}
|
|
1954
|
+
const first = this._steps === undefined;
|
|
1792
1955
|
this.selectedStep = undefined;
|
|
1793
1956
|
this._steps = value || undefined;
|
|
1957
|
+
if (first && this._steps?.length) {
|
|
1958
|
+
this.selectedStep = this._steps[this._steps.length - 1];
|
|
1959
|
+
this.selectedStepChange.emit(this.selectedStep);
|
|
1960
|
+
}
|
|
1794
1961
|
}
|
|
1795
|
-
|
|
1962
|
+
onStepClick(step) {
|
|
1796
1963
|
this.selectedStep = step;
|
|
1797
1964
|
this.selectedStepChange.emit(step);
|
|
1798
1965
|
}
|
|
1799
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1800
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1966
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: StepsMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1967
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: StepsMapComponent, isStandalone: true, selector: "gve-steps-map", inputs: { steps: "steps", selectedStep: "selectedStep", textFontSize: "textFontSize" }, outputs: { selectedStepChange: "selectedStepChange" }, ngImport: i0, template: "<div>\r\n @if (steps?.length) { @for (step of steps; track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [ngClass]=\"{ selected: step === selectedStep }\"\r\n (click)=\"onStepClick(step)\"\r\n >\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} ▶ </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <div class=\"text\" [style.fontSize]=\"textFontSize\">{{ step.result }}</div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#fbfb9d}.step{border-top:1px solid rgb(68,114,253);border-bottom:1px solid rgb(68,114,253);border-right:1px solid rgb(68,114,253);border-radius:6px;margin:8px 0}.tag{background-color:#4472fd;color:#fff;border:1px solid rgb(68,114,253);border-radius:6px;padding:4px;cursor:pointer}.muted-tag{color:silver}.text{font-size:.5em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i9.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatTooltipModule }] }); }
|
|
1801
1968
|
}
|
|
1802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1969
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: StepsMapComponent, decorators: [{
|
|
1803
1970
|
type: Component,
|
|
1804
|
-
args: [{ selector: 'gve-steps-map', standalone: true, imports: [CommonModule, MatButtonModule, MatRippleModule, MatTooltipModule], template: "<div>\r\n @if (steps?.length) { @for (step of steps; track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [ngClass]=\"{ selected: step === selectedStep }\"\r\n (click)=\"
|
|
1971
|
+
args: [{ selector: 'gve-steps-map', standalone: true, imports: [CommonModule, MatButtonModule, MatRippleModule, MatTooltipModule], template: "<div>\r\n @if (steps?.length) { @for (step of steps; track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [ngClass]=\"{ selected: step === selectedStep }\"\r\n (click)=\"onStepClick(step)\"\r\n >\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} ▶ </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <div class=\"text\" [style.fontSize]=\"textFontSize\">{{ step.result }}</div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#fbfb9d}.step{border-top:1px solid rgb(68,114,253);border-bottom:1px solid rgb(68,114,253);border-right:1px solid rgb(68,114,253);border-radius:6px;margin:8px 0}.tag{background-color:#4472fd;color:#fff;border:1px solid rgb(68,114,253);border-radius:6px;padding:4px;cursor:pointer}.muted-tag{color:silver}.text{font-size:.5em}\n"] }]
|
|
1805
1972
|
}], propDecorators: { steps: [{
|
|
1806
1973
|
type: Input
|
|
1807
1974
|
}], selectedStep: [{
|
|
@@ -1813,7 +1980,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1813
1980
|
}] } });
|
|
1814
1981
|
|
|
1815
1982
|
/**
|
|
1983
|
+
* 🔑 `gve-chain-result-view`
|
|
1984
|
+
*
|
|
1816
1985
|
* Component to display a chain result.
|
|
1986
|
+
* Used by the `gve-snapshot-editor` component.
|
|
1987
|
+
*
|
|
1988
|
+
* - ▶️ `result` (`CharChainResult`): the result to display.
|
|
1989
|
+
* - 🔥 `stepPick` (`ChainOperationContextStep`): emitted when a
|
|
1990
|
+
* result's step is picked by the user.
|
|
1817
1991
|
*/
|
|
1818
1992
|
class ChainResultViewComponent {
|
|
1819
1993
|
/**
|
|
@@ -1828,17 +2002,29 @@ class ChainResultViewComponent {
|
|
|
1828
2002
|
}
|
|
1829
2003
|
this._result = value || undefined;
|
|
1830
2004
|
this.updateForm(this._result);
|
|
1831
|
-
// select
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2005
|
+
// select the initial step if set
|
|
2006
|
+
this.selectInitialStep();
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* The index of the initial step to display after the result is set.
|
|
2010
|
+
* If the index is negative, it is counted from the end of the steps.
|
|
2011
|
+
*/
|
|
2012
|
+
get initialStepIndex() {
|
|
2013
|
+
return this._initialStepIndex;
|
|
2014
|
+
}
|
|
2015
|
+
set initialStepIndex(value) {
|
|
2016
|
+
if (value === undefined || value === null) {
|
|
2017
|
+
this._initialStepIndex = undefined;
|
|
2018
|
+
this.step = undefined;
|
|
2019
|
+
}
|
|
2020
|
+
else {
|
|
2021
|
+
this._initialStepIndex = value;
|
|
2022
|
+
this.selectInitialStep();
|
|
1837
2023
|
}
|
|
1838
2024
|
}
|
|
1839
2025
|
constructor(formBuilder) {
|
|
1840
2026
|
/**
|
|
1841
|
-
*
|
|
2027
|
+
* Emitted when a result's step is picked by the user.
|
|
1842
2028
|
*/
|
|
1843
2029
|
this.stepPick = new EventEmitter();
|
|
1844
2030
|
this.versionTags = [];
|
|
@@ -1849,21 +2035,31 @@ class ChainResultViewComponent {
|
|
|
1849
2035
|
}
|
|
1850
2036
|
ngOnInit() {
|
|
1851
2037
|
this._subs = [
|
|
2038
|
+
// whenever the staged version tag changes, update the form
|
|
1852
2039
|
this.versionTag.valueChanges
|
|
1853
2040
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
1854
2041
|
.subscribe((value) => {
|
|
1855
2042
|
this.tag.setValue(this.getTagFromVersion(value) || null);
|
|
1856
2043
|
}),
|
|
2044
|
+
// whenever the tag changes, update the step
|
|
1857
2045
|
this.tag.valueChanges
|
|
1858
2046
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
1859
2047
|
.subscribe((value) => {
|
|
1860
2048
|
this.step = this._result?.steps.find((step) => step.outputTag === value);
|
|
1861
|
-
if (this.step
|
|
2049
|
+
if (this.step) {
|
|
1862
2050
|
this.stepPick.emit(this.step);
|
|
1863
2051
|
}
|
|
1864
2052
|
}),
|
|
1865
2053
|
];
|
|
1866
2054
|
}
|
|
2055
|
+
selectInitialStep() {
|
|
2056
|
+
if (this._initialStepIndex !== undefined && this._result?.steps) {
|
|
2057
|
+
this.step =
|
|
2058
|
+
this._result.steps[this._initialStepIndex < 0
|
|
2059
|
+
? this._result.steps.length + this._initialStepIndex
|
|
2060
|
+
: this._initialStepIndex];
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
1867
2063
|
ngOnDestroy() {
|
|
1868
2064
|
this._subs?.forEach((sub) => sub.unsubscribe());
|
|
1869
2065
|
}
|
|
@@ -1960,13 +2156,14 @@ class ChainResultViewComponent {
|
|
|
1960
2156
|
this.selectionFeatures = features;
|
|
1961
2157
|
}
|
|
1962
2158
|
onStepChange(step) {
|
|
2159
|
+
// setting the tag will trigger the step change
|
|
1963
2160
|
this.tag.setValue(step.outputTag);
|
|
1964
2161
|
this.selectionFeatures = [];
|
|
1965
2162
|
}
|
|
1966
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1967
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2163
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: ChainResultViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2164
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: ChainResultViewComponent, isStandalone: true, selector: "gve-chain-result-view", inputs: { result: "result", initialStepIndex: "initialStepIndex" }, outputs: { stepPick: "stepPick" }, ngImport: i0, template: "@if (result) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n <mat-form-field *ngIf=\"versionTags?.length\">\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n <mat-option *ngFor=\"let t of versionTags\" [value]=\"t\">{{\r\n t\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- tag -->\r\n <mat-form-field *ngIf=\"tags?.length\">\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n <mat-option *ngFor=\"let t of tags\" [value]=\"t\">{{ t }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result.taggedNodes[step.outputTag]\"\r\n (charPick)=\"onStepCharPick($event)\"\r\n (rangePick)=\"onStepRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures.length) {\r\n <div class=\"feat-header\">{{ selection }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result.steps\"\r\n [selectedStep]=\"step\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: FeatureSetViewComponent, selector: "gve-feature-set-view", inputs: ["features", "featNames", "featValues", "filterThreshold"] }, { kind: "component", type: StepsMapComponent, selector: "gve-steps-map", inputs: ["steps", "selectedStep", "textFontSize"], outputs: ["selectedStepChange"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text"], outputs: ["charPick", "rangePick"] }] }); }
|
|
1968
2165
|
}
|
|
1969
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: ChainResultViewComponent, decorators: [{
|
|
1970
2167
|
type: Component,
|
|
1971
2168
|
args: [{ selector: 'gve-chain-result-view', standalone: true, imports: [
|
|
1972
2169
|
CommonModule,
|
|
@@ -1981,11 +2178,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1981
2178
|
], template: "@if (result) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n <mat-form-field *ngIf=\"versionTags?.length\">\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n <mat-option *ngFor=\"let t of versionTags\" [value]=\"t\">{{\r\n t\r\n }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- tag -->\r\n <mat-form-field *ngIf=\"tags?.length\">\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n <mat-option *ngFor=\"let t of tags\" [value]=\"t\">{{ t }}</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result.taggedNodes[step.outputTag]\"\r\n (charPick)=\"onStepCharPick($event)\"\r\n (rangePick)=\"onStepRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures.length) {\r\n <div class=\"feat-header\">{{ selection }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result.steps\"\r\n [selectedStep]=\"step\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"] }]
|
|
1982
2179
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { result: [{
|
|
1983
2180
|
type: Input
|
|
2181
|
+
}], initialStepIndex: [{
|
|
2182
|
+
type: Input
|
|
1984
2183
|
}], stepPick: [{
|
|
1985
2184
|
type: Output
|
|
1986
2185
|
}] } });
|
|
1987
2186
|
|
|
2187
|
+
/**
|
|
2188
|
+
* 🔑 `gve-ln-heights-editor`
|
|
2189
|
+
*
|
|
2190
|
+
* A component to edit line heights.
|
|
2191
|
+
* Used by the `gve-snapshot-editor` component.
|
|
2192
|
+
*
|
|
2193
|
+
* - ▶️ `lineCount` (`number`): the number of lines.
|
|
2194
|
+
* - ▶️ `heights` (`Record<number, number>`): the line heights.
|
|
2195
|
+
* - 🔥 `heightsChange` (`EventEmitter<Record<number, number>>`): the event
|
|
2196
|
+
* emitted when the heights change.
|
|
2197
|
+
*/
|
|
1988
2198
|
class LnHeightsEditorComponent {
|
|
2199
|
+
/**
|
|
2200
|
+
* The total number of lines in the text.
|
|
2201
|
+
*/
|
|
1989
2202
|
get lineCount() {
|
|
1990
2203
|
return this._lineCount;
|
|
1991
2204
|
}
|
|
@@ -1995,6 +2208,10 @@ class LnHeightsEditorComponent {
|
|
|
1995
2208
|
this._lineCount = value;
|
|
1996
2209
|
this.lineNumbers = Array.from({ length: value }, (_, i) => i + 1);
|
|
1997
2210
|
}
|
|
2211
|
+
/**
|
|
2212
|
+
* The heights map of the lines. Each key is a line number and the value is
|
|
2213
|
+
* the height of the line.
|
|
2214
|
+
*/
|
|
1998
2215
|
get heights() {
|
|
1999
2216
|
return this._heights;
|
|
2000
2217
|
}
|
|
@@ -2005,20 +2222,33 @@ class LnHeightsEditorComponent {
|
|
|
2005
2222
|
}
|
|
2006
2223
|
constructor(formBuilder) {
|
|
2007
2224
|
this._lineCount = 0;
|
|
2225
|
+
/**
|
|
2226
|
+
* The event emitted when the heights change.
|
|
2227
|
+
*/
|
|
2008
2228
|
this.heightsChange = new EventEmitter();
|
|
2009
2229
|
this.lineNumbers = [];
|
|
2010
2230
|
this.lineNumber = formBuilder.control(0, { nonNullable: true });
|
|
2011
2231
|
this.height = formBuilder.control(0, { nonNullable: true });
|
|
2012
2232
|
}
|
|
2233
|
+
pruneHeights() {
|
|
2234
|
+
// remove all the heigths with value=0
|
|
2235
|
+
this._heights = Object.fromEntries(Object.entries(this._heights || {}).filter(([_, v]) => v !== 0));
|
|
2236
|
+
// if heights are now empty, set to undefined
|
|
2237
|
+
if (Object.keys(this._heights).length === 0) {
|
|
2238
|
+
this._heights = undefined;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2013
2241
|
ngOnInit() {
|
|
2014
2242
|
this._subs = [];
|
|
2015
2243
|
// update heights[ln] when height changes and emit heightsChange
|
|
2016
2244
|
this._subs.push(this.height.valueChanges
|
|
2017
2245
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
2018
2246
|
.subscribe((value) => {
|
|
2019
|
-
if (!this._heights)
|
|
2020
|
-
|
|
2247
|
+
if (!this._heights) {
|
|
2248
|
+
this._heights = {};
|
|
2249
|
+
}
|
|
2021
2250
|
this._heights[this.lineNumber.value] = value;
|
|
2251
|
+
this.pruneHeights();
|
|
2022
2252
|
this.heightsChange.emit(this._heights);
|
|
2023
2253
|
}));
|
|
2024
2254
|
// update height when line number changes
|
|
@@ -2037,10 +2267,10 @@ class LnHeightsEditorComponent {
|
|
|
2037
2267
|
this._heights = undefined;
|
|
2038
2268
|
this.heightsChange.emit(undefined);
|
|
2039
2269
|
}
|
|
2040
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2041
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2270
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: LnHeightsEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2271
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: LnHeightsEditorComponent, isStandalone: true, selector: "gve-ln-heights-editor", inputs: { lineCount: "lineCount", heights: "heights" }, outputs: { heightsChange: "heightsChange" }, ngImport: i0, template: "@if (lineNumbers.length) {\r\n<div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n <mat-option *ngFor=\"let n of lineNumbers\" [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n @if (lineNumber.value) {\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n</div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
2042
2272
|
}
|
|
2043
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2273
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: LnHeightsEditorComponent, decorators: [{
|
|
2044
2274
|
type: Component,
|
|
2045
2275
|
args: [{ selector: 'gve-ln-heights-editor', standalone: true, imports: [
|
|
2046
2276
|
CommonModule,
|
|
@@ -2051,7 +2281,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2051
2281
|
MatInputModule,
|
|
2052
2282
|
MatSelectModule,
|
|
2053
2283
|
MatTooltipModule,
|
|
2054
|
-
], template: "@if (lineNumbers.length) {\r\n<div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n <mat-option *ngFor=\"let n of lineNumbers\" [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n</div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
|
|
2284
|
+
], template: "@if (lineNumbers.length) {\r\n<div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n <mat-option *ngFor=\"let n of lineNumbers\" [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n @if (lineNumber.value) {\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n</div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
|
|
2055
2285
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { lineCount: [{
|
|
2056
2286
|
type: Input
|
|
2057
2287
|
}], heights: [{
|
|
@@ -2060,7 +2290,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2060
2290
|
type: Output
|
|
2061
2291
|
}] } });
|
|
2062
2292
|
|
|
2293
|
+
/**
|
|
2294
|
+
* 🔑 `gve-animation-timeline-set`
|
|
2295
|
+
*
|
|
2296
|
+
* A component to edit a set of animation timelines.
|
|
2297
|
+
* Used by the `gve-snapshot-editor` component.
|
|
2298
|
+
*
|
|
2299
|
+
* - ▶️ `timelines` (`GveAnimationTimeline[]`): the animation timelines to edit.
|
|
2300
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected by the tween.
|
|
2301
|
+
* - ▶️ `tags` (`string[]`): the tags that can be used by the timeline.
|
|
2302
|
+
* - 🔥 `timelinesChange` (`GveAnimationTimeline[]`): emitted when the timelines are changed.
|
|
2303
|
+
* - 🔥 `timelinesCancel` (`void`): emitted when the timeline editing is canceled.
|
|
2304
|
+
*/
|
|
2063
2305
|
class AnimationTimelineSetComponent {
|
|
2306
|
+
/**
|
|
2307
|
+
* The animation timelines to edit.
|
|
2308
|
+
*/
|
|
2064
2309
|
get timelines() {
|
|
2065
2310
|
return this._timelines;
|
|
2066
2311
|
}
|
|
@@ -2074,9 +2319,18 @@ class AnimationTimelineSetComponent {
|
|
|
2074
2319
|
constructor(formBuilder, _dialogService) {
|
|
2075
2320
|
this._dialogService = _dialogService;
|
|
2076
2321
|
this._timelines = [];
|
|
2322
|
+
/**
|
|
2323
|
+
* The tags that can be used by the timeline.
|
|
2324
|
+
*/
|
|
2077
2325
|
this.tags = [];
|
|
2326
|
+
/**
|
|
2327
|
+
* Emitted when the timelines are changed.
|
|
2328
|
+
*/
|
|
2078
2329
|
this.timelinesChange = new EventEmitter();
|
|
2079
|
-
|
|
2330
|
+
/**
|
|
2331
|
+
* Emitted when the timeline editing is canceled.
|
|
2332
|
+
*/
|
|
2333
|
+
this.timelinesCancel = new EventEmitter();
|
|
2080
2334
|
this.set = formBuilder.control([], {
|
|
2081
2335
|
nonNullable: true,
|
|
2082
2336
|
});
|
|
@@ -2105,7 +2359,7 @@ class AnimationTimelineSetComponent {
|
|
|
2105
2359
|
editTimeline(index) {
|
|
2106
2360
|
this.editedTimeline = this._timelines[index];
|
|
2107
2361
|
}
|
|
2108
|
-
|
|
2362
|
+
onTimelineChange(timeline) {
|
|
2109
2363
|
const timelines = [...this.set.value];
|
|
2110
2364
|
const i = timelines.findIndex((t) => t.tag === timeline.tag);
|
|
2111
2365
|
if (i === -1) {
|
|
@@ -2138,10 +2392,10 @@ class AnimationTimelineSetComponent {
|
|
|
2138
2392
|
}
|
|
2139
2393
|
});
|
|
2140
2394
|
}
|
|
2141
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2142
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2395
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineSetComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2396
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: AnimationTimelineSetComponent, isStandalone: true, selector: "gve-animation-timeline-set", inputs: { timelines: "timelines", elementIds: "elementIds", tags: "tags" }, outputs: { timelinesChange: "timelinesChange", timelinesCancel: "timelinesCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\">\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of set.value; track t.tag; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds\"\r\n [tags]=\"tags\"\r\n [timeline]=\"editedTimeline\"\r\n (timelineChange)=\"onTimelineChange($event)\"\r\n (timelineCancel)=\"closeTimeline()\"\r\n />\r\n </mat-expansion-panel>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse;margin:8px 0}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i8$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i8$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: NgToolsModule }, { kind: "component", type: AnimationTimelineComponent, selector: "gve-animation-timeline", inputs: ["timeline", "elementIds", "tags"], outputs: ["timelineChange", "timelineCancel"] }] }); }
|
|
2143
2397
|
}
|
|
2144
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2398
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineSetComponent, decorators: [{
|
|
2145
2399
|
type: Component,
|
|
2146
2400
|
args: [{ selector: 'gve-animation-timeline-set', standalone: true, imports: [
|
|
2147
2401
|
CommonModule,
|
|
@@ -2157,7 +2411,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2157
2411
|
MatTooltipModule,
|
|
2158
2412
|
NgToolsModule,
|
|
2159
2413
|
AnimationTimelineComponent,
|
|
2160
|
-
], template: "<form [formGroup]=\"form\">\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of set.value; track t.tag; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds\"\r\n [tags]=\"tags\"\r\n [timeline]=\"editedTimeline\"\r\n (timelineChange)=\"
|
|
2414
|
+
], template: "<form [formGroup]=\"form\">\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of set.value; track t.tag; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds\"\r\n [tags]=\"tags\"\r\n [timeline]=\"editedTimeline\"\r\n (timelineChange)=\"onTimelineChange($event)\"\r\n (timelineCancel)=\"closeTimeline()\"\r\n />\r\n </mat-expansion-panel>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse;margin:8px 0}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"] }]
|
|
2161
2415
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.DialogService }], propDecorators: { timelines: [{
|
|
2162
2416
|
type: Input
|
|
2163
2417
|
}], elementIds: [{
|
|
@@ -2166,7 +2420,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2166
2420
|
type: Input
|
|
2167
2421
|
}], timelinesChange: [{
|
|
2168
2422
|
type: Output
|
|
2169
|
-
}],
|
|
2423
|
+
}], timelinesCancel: [{
|
|
2170
2424
|
type: Output
|
|
2171
2425
|
}] } });
|
|
2172
2426
|
|
|
@@ -2220,19 +2474,31 @@ class GveApiService {
|
|
|
2220
2474
|
})
|
|
2221
2475
|
.pipe(catchError(this._error.handleError));
|
|
2222
2476
|
}
|
|
2223
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2224
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.
|
|
2477
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: GveApiService, deps: [{ token: i1$1.HttpClient }, { token: i2$1.ErrorService }, { token: i2$1.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2478
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
|
|
2225
2479
|
}
|
|
2226
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2480
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: GveApiService, decorators: [{
|
|
2227
2481
|
type: Injectable,
|
|
2228
2482
|
args: [{
|
|
2229
2483
|
providedIn: 'root',
|
|
2230
2484
|
}]
|
|
2231
2485
|
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2$1.ErrorService }, { type: i2$1.EnvService }] });
|
|
2232
2486
|
|
|
2487
|
+
// default snapshot view title
|
|
2233
2488
|
const VIEW_TITLE = 'preview';
|
|
2489
|
+
// suffix for IDs of SVG elements to be made transparent at first rendition
|
|
2490
|
+
const SVG_TRANSP_SUFFIX = '_t';
|
|
2234
2491
|
/**
|
|
2235
|
-
*
|
|
2492
|
+
* 🔑 `gve-snapshot-editor`
|
|
2493
|
+
*
|
|
2494
|
+
* A component to edit a text snapshot. This is the top level component in the GVE core
|
|
2495
|
+
* library.
|
|
2496
|
+
*
|
|
2497
|
+
* - ▶️ `snapshot` (`Snapshot`): the snapshot to edit.
|
|
2498
|
+
* - ▶️ `batchOps` (`string`): the batch operations text to set for the editor.
|
|
2499
|
+
* - ▶️ `noSave` (`boolean`): true to disable saving.
|
|
2500
|
+
* - 🔥 `snapshotChange` (`Snapshot`): emitted when the user saves the edited snapshot.
|
|
2501
|
+
* - 🔥 `snapshotCancel` (`void`): emitted when the user cancels the snapshot editing.
|
|
2236
2502
|
*/
|
|
2237
2503
|
class SnapshotEditorComponent {
|
|
2238
2504
|
/**
|
|
@@ -2247,7 +2513,9 @@ class SnapshotEditorComponent {
|
|
|
2247
2513
|
}
|
|
2248
2514
|
this._snapshot = value || undefined;
|
|
2249
2515
|
this.updateForm(this._snapshot);
|
|
2250
|
-
|
|
2516
|
+
setTimeout(() => {
|
|
2517
|
+
this.runToLast();
|
|
2518
|
+
}, 0);
|
|
2251
2519
|
}
|
|
2252
2520
|
constructor(formBuilder, _api, _dialogService, _snackbar) {
|
|
2253
2521
|
this._api = _api;
|
|
@@ -2262,9 +2530,13 @@ class SnapshotEditorComponent {
|
|
|
2262
2530
|
* Emitted when the user cancels the snapshot editing.
|
|
2263
2531
|
*/
|
|
2264
2532
|
this.snapshotCancel = new EventEmitter();
|
|
2265
|
-
|
|
2533
|
+
// list of operations output tags
|
|
2266
2534
|
this.opTags = [];
|
|
2535
|
+
// list of operation diplomatic.g element IDs
|
|
2267
2536
|
this.opElementIds = [];
|
|
2537
|
+
// the lines count for the current base text
|
|
2538
|
+
this.lineCount = 0;
|
|
2539
|
+
this.editedOpIndex = -1;
|
|
2268
2540
|
this.opTypeMap = {
|
|
2269
2541
|
0: 'replace',
|
|
2270
2542
|
1: 'delete',
|
|
@@ -2275,9 +2547,10 @@ class SnapshotEditorComponent {
|
|
|
2275
2547
|
6: 'swap',
|
|
2276
2548
|
7: 'annotate',
|
|
2277
2549
|
};
|
|
2278
|
-
|
|
2550
|
+
// snapshot view data
|
|
2279
2551
|
this.viewTitle = VIEW_TITLE;
|
|
2280
2552
|
this.rulers = true;
|
|
2553
|
+
this.initialStepIndex = -1;
|
|
2281
2554
|
// general
|
|
2282
2555
|
this.width = new FormControl(800, { nonNullable: true });
|
|
2283
2556
|
this.height = new FormControl(600, { nonNullable: true });
|
|
@@ -2312,6 +2585,7 @@ class SnapshotEditorComponent {
|
|
|
2312
2585
|
this.timelines = new FormControl([], {
|
|
2313
2586
|
nonNullable: true,
|
|
2314
2587
|
});
|
|
2588
|
+
// form
|
|
2315
2589
|
this.form = formBuilder.group({
|
|
2316
2590
|
width: this.width,
|
|
2317
2591
|
height: this.height,
|
|
@@ -2340,42 +2614,44 @@ class SnapshotEditorComponent {
|
|
|
2340
2614
|
});
|
|
2341
2615
|
}
|
|
2342
2616
|
ngOnInit() {
|
|
2617
|
+
// if there are any batch operations, parse them into snapshot operations
|
|
2343
2618
|
if (this.batchOps) {
|
|
2344
2619
|
this.inputOps.setValue(this.batchOps);
|
|
2345
2620
|
this.parseOperations();
|
|
2346
2621
|
}
|
|
2347
2622
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2623
|
+
/**
|
|
2624
|
+
* Set the view data for the snapshot view.
|
|
2625
|
+
*
|
|
2626
|
+
* @param snapshot The optional snapshot data to use. If not provided,
|
|
2627
|
+
* a snapshot will be created from the form data.
|
|
2628
|
+
* @param title The optional title to set for the view.
|
|
2629
|
+
*/
|
|
2630
|
+
setViewData(snapshot, title = VIEW_TITLE) {
|
|
2631
|
+
console.log('set view data');
|
|
2632
|
+
// get the snapshot to use
|
|
2350
2633
|
if (!snapshot) {
|
|
2351
2634
|
snapshot = this.getSnapshot();
|
|
2352
2635
|
}
|
|
2353
|
-
// update or add new operation in copy
|
|
2354
|
-
if (newOperation) {
|
|
2355
|
-
const index = snapshot.operations.findIndex((op) => op.id === newOperation.id);
|
|
2356
|
-
if (index > -1) {
|
|
2357
|
-
snapshot.operations.splice(index, 1, newOperation);
|
|
2358
|
-
}
|
|
2359
|
-
else {
|
|
2360
|
-
snapshot.operations.push(newOperation);
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
2636
|
// update view data
|
|
2364
2637
|
this.viewTitle = title;
|
|
2365
2638
|
this.visualInfo = undefined;
|
|
2366
2639
|
this.viewData = {
|
|
2367
2640
|
snapshot: snapshot,
|
|
2368
2641
|
options: {
|
|
2369
|
-
|
|
2642
|
+
debug: this.debug,
|
|
2370
2643
|
delayedRender: true,
|
|
2371
2644
|
showRulers: true,
|
|
2372
2645
|
showGrid: true,
|
|
2373
2646
|
panZoom: true,
|
|
2647
|
+
transparentIds: this._transparentIds,
|
|
2374
2648
|
},
|
|
2375
2649
|
};
|
|
2376
|
-
console.log(
|
|
2650
|
+
console.log('view data: ', this.viewData);
|
|
2377
2651
|
}
|
|
2378
2652
|
updateForm(snapshot) {
|
|
2653
|
+
this._transparentIds = undefined;
|
|
2654
|
+
this.initialStepIndex = -1;
|
|
2379
2655
|
if (!snapshot) {
|
|
2380
2656
|
this.form.reset();
|
|
2381
2657
|
}
|
|
@@ -2410,9 +2686,74 @@ class SnapshotEditorComponent {
|
|
|
2410
2686
|
}
|
|
2411
2687
|
this.timelines.setValue(timelines);
|
|
2412
2688
|
}
|
|
2689
|
+
this.updateLineCount(this.baseText.value);
|
|
2690
|
+
this.updateOpLists();
|
|
2413
2691
|
this.form.markAsPristine();
|
|
2414
2692
|
}
|
|
2693
|
+
// #region base text
|
|
2694
|
+
/**
|
|
2695
|
+
* Update the line count based on the received text.
|
|
2696
|
+
* @param text The text to use for counting lines.
|
|
2697
|
+
*/
|
|
2698
|
+
updateLineCount(text) {
|
|
2699
|
+
if (!text.length) {
|
|
2700
|
+
this.lineCount = 0;
|
|
2701
|
+
}
|
|
2702
|
+
else {
|
|
2703
|
+
this.lineCount = text.filter((c) => c.data === '\n').length + 1;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* Handle the event fired by the base text editor to pick a text range.
|
|
2708
|
+
* @param range The picked range.
|
|
2709
|
+
*/
|
|
2710
|
+
onRangePick(range) {
|
|
2711
|
+
this.textRange = range;
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Handle the event fired by the base text editor to change the base text.
|
|
2715
|
+
* @param text The text to set.
|
|
2716
|
+
*/
|
|
2717
|
+
onTextChange(text) {
|
|
2718
|
+
console.log('text change: ' + text);
|
|
2719
|
+
// update the form control and reset the current text range
|
|
2720
|
+
this.baseText.setValue(text);
|
|
2721
|
+
this.baseText.updateValueAndValidity();
|
|
2722
|
+
this.baseText.markAsDirty();
|
|
2723
|
+
this.textRange = undefined;
|
|
2724
|
+
// update the line count
|
|
2725
|
+
this.updateLineCount(text);
|
|
2726
|
+
// remove all operations and update the view data
|
|
2727
|
+
this.removeAllOperations();
|
|
2728
|
+
}
|
|
2729
|
+
// #endregion
|
|
2730
|
+
// #region operations
|
|
2731
|
+
/**
|
|
2732
|
+
* Update the lists of operation output tags and element IDs by collecting
|
|
2733
|
+
* all the operation tags and the IDs of the elements in their diplomatic.g
|
|
2734
|
+
* SVG code if any.
|
|
2735
|
+
*/
|
|
2736
|
+
updateOpLists() {
|
|
2737
|
+
const tags = new Set();
|
|
2738
|
+
const ids = new Set();
|
|
2739
|
+
let n = 0;
|
|
2740
|
+
for (const op of this.operations.value) {
|
|
2741
|
+
// output tag
|
|
2742
|
+
n++;
|
|
2743
|
+
tags.add(op.outputTag || `v${n}`);
|
|
2744
|
+
// element IDs
|
|
2745
|
+
if (op.diplomatics?.g) {
|
|
2746
|
+
this.parseSvgIds(op.diplomatics.g)?.forEach((id) => ids.add(id));
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
this.opTags = [...tags];
|
|
2750
|
+
this.opElementIds = [...ids];
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Edit a new operation.
|
|
2754
|
+
*/
|
|
2415
2755
|
editNewOperation() {
|
|
2756
|
+
// create a new operation and edit it
|
|
2416
2757
|
this.editedOp = {
|
|
2417
2758
|
id: this._nanoid(),
|
|
2418
2759
|
type: 0,
|
|
@@ -2421,66 +2762,91 @@ class SnapshotEditorComponent {
|
|
|
2421
2762
|
};
|
|
2422
2763
|
this.editedOpIndex = -1;
|
|
2423
2764
|
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Edit (a copy of) the operation at the specified index.
|
|
2767
|
+
* @param index The operation index.
|
|
2768
|
+
*/
|
|
2424
2769
|
editOperation(index) {
|
|
2425
2770
|
this.editedOpIndex = index;
|
|
2426
2771
|
this.editedOp = deepCopy(this.operations.value[index]);
|
|
2427
2772
|
}
|
|
2428
|
-
|
|
2773
|
+
/**
|
|
2774
|
+
* Close the currently edited operation.
|
|
2775
|
+
*/
|
|
2776
|
+
closeEditedOperation() {
|
|
2429
2777
|
this.editedOpIndex = -1;
|
|
2430
2778
|
this.editedOp = undefined;
|
|
2431
2779
|
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Handle the event fired by the operation editor to cancel
|
|
2782
|
+
* the current operation edits.
|
|
2783
|
+
*/
|
|
2432
2784
|
onOperationCancel() {
|
|
2433
|
-
this.
|
|
2785
|
+
this.closeEditedOperation();
|
|
2434
2786
|
}
|
|
2435
|
-
|
|
2787
|
+
/**
|
|
2788
|
+
* Handle the event fired by the operation editor to change
|
|
2789
|
+
* the currently edited operation, or add a new one if that
|
|
2790
|
+
* was a new operation.
|
|
2791
|
+
* @param op The changed operation.
|
|
2792
|
+
*/
|
|
2793
|
+
async onOperationChange(op) {
|
|
2436
2794
|
console.log('operation change');
|
|
2437
2795
|
const operations = [...this.operations.value];
|
|
2796
|
+
// replace or add the operation
|
|
2797
|
+
let i = this.editedOpIndex;
|
|
2438
2798
|
if (this.editedOpIndex > -1) {
|
|
2439
2799
|
operations.splice(this.editedOpIndex, 1, op);
|
|
2440
2800
|
}
|
|
2441
2801
|
else {
|
|
2442
2802
|
operations.push(op);
|
|
2803
|
+
i = operations.length - 1;
|
|
2443
2804
|
}
|
|
2805
|
+
// update the operations form control
|
|
2444
2806
|
this.operations.setValue(operations);
|
|
2445
2807
|
this.operations.markAsDirty();
|
|
2446
2808
|
this.operations.updateValueAndValidity();
|
|
2447
|
-
|
|
2809
|
+
// update the operation lists
|
|
2810
|
+
this.updateOpLists();
|
|
2811
|
+
// update the view data
|
|
2812
|
+
await this.runTo(i);
|
|
2813
|
+
// close the edited operation
|
|
2814
|
+
this.closeEditedOperation();
|
|
2448
2815
|
}
|
|
2816
|
+
/**
|
|
2817
|
+
* Delete the operation at the specified index.
|
|
2818
|
+
* @param index The index of the operation to delete.
|
|
2819
|
+
*/
|
|
2449
2820
|
deleteOperation(index) {
|
|
2450
2821
|
this._dialogService
|
|
2451
2822
|
.confirm('Confirm Deletion', `Delete operation "${this.operations.value[index].id}"?`)
|
|
2452
2823
|
.subscribe((yes) => {
|
|
2453
2824
|
if (yes) {
|
|
2825
|
+
// close the edited operation if it is the one being deleted
|
|
2826
|
+
if (this.editedOpIndex === index) {
|
|
2827
|
+
this.closeEditedOperation();
|
|
2828
|
+
}
|
|
2829
|
+
// reset the result operation ID if it is the one being deleted
|
|
2454
2830
|
if (this.resultOperationId === this.operations.value[index].id) {
|
|
2455
2831
|
this.resultOperationId = undefined;
|
|
2456
2832
|
}
|
|
2833
|
+
// delete the operation and update the form control
|
|
2457
2834
|
const operations = [...this.operations.value];
|
|
2458
2835
|
operations.splice(index, 1);
|
|
2459
2836
|
this.operations.setValue(operations);
|
|
2460
2837
|
this.operations.markAsDirty();
|
|
2461
2838
|
this.operations.updateValueAndValidity();
|
|
2839
|
+
// update the operation lists
|
|
2840
|
+
this.updateOpLists();
|
|
2841
|
+
// update the view data
|
|
2842
|
+
this.runToLast();
|
|
2462
2843
|
}
|
|
2463
2844
|
});
|
|
2464
2845
|
}
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
this.baseText.markAsDirty();
|
|
2470
|
-
if (!text.length) {
|
|
2471
|
-
this.lineCount = 0;
|
|
2472
|
-
}
|
|
2473
|
-
else {
|
|
2474
|
-
this.lineCount = text.filter((c) => c.data === '\n').length + 1;
|
|
2475
|
-
}
|
|
2476
|
-
this.textRange = undefined;
|
|
2477
|
-
this.closeEditedOp();
|
|
2478
|
-
this.operations.reset();
|
|
2479
|
-
this.updateViewData();
|
|
2480
|
-
}
|
|
2481
|
-
onRangePick(range) {
|
|
2482
|
-
this.textRange = range;
|
|
2483
|
-
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Parse the operations from their text and append them to the current
|
|
2848
|
+
* snapshot operations.
|
|
2849
|
+
*/
|
|
2484
2850
|
parseOperations() {
|
|
2485
2851
|
if (this.busy || !this.inputOps.value) {
|
|
2486
2852
|
return;
|
|
@@ -2498,7 +2864,10 @@ class SnapshotEditorComponent {
|
|
|
2498
2864
|
this.operations.setValue(operations);
|
|
2499
2865
|
this.operations.markAsDirty();
|
|
2500
2866
|
this.operations.updateValueAndValidity();
|
|
2501
|
-
|
|
2867
|
+
// update the operation lists
|
|
2868
|
+
this.updateOpLists();
|
|
2869
|
+
// update the view data
|
|
2870
|
+
this.runToLast();
|
|
2502
2871
|
},
|
|
2503
2872
|
error: (error) => {
|
|
2504
2873
|
this.parseError = error.message;
|
|
@@ -2510,103 +2879,257 @@ class SnapshotEditorComponent {
|
|
|
2510
2879
|
}
|
|
2511
2880
|
});
|
|
2512
2881
|
}
|
|
2882
|
+
removeAllOperations() {
|
|
2883
|
+
this.resultOperationId = undefined;
|
|
2884
|
+
this.closeEditedOperation();
|
|
2885
|
+
this.operations.reset();
|
|
2886
|
+
this.operations.markAsDirty();
|
|
2887
|
+
this.operations.updateValueAndValidity();
|
|
2888
|
+
this.opTags = [];
|
|
2889
|
+
this.opElementIds = [];
|
|
2890
|
+
this.setViewData();
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Remove all the operations.
|
|
2894
|
+
*/
|
|
2513
2895
|
clearOperations() {
|
|
2514
2896
|
this._dialogService
|
|
2515
2897
|
.confirm('Confirm', 'Remove all operations?')
|
|
2516
2898
|
.subscribe((yes) => {
|
|
2517
2899
|
if (yes) {
|
|
2518
|
-
this.
|
|
2519
|
-
this.operations.reset();
|
|
2520
|
-
this.operations.markAsDirty();
|
|
2521
|
-
this.operations.updateValueAndValidity();
|
|
2522
|
-
this.updateViewData();
|
|
2900
|
+
this.removeAllOperations();
|
|
2523
2901
|
}
|
|
2524
2902
|
});
|
|
2525
2903
|
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Parse all the id attributes from the received SVG code and
|
|
2906
|
+
* return them as an array.
|
|
2907
|
+
*
|
|
2908
|
+
* @param svg The SVG content to parse or undefined.
|
|
2909
|
+
* @returns Array of IDs found in the SVG content or undefined.
|
|
2910
|
+
*/
|
|
2911
|
+
parseSvgIds(svg) {
|
|
2912
|
+
if (!svg) {
|
|
2913
|
+
return undefined;
|
|
2914
|
+
}
|
|
2915
|
+
const regex = /<[^>]+\bid=(["'])(.*?)\1/g;
|
|
2916
|
+
const ids = [];
|
|
2917
|
+
let match;
|
|
2918
|
+
while ((match = regex.exec(svg)) !== null) {
|
|
2919
|
+
ids.push(match[2]);
|
|
2920
|
+
}
|
|
2921
|
+
return ids;
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* Run the operations up to the specified index (included).
|
|
2925
|
+
* This is called when:
|
|
2926
|
+
* - a preview is requested by the operation editor.
|
|
2927
|
+
* - the currently edited operation is saved.
|
|
2928
|
+
* - the user picks a step in the chain result view.
|
|
2929
|
+
* - runToLast is called, which happens when:
|
|
2930
|
+
* - setting the snapshot.
|
|
2931
|
+
* - parsing a batch of operations.
|
|
2932
|
+
* - deleting an operation.
|
|
2933
|
+
*
|
|
2934
|
+
* @param index The index of the operation to run to.
|
|
2935
|
+
* @param lastOperation The operation to use in place of the existing
|
|
2936
|
+
* operation in the snapshot at index. This is used when previewing
|
|
2937
|
+
* the edited operation from within the operation editor.
|
|
2938
|
+
*/
|
|
2939
|
+
runTo(index, lastOperation) {
|
|
2940
|
+
return new Promise((resolve, reject) => {
|
|
2941
|
+
if (this.busy) {
|
|
2942
|
+
return reject('Busy');
|
|
2943
|
+
}
|
|
2944
|
+
console.log('run to: ' + index);
|
|
2945
|
+
// get the snapshot to run operations on
|
|
2946
|
+
const snapshot = this.getSnapshot();
|
|
2947
|
+
if (!snapshot.text) {
|
|
2948
|
+
return reject('No snapshot text');
|
|
2949
|
+
}
|
|
2950
|
+
// run operations up to the specified index, but replace the last
|
|
2951
|
+
// operation when this was received as a parameter
|
|
2952
|
+
const operations = [...this.operations.value];
|
|
2953
|
+
if (lastOperation) {
|
|
2954
|
+
operations.slice(0, index);
|
|
2955
|
+
operations.push(lastOperation);
|
|
2956
|
+
}
|
|
2957
|
+
else {
|
|
2958
|
+
operations.slice(0, index + 1);
|
|
2959
|
+
}
|
|
2960
|
+
this.busy = true;
|
|
2961
|
+
this.initialStepIndex = index;
|
|
2962
|
+
this._api
|
|
2963
|
+
.runOperations(snapshot.text, operations)
|
|
2964
|
+
.subscribe({
|
|
2965
|
+
next: (wrapper) => {
|
|
2966
|
+
// handle operation (non-fatal) error or result
|
|
2967
|
+
if (wrapper.error) {
|
|
2968
|
+
this._snackbar.open(wrapper.error, 'OK');
|
|
2969
|
+
reject(wrapper.error);
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
// extract the IDs from the last operation's diplomatics and filter
|
|
2973
|
+
// them so that only the ones ending with _t are kept. By convention,
|
|
2974
|
+
// all the IDs ending with this suffix are to be made invisible at
|
|
2975
|
+
// their first rendition (opacity=0). An animation will then make
|
|
2976
|
+
// them visible.
|
|
2977
|
+
this._transparentIds = this.parseSvgIds(operations[index].diplomatics?.g)?.filter((id) => id.endsWith(SVG_TRANSP_SUFFIX));
|
|
2978
|
+
this.setViewData(snapshot, snapshot.operations[index].outputTag);
|
|
2979
|
+
this.result = wrapper.result;
|
|
2980
|
+
resolve();
|
|
2981
|
+
},
|
|
2982
|
+
error: (error) => {
|
|
2983
|
+
console.error(error);
|
|
2984
|
+
this._transparentIds = undefined;
|
|
2985
|
+
this._snackbar.open('Error running operations', 'OK');
|
|
2986
|
+
reject(error);
|
|
2987
|
+
},
|
|
2988
|
+
complete: () => {
|
|
2989
|
+
this.busy = false;
|
|
2990
|
+
},
|
|
2991
|
+
});
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2994
|
+
/**
|
|
2995
|
+
* Run the operations up to the last operation if any.
|
|
2996
|
+
* Otherwise, just update the snapshot view.
|
|
2997
|
+
*/
|
|
2998
|
+
runToLast() {
|
|
2999
|
+
if (this.operations.value.length) {
|
|
3000
|
+
this.runTo(this.operations.value.length - 1);
|
|
3001
|
+
}
|
|
3002
|
+
else {
|
|
3003
|
+
this.setViewData();
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Update the snapshot view by running the operations up to the
|
|
3008
|
+
* currently edited operation if any.
|
|
3009
|
+
*
|
|
3010
|
+
* @param operation The operation being previewed.
|
|
3011
|
+
*/
|
|
2526
3012
|
onOperationPreview(operation) {
|
|
2527
|
-
|
|
3013
|
+
// no multiple previews or previewing a new operation
|
|
3014
|
+
if (this._previewing || this.editedOpIndex < 0) {
|
|
2528
3015
|
return;
|
|
2529
3016
|
}
|
|
2530
3017
|
this._previewing = true;
|
|
2531
3018
|
setTimeout(() => {
|
|
2532
|
-
this.
|
|
3019
|
+
this.runTo(this.editedOpIndex, operation);
|
|
2533
3020
|
this._previewing = false;
|
|
2534
3021
|
}, 0);
|
|
2535
3022
|
}
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
this.result = wrapper.result;
|
|
2550
|
-
// TODO update view
|
|
2551
|
-
// this.updateViewData(
|
|
2552
|
-
// undefined,
|
|
2553
|
-
// snapshot,
|
|
2554
|
-
// this.operations.value[index].outputTag
|
|
2555
|
-
// );
|
|
2556
|
-
},
|
|
2557
|
-
error: (error) => {
|
|
2558
|
-
console.error(error);
|
|
2559
|
-
this._snackbar.open('Error running operations', 'OK');
|
|
2560
|
-
},
|
|
2561
|
-
complete: () => {
|
|
2562
|
-
this.busy = false;
|
|
2563
|
-
},
|
|
2564
|
-
});
|
|
2565
|
-
}
|
|
2566
|
-
onStepPick(step) {
|
|
3023
|
+
// #endregion
|
|
3024
|
+
/**
|
|
3025
|
+
* Build the snapshot at the specified execution step. This implies adding
|
|
3026
|
+
* the nodes introduced by the operations up to the specified step, and
|
|
3027
|
+
* updating the features of the nodes introduced by the specified step.
|
|
3028
|
+
* Also, the operations after the specified step are removed from the
|
|
3029
|
+
* snapshot.
|
|
3030
|
+
*
|
|
3031
|
+
* @param step The step to build the snapshot at.
|
|
3032
|
+
* @returns The snapshot at the specified step.
|
|
3033
|
+
*/
|
|
3034
|
+
buildSnapshotAtStep(step) {
|
|
3035
|
+
// no result, nothing to do
|
|
2567
3036
|
if (!this.result) {
|
|
2568
|
-
return;
|
|
3037
|
+
return null;
|
|
2569
3038
|
}
|
|
2570
|
-
|
|
2571
|
-
// get snapshot for preview by updating its text and
|
|
2572
|
-
// removing operations past the last executed
|
|
2573
|
-
const stepNodes = this.result.taggedNodes[step.outputTag];
|
|
2574
|
-
// we always display the original base text, just updating
|
|
2575
|
-
// its nodes features to those of the picked step, and adding
|
|
2576
|
-
// new nodes if they are not in the original text. It is assumed
|
|
2577
|
-
// that these new nodes have a manually set position.
|
|
3039
|
+
// get the currently edited snapshot
|
|
2578
3040
|
const snapshot = this.getSnapshot();
|
|
2579
|
-
|
|
3041
|
+
// find the max ID in snapshot.text (=original) nodes,
|
|
3042
|
+
// so that any ID greater than it belongs to new nodes
|
|
3043
|
+
const maxOriginalId = snapshot.text.reduce((max, n) => Math.max(max, n.id), 0);
|
|
3044
|
+
// get a copy of the nodes of the snapshot base text
|
|
3045
|
+
const snapNodes = deepCopy(snapshot.text);
|
|
3046
|
+
// get the nodes of the picked step
|
|
3047
|
+
const stepNodes = this.result.taggedNodes[step.outputTag];
|
|
3048
|
+
// update the snapshot text nodes copies with the picked step nodes,
|
|
3049
|
+
// by updating their features if existing, or adding new nodes if not.
|
|
2580
3050
|
for (const sn of stepNodes) {
|
|
2581
|
-
|
|
3051
|
+
// find the node in the original snapshot text
|
|
3052
|
+
const i = snapNodes.findIndex((n) => n.id === sn.id);
|
|
3053
|
+
// if found, update its features, else add it
|
|
2582
3054
|
if (i > -1) {
|
|
2583
|
-
|
|
3055
|
+
snapNodes[i].features = sn.features;
|
|
2584
3056
|
}
|
|
2585
3057
|
else {
|
|
2586
|
-
|
|
3058
|
+
// new node: we assume that it has a manually set position (x,y features)
|
|
3059
|
+
snapNodes.push(sn);
|
|
2587
3060
|
}
|
|
2588
3061
|
}
|
|
2589
|
-
//
|
|
2590
|
-
// so that any ID greater than it belongs to new nodes
|
|
2591
|
-
const maxOriginalId = snapshot.text.reduce((max, n) => Math.max(max, n.id), 0);
|
|
2592
|
-
// append nodes introduced before the picked step if any
|
|
3062
|
+
// append nodes introduced *before* the picked step, if any
|
|
2593
3063
|
// (those introduced by the picked step have already been added)
|
|
2594
3064
|
const stepIndex = this.result.steps.indexOf(step);
|
|
2595
3065
|
for (let i = 0; i < stepIndex; i++) {
|
|
2596
3066
|
const stepNodes = this.result.taggedNodes[this.result.steps[i].outputTag];
|
|
2597
3067
|
for (const n of stepNodes) {
|
|
2598
|
-
if (n.id > maxOriginalId && !
|
|
2599
|
-
|
|
3068
|
+
if (n.id > maxOriginalId && !snapNodes.find((x) => x.id === n.id)) {
|
|
3069
|
+
snapNodes.push(n);
|
|
2600
3070
|
}
|
|
2601
3071
|
}
|
|
2602
3072
|
}
|
|
2603
|
-
snapshot
|
|
3073
|
+
// update the snapshot text nodes
|
|
3074
|
+
snapshot.text = snapNodes;
|
|
3075
|
+
// remove from the snapshot the operations after the picked step
|
|
2604
3076
|
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
2605
3077
|
if (i > -1) {
|
|
2606
3078
|
snapshot.operations = snapshot.operations.slice(0, i + 1);
|
|
2607
3079
|
}
|
|
2608
|
-
|
|
3080
|
+
return snapshot;
|
|
3081
|
+
}
|
|
3082
|
+
playTimeline(tl) {
|
|
3083
|
+
const shadowRoot = this.snapshotView?.nativeElement?.shadowRoot;
|
|
3084
|
+
if (tl && shadowRoot) {
|
|
3085
|
+
console.log('play timeline', tl);
|
|
3086
|
+
this._renderer?.playTimeline(tl, undefined, shadowRoot);
|
|
3087
|
+
}
|
|
3088
|
+
else {
|
|
3089
|
+
if (!this.snapshotView) {
|
|
3090
|
+
console.warn('no snapshotView for timeline');
|
|
3091
|
+
}
|
|
3092
|
+
else {
|
|
3093
|
+
console.warn('no shadowRoot for timeline');
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Handle the event fired by the chain result view to pick a step.
|
|
3099
|
+
*
|
|
3100
|
+
* @param step The step to pick.
|
|
3101
|
+
*/
|
|
3102
|
+
async onStepPick(step) {
|
|
3103
|
+
if (!this.result || this._stepPickFrozen) {
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
// get the currently edited snapshot
|
|
3107
|
+
const snapshot = this.buildSnapshotAtStep(step);
|
|
3108
|
+
// update result operation ID
|
|
3109
|
+
this.resultOperationId = step.operation.id;
|
|
3110
|
+
// update view data
|
|
3111
|
+
this.setViewData(snapshot, step.outputTag);
|
|
3112
|
+
// play animation if any
|
|
3113
|
+
const tl = this.timelines.value.find((t) => t.tag === step.outputTag);
|
|
3114
|
+
if (tl) {
|
|
3115
|
+
this._stepPickFrozen = true;
|
|
3116
|
+
// find the index of the picked step in the snapshot operations
|
|
3117
|
+
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
3118
|
+
// run the operations up to the picked step as we need to hide
|
|
3119
|
+
// those visuals to be revealed by the animation
|
|
3120
|
+
await this.runTo(i);
|
|
3121
|
+
this._stepPickFrozen = false;
|
|
3122
|
+
// play the timeline
|
|
3123
|
+
setTimeout(() => {
|
|
3124
|
+
this.playTimeline(tl);
|
|
3125
|
+
}, 0);
|
|
3126
|
+
}
|
|
2609
3127
|
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Handle the event fired by a visual in the snapshot view.
|
|
3130
|
+
*
|
|
3131
|
+
* @param event The event.
|
|
3132
|
+
*/
|
|
2610
3133
|
onVisualEvent(event) {
|
|
2611
3134
|
const d = event.detail;
|
|
2612
3135
|
if (d.event.type === 'mouseover') {
|
|
@@ -2619,6 +3142,7 @@ class SnapshotEditorComponent {
|
|
|
2619
3142
|
sb.push(visual.data.label);
|
|
2620
3143
|
}
|
|
2621
3144
|
if (visual.data?.features?.length) {
|
|
3145
|
+
sb.push(' ');
|
|
2622
3146
|
sb.push(visual.data.features
|
|
2623
3147
|
.map((f) => `${f.name}=${f.value}`)
|
|
2624
3148
|
.join('\n'));
|
|
@@ -2626,23 +3150,55 @@ class SnapshotEditorComponent {
|
|
|
2626
3150
|
this.visualInfo = sb.join('');
|
|
2627
3151
|
}
|
|
2628
3152
|
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Handle the change of line heights by updating the form control.
|
|
3155
|
+
*
|
|
3156
|
+
* @param heights The line heights.
|
|
3157
|
+
*/
|
|
2629
3158
|
onHeightsChange(heights) {
|
|
2630
3159
|
this.lnHeights.setValue(heights || null);
|
|
2631
3160
|
this.lnHeights.markAsDirty();
|
|
2632
3161
|
this.lnHeights.updateValueAndValidity();
|
|
2633
3162
|
}
|
|
3163
|
+
/**
|
|
3164
|
+
* Handle the change of timelines by updating the form control.
|
|
3165
|
+
*
|
|
3166
|
+
* @param timelines The timelines.
|
|
3167
|
+
*/
|
|
3168
|
+
onTimelinesChange(timelines) {
|
|
3169
|
+
this.timelines.setValue(timelines);
|
|
3170
|
+
this.timelines.markAsDirty();
|
|
3171
|
+
this.timelines.updateValueAndValidity();
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Emit the cancel event for this snapshot edit.
|
|
3175
|
+
*/
|
|
2634
3176
|
close() {
|
|
2635
3177
|
this.snapshotCancel.emit();
|
|
2636
3178
|
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Handle the render event from the snapshot view to get a reference
|
|
3181
|
+
* to the renderer.
|
|
3182
|
+
*
|
|
3183
|
+
* @param event The rendition event.
|
|
3184
|
+
*/
|
|
2637
3185
|
onSnapshotRender(event) {
|
|
2638
3186
|
this._renderer = event.detail.renderer;
|
|
2639
3187
|
}
|
|
3188
|
+
/**
|
|
3189
|
+
* Toggle rulers in the snapshot view.
|
|
3190
|
+
*/
|
|
2640
3191
|
toggleRulers() {
|
|
2641
3192
|
if (!this._renderer) {
|
|
2642
3193
|
return;
|
|
2643
3194
|
}
|
|
2644
3195
|
this.rulers = this._renderer.toggleRulers();
|
|
2645
3196
|
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Get a snapshot from the form data.
|
|
3199
|
+
*
|
|
3200
|
+
* @returns New snapshot object.
|
|
3201
|
+
*/
|
|
2646
3202
|
getSnapshot() {
|
|
2647
3203
|
const snapshot = {
|
|
2648
3204
|
size: {
|
|
@@ -2665,7 +3221,6 @@ class SnapshotEditorComponent {
|
|
|
2665
3221
|
},
|
|
2666
3222
|
operations: [...this.operations.value],
|
|
2667
3223
|
opStyle: this.opStyle.value || undefined,
|
|
2668
|
-
timelines: {}, // TODO
|
|
2669
3224
|
};
|
|
2670
3225
|
// image
|
|
2671
3226
|
if (this.imageUrl.value) {
|
|
@@ -2697,11 +3252,10 @@ class SnapshotEditorComponent {
|
|
|
2697
3252
|
}
|
|
2698
3253
|
return snapshot;
|
|
2699
3254
|
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
}
|
|
3255
|
+
/**
|
|
3256
|
+
* Get a snapshot from the form data and emit it
|
|
3257
|
+
* in the snapshot change event.
|
|
3258
|
+
*/
|
|
2705
3259
|
save() {
|
|
2706
3260
|
if (this.form.invalid || this.noSave) {
|
|
2707
3261
|
return;
|
|
@@ -2709,10 +3263,10 @@ class SnapshotEditorComponent {
|
|
|
2709
3263
|
this._snapshot = this.getSnapshot();
|
|
2710
3264
|
this.snapshotChange.emit(this._snapshot);
|
|
2711
3265
|
}
|
|
2712
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2713
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.6", type: SnapshotEditorComponent, isStandalone: true, selector: "gve-snapshot-editor", inputs: { snapshot: "snapshot", batchOps: "batchOps", noSave: "noSave" }, outputs: { snapshotChange: "snapshotChange", snapshotCancel: "snapshotCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab label=\"text\">\r\n <mat-expansion-panel [expanded]=\"!baseText.value\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-base-text-editor\r\n [text]=\"baseText.value\"\r\n (textChange)=\"onTextChange($event)\"\r\n />\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div>\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount\"\r\n [heights]=\"lnHeights.value\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab label=\"operations\">\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary right\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [ngClass]=\"{ selected: operation.id === resultOperationId }\"\r\n [ngClass]=\"{ edited: operation.id === editedOp?.id }\"\r\n >\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n <mat-expansion-panel [expanded]=\"editedOp\" [disabled]=\"!editedOp\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [operation]=\"editedOp\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\" *ngIf=\"busy\" />\r\n </div>\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- batch ops -->\r\n <div>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>batch operations</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <div id=\"batch-container\">\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"inputOps\"\r\n placeholder=\"operations\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy\"\r\n (click)=\"clearOperations()\"\r\n >\r\n clear\r\n </button>\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text and add results to the operations\"\r\n [disabled]=\"!inputOps.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n @if (parseError) {\r\n <span class=\"error\">{{ parseError }}</span>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>>[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>>]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong><></strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or\r\n output tags, separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character\r\n indexes (0-N) rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>,\r\n separated by space, where:\r\n </li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove\r\n feature (no value). Suffixes:\r\n <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple),\r\n <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </ul>\r\n </div>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </div>\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab label=\"image\">\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img alt=\"background\" [src]=\"imageUrl.value\" width=\"600\" />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab label=\"animation\">\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags\"\r\n [elementIds]=\"opElementIds\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- preview -->\r\n @if (snapshot) {\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n <button\r\n color=\"primary\"\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"updateViewData()\"\r\n >\r\n <mat-icon class=\"mat-primary\">refresh</mat-icon>\r\n </button>\r\n </legend>\r\n <gve-snapshot-view\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div>\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row *.right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}div#batch-container{display:grid;gap:16px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"batch-input batch-help\"}div#batch-input{grid-area:batch-input}div#batch-help{grid-area:batch-help}div#batch-help strong{color:#ff8c00}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}@media only screen and (max-width: 959px){div#batch-container{grid-template-columns:1fr;grid-template-areas:\"batch-input\" \"batch-help\"}div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "component", type: i7.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i12.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i13.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i13.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: BaseTextEditorComponent, selector: "gve-base-text-editor", inputs: ["text"], outputs: ["textChange"] }, { kind: "component", type: ChainOperationEditorComponent, selector: "gve-chain-operation-editor", inputs: ["operation", "snapshot", "hidePreview"], outputs: ["operationChange", "operationPreview", "operationCancel"] }, { kind: "component", type: ChainResultViewComponent, selector: "gve-chain-result-view", inputs: ["result"], outputs: ["stepPick"] }, { kind: "component", type: LnHeightsEditorComponent, selector: "gve-ln-heights-editor", inputs: ["lineCount", "heights"], outputs: ["heightsChange"] }, { kind: "component", type: AnimationTimelineSetComponent, selector: "gve-animation-timeline-set", inputs: ["timelines", "elementIds", "tags"], outputs: ["timelinesChange", "cancel"] }] }); }
|
|
3266
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SnapshotEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i2$2.DialogService }, { token: i4$1.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3267
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: SnapshotEditorComponent, isStandalone: true, selector: "gve-snapshot-editor", inputs: { snapshot: "snapshot", batchOps: "batchOps", noSave: "noSave", debug: "debug" }, outputs: { snapshotChange: "snapshotChange", snapshotCancel: "snapshotCancel" }, viewQueries: [{ propertyName: "snapshotView", first: true, predicate: ["snapshotView"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel [expanded]=\"!baseText.value\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-base-text-editor\r\n [text]=\"baseText.value\"\r\n (textChange)=\"onTextChange($event)\"\r\n />\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount\"\r\n [heights]=\"lnHeights.value\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary right\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [ngClass]=\"{ selected: operation.id === resultOperationId }\"\r\n [ngClass]=\"{ edited: operation.id === editedOp?.id }\"\r\n >\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n <mat-expansion-panel [expanded]=\"editedOp\" [disabled]=\"!editedOp\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [hidePreview]=\"editedOpIndex === -1\"\r\n [operation]=\"editedOp\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- batch ops -->\r\n <div>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>batch operations</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <div id=\"batch-container\">\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"inputOps\"\r\n placeholder=\"operations\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy\"\r\n (click)=\"clearOperations()\"\r\n >\r\n clear\r\n </button>\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text and add results to the operations\"\r\n [disabled]=\"!inputOps.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n @if (parseError) {\r\n <span class=\"error\">{{ parseError }}</span>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>>[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>>]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong><></strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or\r\n output tags, separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character\r\n indexes (0-N) rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>,\r\n separated by space, where:\r\n </li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove\r\n feature (no value). Suffixes:\r\n <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple),\r\n <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </ul>\r\n </div>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img alt=\"background\" [src]=\"imageUrl.value\" width=\"600\" />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags\"\r\n [elementIds]=\"opElementIds\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\" *ngIf=\"busy\" />\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div>\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row *.right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}div#batch-container{display:grid;gap:16px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"batch-input batch-help\"}div#batch-input{grid-area:batch-input}div#batch-help{grid-area:batch-help}div#batch-help strong{color:#ff8c00}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}@media only screen and (max-width: 959px){div#batch-container{grid-template-columns:1fr;grid-template-areas:\"batch-input\" \"batch-help\"}div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "component", type: i7.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i8$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i8$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i8$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i12.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i13.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i13.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i13.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "pipe", type: i2$1.FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: BaseTextEditorComponent, selector: "gve-base-text-editor", inputs: ["text"], outputs: ["textChange"] }, { kind: "component", type: ChainOperationEditorComponent, selector: "gve-chain-operation-editor", inputs: ["operation", "snapshot", "hidePreview"], outputs: ["operationChange", "operationPreview", "operationCancel"] }, { kind: "component", type: ChainResultViewComponent, selector: "gve-chain-result-view", inputs: ["result", "initialStepIndex"], outputs: ["stepPick"] }, { kind: "component", type: LnHeightsEditorComponent, selector: "gve-ln-heights-editor", inputs: ["lineCount", "heights"], outputs: ["heightsChange"] }, { kind: "component", type: AnimationTimelineSetComponent, selector: "gve-animation-timeline-set", inputs: ["timelines", "elementIds", "tags"], outputs: ["timelinesChange", "timelinesCancel"] }] }); }
|
|
2714
3268
|
}
|
|
2715
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
3269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: SnapshotEditorComponent, decorators: [{
|
|
2716
3270
|
type: Component,
|
|
2717
3271
|
args: [{ selector: 'gve-snapshot-editor', standalone: true, imports: [
|
|
2718
3272
|
CommonModule,
|
|
@@ -2736,13 +3290,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2736
3290
|
ChainResultViewComponent,
|
|
2737
3291
|
LnHeightsEditorComponent,
|
|
2738
3292
|
AnimationTimelineSetComponent,
|
|
2739
|
-
], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab label=\"text\">\r\n <mat-expansion-panel [expanded]=\"!baseText.value\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-base-text-editor\r\n [text]=\"baseText.value\"\r\n (textChange)=\"onTextChange($event)\"\r\n />\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div>\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount\"\r\n [heights]=\"lnHeights.value\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab label=\"operations\">\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary right\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [ngClass]=\"{ selected: operation.id === resultOperationId }\"\r\n [ngClass]=\"{ edited: operation.id === editedOp?.id }\"\r\n >\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n <mat-expansion-panel [expanded]=\"editedOp\" [disabled]=\"!editedOp\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [operation]=\"editedOp\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\" *ngIf=\"busy\" />\r\n </div>\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- batch ops -->\r\n <div>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>batch operations</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <div id=\"batch-container\">\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"inputOps\"\r\n placeholder=\"operations\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy\"\r\n (click)=\"clearOperations()\"\r\n >\r\n clear\r\n </button>\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text and add results to the operations\"\r\n [disabled]=\"!inputOps.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n @if (parseError) {\r\n <span class=\"error\">{{ parseError }}</span>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>>[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>>]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong><></strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or\r\n output tags, separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character\r\n indexes (0-N) rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>,\r\n separated by space, where:\r\n </li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove\r\n feature (no value). Suffixes:\r\n <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple),\r\n <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </ul>\r\n </div>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </div>\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab label=\"image\">\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img alt=\"background\" [src]=\"imageUrl.value\" width=\"600\" />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab label=\"animation\">\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags\"\r\n [elementIds]=\"opElementIds\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- preview -->\r\n @if (snapshot) {\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n <button\r\n color=\"primary\"\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"updateViewData()\"\r\n >\r\n <mat-icon class=\"mat-primary\">refresh</mat-icon>\r\n </button>\r\n </legend>\r\n <gve-snapshot-view\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div>\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row *.right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}div#batch-container{display:grid;gap:16px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"batch-input batch-help\"}div#batch-input{grid-area:batch-input}div#batch-help{grid-area:batch-help}div#batch-help strong{color:#ff8c00}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}@media only screen and (max-width: 959px){div#batch-container{grid-template-columns:1fr;grid-template-areas:\"batch-input\" \"batch-help\"}div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
|
|
2740
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i2$2.DialogService }, { type: i4$
|
|
3293
|
+
], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel [expanded]=\"!baseText.value\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-base-text-editor\r\n [text]=\"baseText.value\"\r\n (textChange)=\"onTextChange($event)\"\r\n />\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount\"\r\n [heights]=\"lnHeights.value\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary right\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [ngClass]=\"{ selected: operation.id === resultOperationId }\"\r\n [ngClass]=\"{ edited: operation.id === editedOp?.id }\"\r\n >\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n <mat-expansion-panel [expanded]=\"editedOp\" [disabled]=\"!editedOp\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [hidePreview]=\"editedOpIndex === -1\"\r\n [operation]=\"editedOp\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- batch ops -->\r\n <div>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>batch operations</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <div id=\"batch-container\">\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"inputOps\"\r\n placeholder=\"operations\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy\"\r\n (click)=\"clearOperations()\"\r\n >\r\n clear\r\n </button>\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text and add results to the operations\"\r\n [disabled]=\"!inputOps.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n @if (parseError) {\r\n <span class=\"error\">{{ parseError }}</span>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>>[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>>]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong><></strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or\r\n output tags, separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character\r\n indexes (0-N) rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>,\r\n separated by space, where:\r\n </li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove\r\n feature (no value). Suffixes:\r\n <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple),\r\n <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </ul>\r\n </div>\r\n </div>\r\n </fieldset>\r\n </mat-expansion-panel>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img alt=\"background\" [src]=\"imageUrl.value\" width=\"600\" />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags\"\r\n [elementIds]=\"opElementIds\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\" *ngIf=\"busy\" />\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div>\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row *.right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}div#batch-container{display:grid;gap:16px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"batch-input batch-help\"}div#batch-input{grid-area:batch-input}div#batch-help{grid-area:batch-help}div#batch-help strong{color:#ff8c00}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}@media only screen and (max-width: 959px){div#batch-container{grid-template-columns:1fr;grid-template-areas:\"batch-input\" \"batch-help\"}div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
|
|
3294
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i2$2.DialogService }, { type: i4$1.MatSnackBar }], propDecorators: { snapshotView: [{
|
|
3295
|
+
type: ViewChild,
|
|
3296
|
+
args: ['snapshotView', { static: false }]
|
|
3297
|
+
}], snapshot: [{
|
|
2741
3298
|
type: Input
|
|
2742
3299
|
}], batchOps: [{
|
|
2743
3300
|
type: Input
|
|
2744
3301
|
}], noSave: [{
|
|
2745
3302
|
type: Input
|
|
3303
|
+
}], debug: [{
|
|
3304
|
+
type: Input
|
|
2746
3305
|
}], snapshotChange: [{
|
|
2747
3306
|
type: Output
|
|
2748
3307
|
}], snapshotCancel: [{
|
|
@@ -2757,5 +3316,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2757
3316
|
* Generated bundle index. Do not edit.
|
|
2758
3317
|
*/
|
|
2759
3318
|
|
|
2760
|
-
export { AnimationTimelineComponent, AnimationTweenComponent,
|
|
3319
|
+
export { AnimationTimelineComponent, AnimationTweenComponent, BaseTextCharComponent, BaseTextEditorComponent, BaseTextViewComponent, ChainOperationEditorComponent, ChainResultViewComponent, FeatureEditorComponent, FeatureSetEditorComponent, FeatureSetViewComponent, GveApiService, LnHeightsEditorComponent, OperationSourceEditorComponent, SettingsService, SimpleTreeComponent, SnapshotEditorComponent, StepsMapComponent };
|
|
2761
3320
|
//# sourceMappingURL=myrmidon-gve-core.mjs.map
|