@myrmidon/gve-core 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -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/batch-operation-editor/batch-operation-editor.component.mjs +111 -0
- 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 +48 -13
- 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 +471 -159
- package/esm2022/lib/components/steps-map/steps-map.component.mjs +37 -9
- package/esm2022/lib/models.mjs +1 -1
- package/esm2022/lib/services/gve-api.service.mjs +4 -4
- package/esm2022/lib/services/settings.service.mjs +6 -5
- package/esm2022/lib/validators/svg-validators.mjs +7 -1
- package/esm2022/public-api.mjs +2 -2
- package/fesm2022/myrmidon-gve-core.mjs +1159 -487
- 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/batch-operation-editor/batch-operation-editor.component.d.ts +46 -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 +21 -4
- package/lib/components/feature-editor/feature-editor.component.d.ts +20 -6
- package/lib/components/feature-set-editor/feature-set-editor.component.d.ts +27 -4
- 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 +184 -19
- package/lib/components/steps-map/steps-map.component.d.ts +22 -3
- package/lib/models.d.ts +8 -0
- package/lib/services/gve-api.service.d.ts +33 -0
- package/lib/services/settings.service.d.ts +2 -1
- package/lib/validators/svg-validators.d.ts +6 -0
- package/package.json +10 -10
- package/public-api.d.ts +1 -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
|
@@ -1,174 +1,61 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Component, Input, Output, ViewChild, Injectable, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
2
|
+
import { EventEmitter, Component, Input, Output, ViewChild, Injectable, Optional, Inject, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { Validators, ReactiveFormsModule, FormControl, FormsModule } from '@angular/forms';
|
|
5
5
|
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$1 from '@angular/material/checkbox';
|
|
10
10
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
11
|
-
import * as i4
|
|
11
|
+
import * as i4 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';
|
|
19
19
|
import * as i8 from '@angular/material/select';
|
|
20
20
|
import { MatSelectModule } from '@angular/material/select';
|
|
21
|
-
import * as
|
|
21
|
+
import * as i14 from '@angular/material/tabs';
|
|
22
22
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
23
23
|
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
|
+
import * as i3$1 from '@angular/material/dialog';
|
|
34
|
+
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
35
|
+
import * as i1$1 from '@angular/common/http';
|
|
33
36
|
import { filter, debounceTime as debounceTime$1 } from 'rxjs/operators';
|
|
34
37
|
import * as i2$3 from '@angular/cdk/clipboard';
|
|
35
38
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
|
36
|
-
import * as
|
|
39
|
+
import * as i16 from '@cisstech/nge/monaco';
|
|
37
40
|
import { NgeMonacoModule } from '@cisstech/nge/monaco';
|
|
38
41
|
import { customAlphabet } from 'nanoid';
|
|
39
|
-
import * as
|
|
42
|
+
import * as i8$1 from '@angular/material/button-toggle';
|
|
40
43
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
41
|
-
import * as
|
|
44
|
+
import * as i13 from '@angular/material/progress-bar';
|
|
42
45
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
43
|
-
import * as
|
|
46
|
+
import * as i5$2 from '@angular/material/snack-bar';
|
|
44
47
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
45
|
-
import * as i1$1 from '@angular/common/http';
|
|
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
48
|
|
|
170
49
|
/**
|
|
50
|
+
* 🔑 `gve-animation-tween`
|
|
51
|
+
*
|
|
171
52
|
* A component to edit an animation tween.
|
|
53
|
+
* Used by the `gve-animation-timeline` component.
|
|
54
|
+
*
|
|
55
|
+
* - ▶️ `tween` (`GveAnimationTween`): the tween to edit.
|
|
56
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected by the tween.
|
|
57
|
+
* - 🔥 `tweenChange` (`GveAnimationTween`): emitted when the tween is changed.
|
|
58
|
+
* - 🔥 `tweenCancel` (`void`): emitted when the user cancels the edit.
|
|
172
59
|
*/
|
|
173
60
|
class AnimationTweenComponent {
|
|
174
61
|
/**
|
|
@@ -185,25 +72,30 @@ class AnimationTweenComponent {
|
|
|
185
72
|
this.updateForm(this._tween);
|
|
186
73
|
}
|
|
187
74
|
constructor(formBuilder) {
|
|
75
|
+
/**
|
|
76
|
+
* Emitted when the tween is changed.
|
|
77
|
+
*/
|
|
188
78
|
this.tweenChange = new EventEmitter();
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
];
|
|
79
|
+
/**
|
|
80
|
+
* Emitted when the user cancels the edit.
|
|
81
|
+
*/
|
|
82
|
+
this.tweenCancel = new EventEmitter();
|
|
83
|
+
this.types = ['to', 'from', 'fromTo', 'set'];
|
|
195
84
|
this.label = formBuilder.control('', {
|
|
196
85
|
nonNullable: true,
|
|
197
86
|
validators: [Validators.required, Validators.maxLength(100)],
|
|
198
87
|
});
|
|
199
88
|
this.note = formBuilder.control(null);
|
|
200
|
-
this.type = formBuilder.control(
|
|
89
|
+
this.type = formBuilder.control('to', { nonNullable: true });
|
|
201
90
|
this.selector = formBuilder.control('', {
|
|
202
91
|
nonNullable: true,
|
|
203
92
|
validators: [Validators.required, Validators.maxLength(200)],
|
|
204
93
|
});
|
|
205
|
-
this.vars = formBuilder.control({}, {
|
|
206
|
-
|
|
94
|
+
this.vars = formBuilder.control('{}', {
|
|
95
|
+
nonNullable: true,
|
|
96
|
+
validators: [Validators.required, this.jsonValidator],
|
|
97
|
+
});
|
|
98
|
+
this.vars2 = formBuilder.control(null, this.jsonValidator);
|
|
207
99
|
this.position = formBuilder.control(null, Validators.maxLength(100));
|
|
208
100
|
this.form = formBuilder.group({
|
|
209
101
|
label: this.label,
|
|
@@ -240,7 +132,19 @@ class AnimationTweenComponent {
|
|
|
240
132
|
this.vars2.updateValueAndValidity();
|
|
241
133
|
}
|
|
242
134
|
close() {
|
|
243
|
-
this.
|
|
135
|
+
this.tweenCancel.emit();
|
|
136
|
+
}
|
|
137
|
+
jsonValidator(control) {
|
|
138
|
+
if (!control.value) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
JSON.parse(control.value);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
return { json: true };
|
|
147
|
+
}
|
|
244
148
|
}
|
|
245
149
|
updateForm(tween) {
|
|
246
150
|
if (!tween) {
|
|
@@ -251,7 +155,7 @@ class AnimationTweenComponent {
|
|
|
251
155
|
this.note.setValue(tween.note || null);
|
|
252
156
|
this.type.setValue(tween.type);
|
|
253
157
|
this.selector.setValue(tween.selector);
|
|
254
|
-
this.vars.setValue(tween.vars || {});
|
|
158
|
+
this.vars.setValue(tween.vars || '{}');
|
|
255
159
|
this.vars2.setValue(tween.vars2 || null);
|
|
256
160
|
this.position.setValue(tween.position || null);
|
|
257
161
|
this.form.markAsPristine();
|
|
@@ -262,13 +166,14 @@ class AnimationTweenComponent {
|
|
|
262
166
|
note: this.note.value?.trim() || undefined,
|
|
263
167
|
type: this.type.value,
|
|
264
168
|
selector: this.selector.value.trim(),
|
|
265
|
-
vars:
|
|
266
|
-
vars2: this.vars2.value
|
|
267
|
-
? undefined
|
|
268
|
-
: this.vars2.value || undefined,
|
|
169
|
+
vars: this.vars.value,
|
|
170
|
+
vars2: this.vars2.value || undefined,
|
|
269
171
|
position: this.position.value?.trim() || undefined,
|
|
270
172
|
};
|
|
271
173
|
}
|
|
174
|
+
openUrl(url) {
|
|
175
|
+
window.open(url, '_blank');
|
|
176
|
+
}
|
|
272
177
|
save() {
|
|
273
178
|
if (!this.form.valid) {
|
|
274
179
|
return;
|
|
@@ -276,10 +181,10 @@ class AnimationTweenComponent {
|
|
|
276
181
|
this._tween = this.getTween();
|
|
277
182
|
this.tweenChange.emit(this._tween);
|
|
278
183
|
}
|
|
279
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
280
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
184
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTweenComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
185
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
186
|
}
|
|
282
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
187
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTweenComponent, decorators: [{
|
|
283
188
|
type: Component,
|
|
284
189
|
args: [{ selector: 'gve-animation-tween', standalone: true, imports: [
|
|
285
190
|
CommonModule,
|
|
@@ -292,19 +197,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
292
197
|
MatSelectModule,
|
|
293
198
|
MatTabsModule,
|
|
294
199
|
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"] }]
|
|
200
|
+
], 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
201
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { tween: [{
|
|
298
202
|
type: Input
|
|
299
203
|
}], elementIds: [{
|
|
300
204
|
type: Input
|
|
301
205
|
}], tweenChange: [{
|
|
302
206
|
type: Output
|
|
303
|
-
}],
|
|
207
|
+
}], tweenCancel: [{
|
|
304
208
|
type: Output
|
|
305
209
|
}] } });
|
|
306
210
|
|
|
211
|
+
/**
|
|
212
|
+
* 🔑 `gve-animation-timeline`
|
|
213
|
+
*
|
|
214
|
+
* A component to edit an animation timeline.
|
|
215
|
+
* Used by the `gve-animation-timeline-set` component.
|
|
216
|
+
*
|
|
217
|
+
* - ▶️ `timeline` (`GveAnimationTimeline`): the animation timeline to edit.
|
|
218
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected
|
|
219
|
+
* by the tween.
|
|
220
|
+
* - ▶️ `tags` (`string[]`): the tags that can be used by the timeline.
|
|
221
|
+
* - 🔥 `timelineChange` (`GveAnimationTimeline`): emitted when the timeline
|
|
222
|
+
* is changed.
|
|
223
|
+
* - 🔥 `timelineCancel` (`void`): emitted when the timeline editing is canceled.
|
|
224
|
+
*/
|
|
307
225
|
class AnimationTimelineComponent {
|
|
226
|
+
/**
|
|
227
|
+
* The animation timeline to edit.
|
|
228
|
+
*/
|
|
308
229
|
get timeline() {
|
|
309
230
|
return this._timeline;
|
|
310
231
|
}
|
|
@@ -313,15 +234,19 @@ class AnimationTimelineComponent {
|
|
|
313
234
|
this.updateForm(this._timeline);
|
|
314
235
|
}
|
|
315
236
|
constructor(formBuilder) {
|
|
237
|
+
/**
|
|
238
|
+
* The tags that can be used by the timeline.
|
|
239
|
+
*/
|
|
316
240
|
this.tags = [];
|
|
241
|
+
/**
|
|
242
|
+
* Emitted when the timeline is changed.
|
|
243
|
+
*/
|
|
317
244
|
this.timelineChange = new EventEmitter();
|
|
318
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Emitted when the timeline editing is canceled.
|
|
247
|
+
*/
|
|
248
|
+
this.timelineCancel = new EventEmitter();
|
|
319
249
|
this.editedTweenIndex = -1;
|
|
320
|
-
this.selectors = {
|
|
321
|
-
0: 'to',
|
|
322
|
-
1: 'from',
|
|
323
|
-
2: 'fromTo',
|
|
324
|
-
};
|
|
325
250
|
this.tag = formBuilder.control('', {
|
|
326
251
|
nonNullable: true,
|
|
327
252
|
validators: [Validators.required, Validators.maxLength(100)],
|
|
@@ -330,13 +255,25 @@ class AnimationTimelineComponent {
|
|
|
330
255
|
nonNullable: true,
|
|
331
256
|
validators: NgToolsValidators.strictMinLengthValidator(1),
|
|
332
257
|
});
|
|
333
|
-
this.vars = formBuilder.control(
|
|
258
|
+
this.vars = formBuilder.control(null, this.jsonValidator);
|
|
334
259
|
this.form = formBuilder.group({
|
|
335
260
|
tag: this.tag,
|
|
336
261
|
tweens: this.tweens,
|
|
337
262
|
vars: this.vars,
|
|
338
263
|
});
|
|
339
264
|
}
|
|
265
|
+
jsonValidator(control) {
|
|
266
|
+
if (!control.value) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
JSON.parse(control.value);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
return { json: true };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
340
277
|
updateForm(timeline) {
|
|
341
278
|
if (!timeline) {
|
|
342
279
|
this.form.reset();
|
|
@@ -344,14 +281,14 @@ class AnimationTimelineComponent {
|
|
|
344
281
|
}
|
|
345
282
|
this.tag.setValue(timeline.tag);
|
|
346
283
|
this.tweens.setValue(timeline.tweens);
|
|
347
|
-
this.vars.setValue(timeline.vars ||
|
|
284
|
+
this.vars.setValue(timeline.vars || null);
|
|
348
285
|
this.form.markAsPristine();
|
|
349
286
|
}
|
|
350
287
|
addTween() {
|
|
351
288
|
this.editedTweenIndex = -1;
|
|
352
289
|
this.editedTween = {
|
|
353
290
|
label: 'tween #' + (this.tweens.value.length + 1),
|
|
354
|
-
type:
|
|
291
|
+
type: 'to',
|
|
355
292
|
selector: '',
|
|
356
293
|
};
|
|
357
294
|
}
|
|
@@ -407,11 +344,11 @@ class AnimationTimelineComponent {
|
|
|
407
344
|
return {
|
|
408
345
|
tag: this.tag.value || '',
|
|
409
346
|
tweens: this.tweens.value,
|
|
410
|
-
vars:
|
|
347
|
+
vars: this.vars.value || undefined,
|
|
411
348
|
};
|
|
412
349
|
}
|
|
413
350
|
close() {
|
|
414
|
-
this.
|
|
351
|
+
this.timelineCancel.emit();
|
|
415
352
|
}
|
|
416
353
|
save() {
|
|
417
354
|
if (this.form.invalid) {
|
|
@@ -421,10 +358,10 @@ class AnimationTimelineComponent {
|
|
|
421
358
|
this.timelineChange.emit(this._timeline);
|
|
422
359
|
this.form.markAsPristine();
|
|
423
360
|
}
|
|
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"] }] }); }
|
|
361
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
362
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.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
363
|
}
|
|
427
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
|
|
428
365
|
type: Component,
|
|
429
366
|
args: [{ selector: 'gve-animation-timeline', standalone: true, imports: [
|
|
430
367
|
CommonModule,
|
|
@@ -440,7 +377,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
440
377
|
MatTooltipModule,
|
|
441
378
|
NgToolsModule,
|
|
442
379
|
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
|
|
380
|
+
], 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
381
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timeline: [{
|
|
445
382
|
type: Input
|
|
446
383
|
}], elementIds: [{
|
|
@@ -449,20 +386,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
449
386
|
type: Input
|
|
450
387
|
}], timelineChange: [{
|
|
451
388
|
type: Output
|
|
452
|
-
}],
|
|
389
|
+
}], timelineCancel: [{
|
|
453
390
|
type: Output
|
|
454
391
|
}] } });
|
|
455
392
|
|
|
456
393
|
/**
|
|
394
|
+
* 🔑 `gve-base-text-char`
|
|
395
|
+
*
|
|
457
396
|
* A component that displays a single character from a base text.
|
|
397
|
+
* Used by `gve-base-text-view`.
|
|
458
398
|
*/
|
|
459
399
|
class BaseTextCharComponent {
|
|
460
400
|
constructor() {
|
|
461
401
|
this.defaultColor = '#D8D8D8';
|
|
462
402
|
this.defaultBorderColor = '#D8D8D8';
|
|
463
403
|
this.defaultEmSize = 1.5;
|
|
404
|
+
/**
|
|
405
|
+
* Emitted when the character is clicked.
|
|
406
|
+
*/
|
|
464
407
|
this.charPick = new EventEmitter();
|
|
465
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* The character to display.
|
|
411
|
+
*/
|
|
466
412
|
get char() {
|
|
467
413
|
return this._char;
|
|
468
414
|
}
|
|
@@ -472,10 +418,10 @@ class BaseTextCharComponent {
|
|
|
472
418
|
onCharClick(event) {
|
|
473
419
|
this.charPick.emit({ char: this._char, event });
|
|
474
420
|
}
|
|
475
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
476
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
421
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextCharComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
422
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
423
|
}
|
|
478
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
424
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextCharComponent, decorators: [{
|
|
479
425
|
type: Component,
|
|
480
426
|
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
427
|
}], propDecorators: { char: [{
|
|
@@ -485,18 +431,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
485
431
|
}] } });
|
|
486
432
|
|
|
487
433
|
/**
|
|
434
|
+
* 🔑 `gve-base-text-view`
|
|
435
|
+
*
|
|
488
436
|
* A component to display a selectable base text.
|
|
437
|
+
* Used by the chain result view component and the base text editor component.
|
|
438
|
+
*
|
|
439
|
+
* - ▶️ `defaultColor` (`string`): the default color for the text.
|
|
440
|
+
* - ▶️ `defaultBorderColor` (`string`): the default border color for the text.
|
|
441
|
+
* - ▶️ `selectionColor` (`string`): the color for the selected text.
|
|
442
|
+
* - ▶️ `hasLineNumber` (`boolean`): true if line numbers should be displayed next to each line.
|
|
443
|
+
* - ▶️ `text` (`string` | `CharNode[]`): the text to display.
|
|
444
|
+
* - 🔥 `charPick` (`BaseTextCharEvent`): emitted when a character is picked.
|
|
445
|
+
* - 🔥 `rangePick` (`VarBaseTextRange`): emitted when a range is picked.
|
|
489
446
|
*/
|
|
490
447
|
class BaseTextViewComponent {
|
|
491
448
|
constructor() {
|
|
449
|
+
/**
|
|
450
|
+
* The default color for the text.
|
|
451
|
+
*/
|
|
492
452
|
this.defaultColor = '#DBDBDB';
|
|
453
|
+
/**
|
|
454
|
+
* The default border color for the text.
|
|
455
|
+
*/
|
|
493
456
|
this.defaultBorderColor = '#DBDBDB';
|
|
457
|
+
/**
|
|
458
|
+
* The color for the selected text.
|
|
459
|
+
*/
|
|
494
460
|
this.selectionColor = '#3E92CC';
|
|
461
|
+
/**
|
|
462
|
+
* True if line numbers should be displayed next to each line.
|
|
463
|
+
*/
|
|
495
464
|
this.hasLineNumber = false;
|
|
465
|
+
/**
|
|
466
|
+
* Emitted when a character is picked.
|
|
467
|
+
*/
|
|
496
468
|
this.charPick = new EventEmitter();
|
|
469
|
+
/**
|
|
470
|
+
* Emitted when a range is picked. This is preceded by a character pick event.
|
|
471
|
+
* The range is defined by the starting character and the number of characters.
|
|
472
|
+
* The range is inclusive.
|
|
473
|
+
*/
|
|
497
474
|
this.rangePick = new EventEmitter();
|
|
498
475
|
this.lines = [];
|
|
499
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* The text to display.
|
|
479
|
+
*/
|
|
500
480
|
get text() {
|
|
501
481
|
return this._text;
|
|
502
482
|
}
|
|
@@ -582,12 +562,12 @@ class BaseTextViewComponent {
|
|
|
582
562
|
}
|
|
583
563
|
this._lastSelectedChar = event.char;
|
|
584
564
|
}
|
|
585
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
586
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
565
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
566
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
567
|
}
|
|
588
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextViewComponent, decorators: [{
|
|
589
569
|
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
|
|
570
|
+
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
571
|
}], propDecorators: { defaultColor: [{
|
|
592
572
|
type: Input
|
|
593
573
|
}], defaultBorderColor: [{
|
|
@@ -605,12 +585,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
605
585
|
}] } });
|
|
606
586
|
|
|
607
587
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
588
|
+
* 🔑 `gve-feature-editor`
|
|
589
|
+
*
|
|
590
|
+
* Component for editing a single feature, whose model is a name=value
|
|
591
|
+
* pair, plus a set policy value which defines the desired behavior
|
|
592
|
+
* when adding that feature to a set.
|
|
593
|
+
* Used by `gve-feature-set-editor`.
|
|
594
|
+
*
|
|
595
|
+
* - ▶️ `feature` (`Feature`): the feature to edit.
|
|
596
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
597
|
+
* in the _name_ selection. This is used when you have a closed list of
|
|
598
|
+
* features. Each item in the list is an object with _id_ and _label_
|
|
599
|
+
* properties.
|
|
600
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When
|
|
601
|
+
* specified and the user selects a feature name present in the map keys,
|
|
602
|
+
* the corresponding values will be used to populate the value selection.
|
|
603
|
+
* - 🔥 `featureChange` (`Feature`): emitted when feature has changed.
|
|
604
|
+
* - 🔥 `featureCancel`: emitted when the user cancels the feature editing.
|
|
614
605
|
*/
|
|
615
606
|
class FeatureEditorComponent {
|
|
616
607
|
/**
|
|
@@ -683,8 +674,10 @@ class FeatureEditorComponent {
|
|
|
683
674
|
this._sub = this.name.valueChanges
|
|
684
675
|
.pipe(debounceTime(300), distinctUntilChanged())
|
|
685
676
|
.subscribe((name) => {
|
|
686
|
-
this.
|
|
687
|
-
|
|
677
|
+
if (!this._frozen && this.featNames?.length) {
|
|
678
|
+
this.value.reset();
|
|
679
|
+
this.nameIds = this.getLabeledIdsFor(name);
|
|
680
|
+
}
|
|
688
681
|
});
|
|
689
682
|
}
|
|
690
683
|
ngOnDestroy() {
|
|
@@ -701,12 +694,14 @@ class FeatureEditorComponent {
|
|
|
701
694
|
this.form.reset();
|
|
702
695
|
}
|
|
703
696
|
else {
|
|
697
|
+
this._frozen = true;
|
|
704
698
|
this.name.setValue(feature.name);
|
|
705
699
|
this.value.setValue(feature.value);
|
|
706
700
|
this.setPolicy.setValue(feature.setPolicy || FeatureSetPolicy.multiple);
|
|
707
701
|
this.isNegated.setValue(feature.isNegated || false);
|
|
708
702
|
this.isGlobal.setValue(feature.isGlobal || false);
|
|
709
703
|
this.isShortLived.setValue(feature.isShortLived || false);
|
|
704
|
+
this._frozen = false;
|
|
710
705
|
}
|
|
711
706
|
}
|
|
712
707
|
cancel() {
|
|
@@ -732,10 +727,10 @@ class FeatureEditorComponent {
|
|
|
732
727
|
};
|
|
733
728
|
this.featureChange.emit(this.feature);
|
|
734
729
|
}
|
|
735
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
736
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
730
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
731
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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$1.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
732
|
}
|
|
738
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureEditorComponent, decorators: [{
|
|
739
734
|
type: Component,
|
|
740
735
|
args: [{ selector: 'gve-feature-editor', standalone: true, imports: [
|
|
741
736
|
CommonModule,
|
|
@@ -766,7 +761,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
766
761
|
}] } });
|
|
767
762
|
|
|
768
763
|
/**
|
|
769
|
-
*
|
|
764
|
+
* 🔑 `gve-feature-set-editor`
|
|
765
|
+
*
|
|
766
|
+
* Component for editing a set of features. It shows a list of features,
|
|
767
|
+
* allowing to edit each of them or adding new ones. Optionally you can
|
|
768
|
+
* specify a list of feature names and values to use in the selection.
|
|
769
|
+
* Used by base text editor and chain operation editor components.
|
|
770
|
+
*
|
|
771
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
772
|
+
* in the _name_ selection. This is used when you have a closed list of features.
|
|
773
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When specified
|
|
774
|
+
* and the user selects a feature name present in the map keys, the corresponding
|
|
775
|
+
* values will be used to populate the value selection.
|
|
776
|
+
* - ▶️ `filterThreshold` (`number`): the threshold at which the features filter
|
|
777
|
+
* should become visible. If set to 0, the filter is always visible; if set to -1,
|
|
778
|
+
* it is always invisible; otherwise, it gets visible when the number of features
|
|
779
|
+
* is greater than the threshold. Default is 5.
|
|
780
|
+
* - ▶️ `features` (`Feature[]`): the features to edit.
|
|
781
|
+
* - ▶️ `isVar`: true if the feature is a variant operation feature, which
|
|
782
|
+
* has additional properties like negation, global, and short-lived.
|
|
783
|
+
* - 🔥 `featuresChange` (`Feature[]`): emitted when features have changed.
|
|
770
784
|
*/
|
|
771
785
|
class FeatureSetEditorComponent {
|
|
772
786
|
/**
|
|
@@ -783,14 +797,18 @@ class FeatureSetEditorComponent {
|
|
|
783
797
|
this.applyFeatureFilter(this.filter.value);
|
|
784
798
|
}
|
|
785
799
|
constructor(formBuilder) {
|
|
800
|
+
this.POLICIES = ['M', 'S', 'SF'];
|
|
786
801
|
/**
|
|
787
802
|
* True if the features are variable features.
|
|
788
803
|
*/
|
|
789
804
|
this.isVar = false;
|
|
790
805
|
/**
|
|
791
|
-
*
|
|
806
|
+
* The threshold at which the features filter should become visible.
|
|
807
|
+
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
808
|
+
* invisible; otherwise, it gets visible when the number of features
|
|
809
|
+
* is greater than the threshold. Default is 5.
|
|
792
810
|
*/
|
|
793
|
-
this.
|
|
811
|
+
this.filterThreshold = 5;
|
|
794
812
|
/**
|
|
795
813
|
* Emitted when the features change.
|
|
796
814
|
*/
|
|
@@ -862,10 +880,10 @@ class FeatureSetEditorComponent {
|
|
|
862
880
|
this.editedFeature = undefined;
|
|
863
881
|
this._editedFeatureIndex = -1;
|
|
864
882
|
}
|
|
865
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
866
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
883
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureSetEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
884
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.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
885
|
}
|
|
868
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
886
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureSetEditorComponent, decorators: [{
|
|
869
887
|
type: Component,
|
|
870
888
|
args: [{ selector: 'gve-feature-set-editor', standalone: true, imports: [
|
|
871
889
|
CommonModule,
|
|
@@ -879,14 +897,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
879
897
|
MatTooltipModule,
|
|
880
898
|
NgToolsModule,
|
|
881
899
|
FeatureEditorComponent,
|
|
882
|
-
], template: "<div>\r\n <div class=\"form-row\">\r\n @if (
|
|
900
|
+
], 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
901
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { isVar: [{
|
|
884
902
|
type: Input
|
|
885
903
|
}], featNames: [{
|
|
886
904
|
type: Input
|
|
887
905
|
}], featValues: [{
|
|
888
906
|
type: Input
|
|
889
|
-
}],
|
|
907
|
+
}], filterThreshold: [{
|
|
890
908
|
type: Input
|
|
891
909
|
}], features: [{
|
|
892
910
|
type: Input
|
|
@@ -895,9 +913,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
895
913
|
}] } });
|
|
896
914
|
|
|
897
915
|
/**
|
|
916
|
+
* 🔑 `gve-base-text-editor`
|
|
917
|
+
*
|
|
898
918
|
* Editor for snapshot's base text. This can receive a string or an array
|
|
899
919
|
* of `CharNode` objects, and lets the user edit the text, character by
|
|
900
920
|
* character, optionally also setting its features.
|
|
921
|
+
*
|
|
922
|
+
* Used by the `gve-snapshot-editor` component.
|
|
901
923
|
*/
|
|
902
924
|
class BaseTextEditorComponent {
|
|
903
925
|
/**
|
|
@@ -925,7 +947,7 @@ class BaseTextEditorComponent {
|
|
|
925
947
|
this._dialogService = _dialogService;
|
|
926
948
|
this._text = [];
|
|
927
949
|
/**
|
|
928
|
-
*
|
|
950
|
+
* Emitted for the edited text as an array of `CharNode`'s whenever it changes.
|
|
929
951
|
*/
|
|
930
952
|
this.textChange = new EventEmitter();
|
|
931
953
|
this.userText = formBuilder.control('', {
|
|
@@ -964,10 +986,10 @@ class BaseTextEditorComponent {
|
|
|
964
986
|
}
|
|
965
987
|
});
|
|
966
988
|
}
|
|
967
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
968
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
989
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
990
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
991
|
}
|
|
970
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
992
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BaseTextEditorComponent, decorators: [{
|
|
971
993
|
type: Component,
|
|
972
994
|
args: [{ selector: 'gve-base-text-editor', standalone: true, imports: [
|
|
973
995
|
CommonModule,
|
|
@@ -979,14 +1001,190 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
979
1001
|
MatTooltipModule,
|
|
980
1002
|
BaseTextViewComponent,
|
|
981
1003
|
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
|
|
1004
|
+
], 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
1005
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.DialogService }], propDecorators: { text: [{
|
|
984
1006
|
type: Input
|
|
985
1007
|
}], textChange: [{
|
|
986
1008
|
type: Output
|
|
987
1009
|
}] } });
|
|
988
1010
|
|
|
1011
|
+
/**
|
|
1012
|
+
* Service to interact with the GVE API.
|
|
1013
|
+
*/
|
|
1014
|
+
class GveApiService {
|
|
1015
|
+
constructor(_http, _error, _env) {
|
|
1016
|
+
this._http = _http;
|
|
1017
|
+
this._error = _error;
|
|
1018
|
+
this._env = _env;
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Parse the specified text into operations. Each operation is a line
|
|
1022
|
+
* of the text.
|
|
1023
|
+
* @param text The text to parse.
|
|
1024
|
+
* @returns The operations.
|
|
1025
|
+
*/
|
|
1026
|
+
parseOperations(text) {
|
|
1027
|
+
return this._http
|
|
1028
|
+
.post(`${this._env.get('apiUrl')}text/operations/parse`, {
|
|
1029
|
+
text,
|
|
1030
|
+
})
|
|
1031
|
+
.pipe(catchError(this._error.handleError));
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Run operations on the specified text.
|
|
1035
|
+
*
|
|
1036
|
+
* @param text The base text.
|
|
1037
|
+
* @param operations The operations to run.
|
|
1038
|
+
* @returns Result wrapper.
|
|
1039
|
+
*/
|
|
1040
|
+
runOperations(text, operations) {
|
|
1041
|
+
return this._http
|
|
1042
|
+
.post(`${this._env.get('apiUrl')}text/operations/run`, {
|
|
1043
|
+
text,
|
|
1044
|
+
operations,
|
|
1045
|
+
})
|
|
1046
|
+
.pipe(catchError(this._error.handleError));
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Get the input and output tags for the given operations.
|
|
1050
|
+
*
|
|
1051
|
+
* @param operations The operations.
|
|
1052
|
+
* @returns Result wrapper.
|
|
1053
|
+
*/
|
|
1054
|
+
getTags(operations) {
|
|
1055
|
+
return this._http
|
|
1056
|
+
.post(`${this._env.get('apiUrl')}text/operations/tags`, {
|
|
1057
|
+
operations,
|
|
1058
|
+
})
|
|
1059
|
+
.pipe(catchError(this._error.handleError));
|
|
1060
|
+
}
|
|
1061
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: GveApiService, deps: [{ token: i1$1.HttpClient }, { token: i2$1.ErrorService }, { token: i2$1.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1062
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
|
|
1063
|
+
}
|
|
1064
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: GveApiService, decorators: [{
|
|
1065
|
+
type: Injectable,
|
|
1066
|
+
args: [{
|
|
1067
|
+
providedIn: 'root',
|
|
1068
|
+
}]
|
|
1069
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2$1.ErrorService }, { type: i2$1.EnvService }] });
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* 🔑 `gve-batch-operation-editor`
|
|
1073
|
+
*
|
|
1074
|
+
* A component to edit a batch of operations. This component can be used both in a dialog
|
|
1075
|
+
* and as a standalone component.
|
|
1076
|
+
*
|
|
1077
|
+
* - ▶️ `preset` (`string`): the optional preset text to parse.
|
|
1078
|
+
* - 🔥 `operationsChange` (`CharChainOperation[]`): event emitted when operations change.
|
|
1079
|
+
*/
|
|
1080
|
+
class BatchOperationEditorComponent {
|
|
1081
|
+
/**
|
|
1082
|
+
* The preset text to parse if any. Usually you start with a blank
|
|
1083
|
+
* text, but sometimes you might want to pre-set it.
|
|
1084
|
+
*/
|
|
1085
|
+
get preset() {
|
|
1086
|
+
return this._preset;
|
|
1087
|
+
}
|
|
1088
|
+
set preset(value) {
|
|
1089
|
+
if (this._preset === value) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
this._preset = value || undefined;
|
|
1093
|
+
this.parseOperations();
|
|
1094
|
+
}
|
|
1095
|
+
constructor(formBuilder, _api,
|
|
1096
|
+
// this component can be used as a dialog
|
|
1097
|
+
dialogRef, data) {
|
|
1098
|
+
this._api = _api;
|
|
1099
|
+
this.dialogRef = dialogRef;
|
|
1100
|
+
this.data = data;
|
|
1101
|
+
/**
|
|
1102
|
+
* Emitted when operations change.
|
|
1103
|
+
*/
|
|
1104
|
+
this.operationsChange = new EventEmitter();
|
|
1105
|
+
this.text = formBuilder.control(null, [
|
|
1106
|
+
Validators.required,
|
|
1107
|
+
Validators.maxLength(2000),
|
|
1108
|
+
]);
|
|
1109
|
+
this.form = formBuilder.group({
|
|
1110
|
+
inputOps: this.text,
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
ngOnInit() {
|
|
1114
|
+
if (this.data?.payload?.preset) {
|
|
1115
|
+
this.preset = this.data.payload.preset;
|
|
1116
|
+
this.parseOperations();
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
parseOperations() {
|
|
1120
|
+
if (this.busy || !this.text.value) {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
this.busy = true;
|
|
1124
|
+
this.parseError = undefined;
|
|
1125
|
+
this._api.parseOperations(this.text.value).subscribe({
|
|
1126
|
+
next: (wrapper) => {
|
|
1127
|
+
this.operationsChange.emit(wrapper.result);
|
|
1128
|
+
if (!this.preset) {
|
|
1129
|
+
this.dialogRef?.close(wrapper.result);
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
error: (error) => {
|
|
1133
|
+
this.parseError = error.message;
|
|
1134
|
+
},
|
|
1135
|
+
complete: () => {
|
|
1136
|
+
this.busy = false;
|
|
1137
|
+
},
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
close() {
|
|
1141
|
+
this.dialogRef?.close();
|
|
1142
|
+
}
|
|
1143
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BatchOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$1.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1144
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: BatchOperationEditorComponent, isStandalone: true, selector: "gve-batch-operation-editor", inputs: { preset: "preset" }, outputs: { operationsChange: "operationsChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Add Operations</h2>\r\n </div>\r\n }\r\n <fieldset>\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]=\"text\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n @if (parseError) {\r\n <div class=\"error\">{{ parseError }}</div>\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 output tags,\r\n separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character indexes (0-N)\r\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>, separated by\r\n space, where:\r\n </li>\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 feature (no\r\n value). Suffixes: <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple), <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 </li>\r\n </ul>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text into operations\"\r\n [disabled]=\"!text.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n </div>\r\n </fieldset>\r\n</div>\r\n", styles: [".full-width{width:100%}.error{color:red}.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}div#heading{margin:8px;text-align:center}div#batch-help strong{color:#ff8c00}\n"], dependencies: [{ 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: "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: 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"] }] }); }
|
|
1145
|
+
}
|
|
1146
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: BatchOperationEditorComponent, decorators: [{
|
|
1147
|
+
type: Component,
|
|
1148
|
+
args: [{ selector: 'gve-batch-operation-editor', standalone: true, imports: [
|
|
1149
|
+
ReactiveFormsModule,
|
|
1150
|
+
MatButtonModule,
|
|
1151
|
+
MatFormFieldModule,
|
|
1152
|
+
MatIconModule,
|
|
1153
|
+
MatInputModule,
|
|
1154
|
+
], template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Add Operations</h2>\r\n </div>\r\n }\r\n <fieldset>\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]=\"text\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n @if (parseError) {\r\n <div class=\"error\">{{ parseError }}</div>\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 output tags,\r\n separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>@</code> to AT to use character indexes (0-N)\r\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>, separated by\r\n space, where:\r\n </li>\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 feature (no\r\n value). Suffixes: <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple), <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 </li>\r\n </ul>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text into operations\"\r\n [disabled]=\"!text.value || busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n batch add\r\n </button>\r\n </div>\r\n </fieldset>\r\n</div>\r\n", styles: [".full-width{width:100%}.error{color:red}.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}div#heading{margin:8px;text-align:center}div#batch-help strong{color:#ff8c00}\n"] }]
|
|
1155
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$1.MatDialogRef, decorators: [{
|
|
1156
|
+
type: Optional
|
|
1157
|
+
}] }, { type: undefined, decorators: [{
|
|
1158
|
+
type: Optional
|
|
1159
|
+
}, {
|
|
1160
|
+
type: Inject,
|
|
1161
|
+
args: [MAT_DIALOG_DATA]
|
|
1162
|
+
}] }], propDecorators: { preset: [{
|
|
1163
|
+
type: Input
|
|
1164
|
+
}], operationsChange: [{
|
|
1165
|
+
type: Output
|
|
1166
|
+
}] } });
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* 🔑 `gve-operation-source-editor`
|
|
1170
|
+
*
|
|
1171
|
+
* A component to edit an operation source.
|
|
1172
|
+
* Used by chain operation editor component.
|
|
1173
|
+
*
|
|
1174
|
+
* - ▶️ `source` (`OperationSource`): the source to edit.
|
|
1175
|
+
* - ▶️ `ids` (`LabeledId[]`): the list of source IDs, when using a closed
|
|
1176
|
+
* list for them.
|
|
1177
|
+
* - ▶️ `types` (`LabeledId[]`): the list of source types, when using a closed
|
|
1178
|
+
* list for them.
|
|
1179
|
+
* - 🔥 `sourceChange` (`EventEmitter<OperationSource>`): the event emitted
|
|
1180
|
+
* when the source changes.
|
|
1181
|
+
* - 🔥 `sourceCancel` (`EventEmitter<void>`): the event emitted when the
|
|
1182
|
+
* edit is canceled.
|
|
1183
|
+
*/
|
|
989
1184
|
class OperationSourceEditorComponent {
|
|
1185
|
+
/**
|
|
1186
|
+
* The source to edit.
|
|
1187
|
+
*/
|
|
990
1188
|
get source() {
|
|
991
1189
|
return this._source;
|
|
992
1190
|
}
|
|
@@ -998,7 +1196,13 @@ class OperationSourceEditorComponent {
|
|
|
998
1196
|
this.updateForm(this._source);
|
|
999
1197
|
}
|
|
1000
1198
|
constructor(formBuilder) {
|
|
1199
|
+
/**
|
|
1200
|
+
* The event emitted when the source changes.
|
|
1201
|
+
*/
|
|
1001
1202
|
this.sourceChange = new EventEmitter();
|
|
1203
|
+
/**
|
|
1204
|
+
* The event emitted when the edit is canceled.
|
|
1205
|
+
*/
|
|
1002
1206
|
this.sourceCancel = new EventEmitter();
|
|
1003
1207
|
this.id = new FormControl('', {
|
|
1004
1208
|
validators: [Validators.required, Validators.maxLength(50)],
|
|
@@ -1047,10 +1251,10 @@ class OperationSourceEditorComponent {
|
|
|
1047
1251
|
};
|
|
1048
1252
|
this.sourceChange.emit(this._source);
|
|
1049
1253
|
}
|
|
1050
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1051
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1254
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: OperationSourceEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1255
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
1256
|
}
|
|
1053
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1257
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: OperationSourceEditorComponent, decorators: [{
|
|
1054
1258
|
type: Component,
|
|
1055
1259
|
args: [{ selector: 'gve-operation-source-editor', standalone: true, imports: [
|
|
1056
1260
|
CommonModule,
|
|
@@ -1074,7 +1278,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1074
1278
|
type: Output
|
|
1075
1279
|
}] } });
|
|
1076
1280
|
|
|
1281
|
+
/**
|
|
1282
|
+
* Validators for SVG.
|
|
1283
|
+
*/
|
|
1077
1284
|
class SvgValidators {
|
|
1285
|
+
/**
|
|
1286
|
+
* Validator for a valid SVG `g` root element in GVE.
|
|
1287
|
+
*/
|
|
1078
1288
|
static rootGValidator() {
|
|
1079
1289
|
return (control) => {
|
|
1080
1290
|
if (!control.value) {
|
|
@@ -1102,11 +1312,27 @@ class SvgValidators {
|
|
|
1102
1312
|
}
|
|
1103
1313
|
}
|
|
1104
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* 🔑 `gve-simple-tree`
|
|
1317
|
+
*
|
|
1318
|
+
* A simple tree component.
|
|
1319
|
+
*
|
|
1320
|
+
* - ▶️ `node` (`TreeNode<any>`): the node to display.
|
|
1321
|
+
* - ▶️ `selectedNode` (`TreeNode<any> | null`): the selected node.
|
|
1322
|
+
* - 🔥 `selectedNodeChange` (`EventEmitter<TreeNode<any> | null>`): the event
|
|
1323
|
+
* emitted when the selected node changes.
|
|
1324
|
+
*/
|
|
1105
1325
|
class SimpleTreeComponent {
|
|
1106
1326
|
constructor() {
|
|
1327
|
+
/**
|
|
1328
|
+
* The event emitted when the selected node changes.
|
|
1329
|
+
*/
|
|
1107
1330
|
this.selectedNodeChange = new EventEmitter();
|
|
1108
1331
|
this.level = 0;
|
|
1109
1332
|
}
|
|
1333
|
+
/**
|
|
1334
|
+
* The node to display.
|
|
1335
|
+
*/
|
|
1110
1336
|
get node() {
|
|
1111
1337
|
return this._node;
|
|
1112
1338
|
}
|
|
@@ -1135,10 +1361,10 @@ class SimpleTreeComponent {
|
|
|
1135
1361
|
this.selectedNode = node;
|
|
1136
1362
|
this.selectedNodeChange.emit(node);
|
|
1137
1363
|
}
|
|
1138
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1139
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
1364
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SimpleTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1365
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
1366
|
}
|
|
1141
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SimpleTreeComponent, decorators: [{
|
|
1142
1368
|
type: Component,
|
|
1143
1369
|
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
1370
|
}], propDecorators: { node: [{
|
|
@@ -1150,7 +1376,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1150
1376
|
}] } });
|
|
1151
1377
|
|
|
1152
1378
|
/**
|
|
1153
|
-
* Service to store and retrieve dynamic settings.
|
|
1379
|
+
* Service to store and retrieve dynamic settings. On demand, settings can
|
|
1380
|
+
* be persisted in the local storage.
|
|
1154
1381
|
*/
|
|
1155
1382
|
class SettingsService {
|
|
1156
1383
|
constructor() {
|
|
@@ -1223,10 +1450,10 @@ class SettingsService {
|
|
|
1223
1450
|
this._cache.delete(key);
|
|
1224
1451
|
localStorage.removeItem(key);
|
|
1225
1452
|
}
|
|
1226
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1227
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.
|
|
1453
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1454
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
|
|
1228
1455
|
}
|
|
1229
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1456
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SettingsService, decorators: [{
|
|
1230
1457
|
type: Injectable,
|
|
1231
1458
|
args: [{
|
|
1232
1459
|
providedIn: 'root',
|
|
@@ -1234,7 +1461,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1234
1461
|
}] });
|
|
1235
1462
|
|
|
1236
1463
|
/**
|
|
1464
|
+
* 🔑 `gve-chain-operation-editor`
|
|
1465
|
+
*
|
|
1237
1466
|
* A component for editing a variant generation operation.
|
|
1467
|
+
* Used by the `gve-snapshot-editor` component.
|
|
1468
|
+
* - ▶️ `operation` (`CharChainOperation`): the operation to edit.
|
|
1469
|
+
* - ▶️ `snapshot` (`SnapshotBase`): the snapshot the operation refers to.
|
|
1470
|
+
* - ▶️ `hidePreview` (`boolean`): whether to hide the preview request button.
|
|
1471
|
+
* - 🔥 `operationChange` (`CharChainOperation`): emitted when the operation
|
|
1472
|
+
* is changed.
|
|
1473
|
+
* - 🔥 `operationPreview` (`CharChainOperation`): emitted when the operation
|
|
1474
|
+
* preview is requested.
|
|
1475
|
+
* - 🔥 `operationCancel` (`void`): emitted when operation editing is cancelled.
|
|
1476
|
+
*
|
|
1477
|
+
* The editor is composed of many sections:
|
|
1478
|
+
* - **general**:
|
|
1479
|
+
* - ID: each operation has an ID, automatically assigned. This is not used in
|
|
1480
|
+
* the context of the operation or of its transformations, but is provided as a
|
|
1481
|
+
* reference.
|
|
1482
|
+
* - at and run: coordinates for the selection of the base text targeted by
|
|
1483
|
+
* this operation. Check `idx` when `at` is index-based rather than ID-based.
|
|
1484
|
+
* - to and to-run: a second coordinate, used by some operations.
|
|
1485
|
+
* - value: the text value argument, for those operations requiring it.
|
|
1486
|
+
* - rank and group ID.
|
|
1487
|
+
* - input and output tags.
|
|
1488
|
+
* - features. These are metadata attached to this operation, and can be any
|
|
1489
|
+
* type of name=value pairs.
|
|
1490
|
+
* - **sources**: sources are a special type of metadata used when your text
|
|
1491
|
+
* versions come from more than a single source. In this case, you can specify
|
|
1492
|
+
* the source(s) for each operation, just like in a traditional apparatus you
|
|
1493
|
+
* specify witnesses for each variant. Each source has an ID, a type, a
|
|
1494
|
+
* probability rank (0=not specified, else 1-N), and an optional free text note.
|
|
1495
|
+
* - **diplomatic**: SVG `g` code. The diplomatic section is the approximate
|
|
1496
|
+
* diplomatic representation of the layout of the annotated text. It essentially
|
|
1497
|
+
* consists in a freely editable SVG code, always grouped into a single root `g`
|
|
1498
|
+
* element, encoding all the graphical traits representing this operation.
|
|
1499
|
+
* The visual part of each operation can be placed everywhere on the snapshot,
|
|
1500
|
+
* as specified by its coordinates.
|
|
1501
|
+
* - **elements**: features of any of the SVG code elements having an ID.
|
|
1502
|
+
* The elements section lists all the SVG elements having an `id` attribute in
|
|
1503
|
+
* the SVG code of the diplomatic section. The ID allows you to attach
|
|
1504
|
+
* any number of features (metadata modeled as name=value pairs) to each of
|
|
1505
|
+
* these elements. Among these features, you may use `style` or `class` for
|
|
1506
|
+
* renditional purposes, and any other name for your own metadata. Note that
|
|
1507
|
+
* element IDs will be required also when dealing with animations. So, ensure
|
|
1508
|
+
* to assign an ID to each element you want to animate.
|
|
1509
|
+
* - **dp feats**: diplomatic features: these are features (metadata modeled
|
|
1510
|
+
* as name=value pairs) for the diplomatic representation of this operation
|
|
1511
|
+
* as a whole.
|
|
1238
1512
|
*/
|
|
1239
1513
|
class ChainOperationEditorComponent {
|
|
1240
1514
|
/**
|
|
@@ -1267,23 +1541,24 @@ class ChainOperationEditorComponent {
|
|
|
1267
1541
|
this._editorModel?.setValue(this.svg.value || '');
|
|
1268
1542
|
}
|
|
1269
1543
|
}
|
|
1270
|
-
constructor(formBuilder, _clipboard, _settings) {
|
|
1544
|
+
constructor(formBuilder, _clipboard, _settings, _dialogService) {
|
|
1271
1545
|
this._clipboard = _clipboard;
|
|
1272
1546
|
this._settings = _settings;
|
|
1547
|
+
this._dialogService = _dialogService;
|
|
1273
1548
|
// monaco
|
|
1274
1549
|
this._disposables = [];
|
|
1275
1550
|
this._nanoid = customAlphabet('1234567890abcdef', 10);
|
|
1276
1551
|
this._editedSourceIndex = -1;
|
|
1277
1552
|
/**
|
|
1278
|
-
*
|
|
1553
|
+
* Emitted when the operation is changed.
|
|
1279
1554
|
*/
|
|
1280
1555
|
this.operationChange = new EventEmitter();
|
|
1281
1556
|
/**
|
|
1282
|
-
*
|
|
1557
|
+
* Emitted when the operation preview is requested.
|
|
1283
1558
|
*/
|
|
1284
1559
|
this.operationPreview = new EventEmitter();
|
|
1285
1560
|
/**
|
|
1286
|
-
*
|
|
1561
|
+
* Emitted when operation editing is cancelled.
|
|
1287
1562
|
*/
|
|
1288
1563
|
this.operationCancel = new EventEmitter();
|
|
1289
1564
|
this.tabIndex = 0;
|
|
@@ -1460,7 +1735,9 @@ class ChainOperationEditorComponent {
|
|
|
1460
1735
|
window.URL.revokeObjectURL(url);
|
|
1461
1736
|
}
|
|
1462
1737
|
openSvgEditor() {
|
|
1463
|
-
const url = this._settings.get('svg-editor', 'https://editor.method.ac/'
|
|
1738
|
+
const url = this._settings.get('svg-editor', 'https://editor.method.ac/'
|
|
1739
|
+
// 'https://boxy-svg.com/app'
|
|
1740
|
+
);
|
|
1464
1741
|
if (url) {
|
|
1465
1742
|
if (this.svg.value) {
|
|
1466
1743
|
this._clipboard.copy(this.svg.value);
|
|
@@ -1534,11 +1811,20 @@ class ChainOperationEditorComponent {
|
|
|
1534
1811
|
deleteElementFeatures(element) {
|
|
1535
1812
|
const id = element.id;
|
|
1536
1813
|
const elementFeatures = { ...this.elementFeatures.value };
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (this.editedElementId === id) {
|
|
1540
|
-
this.editedElementId = undefined;
|
|
1814
|
+
if (!elementFeatures[id]) {
|
|
1815
|
+
return;
|
|
1541
1816
|
}
|
|
1817
|
+
this._dialogService
|
|
1818
|
+
.confirm('Confirm', 'Delete element features?')
|
|
1819
|
+
.subscribe((yes) => {
|
|
1820
|
+
if (yes) {
|
|
1821
|
+
delete elementFeatures[id];
|
|
1822
|
+
this.elementFeatures.setValue(elementFeatures);
|
|
1823
|
+
if (this.editedElementId === id) {
|
|
1824
|
+
this.editedElementId = undefined;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1542
1828
|
}
|
|
1543
1829
|
onElementFeaturesChange(features) {
|
|
1544
1830
|
if (this.editedElementId) {
|
|
@@ -1612,8 +1898,6 @@ class ChainOperationEditorComponent {
|
|
|
1612
1898
|
this.elementFeatures.setValue(operation.diplomatics?.elementFeatures || {});
|
|
1613
1899
|
this.form.markAsPristine();
|
|
1614
1900
|
this.elements = this.parseSvg(operation.diplomatics?.g);
|
|
1615
|
-
// SVG
|
|
1616
|
-
this.requestPreview();
|
|
1617
1901
|
this.updateArgsUI();
|
|
1618
1902
|
}
|
|
1619
1903
|
cancel() {
|
|
@@ -1621,9 +1905,9 @@ class ChainOperationEditorComponent {
|
|
|
1621
1905
|
}
|
|
1622
1906
|
getOperation() {
|
|
1623
1907
|
return {
|
|
1624
|
-
rank: this.rank.value,
|
|
1908
|
+
rank: this.rank.value || undefined,
|
|
1625
1909
|
groupId: this.groupId.value || undefined,
|
|
1626
|
-
features: this.features.value,
|
|
1910
|
+
features: this.features.value?.length ? this.features.value : undefined,
|
|
1627
1911
|
sources: this.sources.value?.length ? this.sources.value : undefined,
|
|
1628
1912
|
diplomatics: {
|
|
1629
1913
|
g: this.svg.value,
|
|
@@ -1633,13 +1917,13 @@ class ChainOperationEditorComponent {
|
|
|
1633
1917
|
id: this.id,
|
|
1634
1918
|
type: this.type.value,
|
|
1635
1919
|
at: this.at.value,
|
|
1636
|
-
atAsIndex: this.atAsIndex.value,
|
|
1637
|
-
to: this.to.value,
|
|
1638
|
-
toAsIndex: this.toAsIndex.value,
|
|
1920
|
+
atAsIndex: this.atAsIndex.value || undefined,
|
|
1921
|
+
to: this.to.value || undefined,
|
|
1922
|
+
toAsIndex: this.toAsIndex.value || undefined,
|
|
1639
1923
|
inputTag: this.inputTag.value || undefined,
|
|
1640
1924
|
outputTag: this.outputTag.value || undefined,
|
|
1641
1925
|
run: this.run.value,
|
|
1642
|
-
toRun: this.toRun.value,
|
|
1926
|
+
toRun: this.toRun.value || undefined,
|
|
1643
1927
|
value: this.value.value || undefined,
|
|
1644
1928
|
};
|
|
1645
1929
|
}
|
|
@@ -1653,16 +1937,16 @@ class ChainOperationEditorComponent {
|
|
|
1653
1937
|
this._operation = this.getOperation();
|
|
1654
1938
|
this.operationChange.emit(this._operation);
|
|
1655
1939
|
}
|
|
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:
|
|
1940
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: ChainOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$3.Clipboard }, { token: SettingsService }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1941
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
1942
|
// 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$
|
|
1943
|
+
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$1.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: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.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: i14.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i14.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
1944
|
// monaco
|
|
1661
|
-
NgeMonacoModule }, { kind: "component", type:
|
|
1945
|
+
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
1946
|
// myrmex
|
|
1663
|
-
FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "
|
|
1947
|
+
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
1948
|
}
|
|
1665
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
1949
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: ChainOperationEditorComponent, decorators: [{
|
|
1666
1950
|
type: Component,
|
|
1667
1951
|
args: [{ selector: 'gve-chain-operation-editor', standalone: true, imports: [
|
|
1668
1952
|
CommonModule,
|
|
@@ -1686,8 +1970,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1686
1970
|
FeatureSetEditorComponent,
|
|
1687
1971
|
SimpleTreeComponent,
|
|
1688
1972
|
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: [{
|
|
1973
|
+
], 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"] }]
|
|
1974
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$3.Clipboard }, { type: SettingsService }, { type: i2$2.DialogService }], propDecorators: { operation: [{
|
|
1691
1975
|
type: Input
|
|
1692
1976
|
}], snapshot: [{
|
|
1693
1977
|
type: Input
|
|
@@ -1701,6 +1985,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1701
1985
|
type: Output
|
|
1702
1986
|
}] } });
|
|
1703
1987
|
|
|
1988
|
+
/**
|
|
1989
|
+
* 🔑 `gve-feature-set-view`
|
|
1990
|
+
*
|
|
1991
|
+
* A component to display a list of features.
|
|
1992
|
+
* Used by the chain result view component.
|
|
1993
|
+
*
|
|
1994
|
+
* - ▶️ `features` (`Feature[]`): the features to display.
|
|
1995
|
+
* - ▶️ `featNames` (`LabeledId[]`): the list of feature names to display
|
|
1996
|
+
* in the _name_ selection. This is used when you have a closed list of features.
|
|
1997
|
+
* - ▶️ `featValues` (`FeatureMap`): the feature values map. When specified
|
|
1998
|
+
* and the user selects a feature name present in the map keys, the corresponding
|
|
1999
|
+
* values will be used to populate the value selection.
|
|
2000
|
+
* - ▶️ `filterThreshold` (`number`): the threshold at which the features filter
|
|
2001
|
+
* should become visible. If set to 0, the filter is always visible; if set to -1,
|
|
2002
|
+
* it is always invisible; otherwise, it gets visible when the number of features
|
|
2003
|
+
* is greater than the threshold. Default is 5.
|
|
2004
|
+
*/
|
|
1704
2005
|
class FeatureSetViewComponent {
|
|
1705
2006
|
/**
|
|
1706
2007
|
* The features.
|
|
@@ -1719,9 +2020,12 @@ class FeatureSetViewComponent {
|
|
|
1719
2020
|
constructor(formBuilder) {
|
|
1720
2021
|
this._features = [];
|
|
1721
2022
|
/**
|
|
1722
|
-
* The
|
|
2023
|
+
* The threshold at which the features filter should become visible.
|
|
2024
|
+
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
2025
|
+
* invisible; otherwise, it gets visible when the number of features
|
|
2026
|
+
* is greater than the threshold. Default is 5.
|
|
1723
2027
|
*/
|
|
1724
|
-
this.
|
|
2028
|
+
this.filterThreshold = 5;
|
|
1725
2029
|
this.filteredFeatures = [];
|
|
1726
2030
|
this.filter = formBuilder.control(null);
|
|
1727
2031
|
}
|
|
@@ -1741,10 +2045,10 @@ class FeatureSetViewComponent {
|
|
|
1741
2045
|
ngOnDestroy() {
|
|
1742
2046
|
this._sub?.unsubscribe();
|
|
1743
2047
|
}
|
|
1744
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1745
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2048
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureSetViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2049
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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 [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
2050
|
}
|
|
1747
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2051
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FeatureSetViewComponent, decorators: [{
|
|
1748
2052
|
type: Component,
|
|
1749
2053
|
args: [{ selector: 'gve-feature-set-view', standalone: true, imports: [
|
|
1750
2054
|
CommonModule,
|
|
@@ -1754,28 +2058,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1754
2058
|
MatIconModule,
|
|
1755
2059
|
MatInputModule,
|
|
1756
2060
|
NgToolsModule,
|
|
1757
|
-
], template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (
|
|
2061
|
+
], 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 [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
2062
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { features: [{
|
|
1759
2063
|
type: Input
|
|
1760
2064
|
}], featNames: [{
|
|
1761
2065
|
type: Input
|
|
1762
2066
|
}], featValues: [{
|
|
1763
2067
|
type: Input
|
|
1764
|
-
}],
|
|
2068
|
+
}], filterThreshold: [{
|
|
1765
2069
|
type: Input
|
|
1766
2070
|
}] } });
|
|
1767
2071
|
|
|
1768
2072
|
/**
|
|
1769
|
-
*
|
|
2073
|
+
* 🔑 `gve-steps-map`
|
|
2074
|
+
*
|
|
2075
|
+
* A map of steps in a chain operation context. This shows the list of
|
|
2076
|
+
* steps in the execution of a set of operations, and allows the user to
|
|
2077
|
+
* pick a step to view.
|
|
2078
|
+
*
|
|
2079
|
+
* Used by the `gve-chain-result-view` component.
|
|
2080
|
+
*
|
|
2081
|
+
* - ▶️ `steps` (`ChainOperationContextStep[]`): the steps to display.
|
|
2082
|
+
* - ▶️ `selectedStep` (`ChainOperationContextStep`): the step that is
|
|
2083
|
+
* currently selected.
|
|
2084
|
+
* - ▶️ `textFontSize` (`string`): the font size of the steps text.
|
|
2085
|
+
* - 🔥 `selectedStepChange` (`ChainOperationContextStep`): emitted when
|
|
2086
|
+
* the selectd step has been changed by the user. This is not emitted
|
|
2087
|
+
* when the selected step is changed programmatically, unless this is
|
|
2088
|
+
* the first time the steps are set. In that case, the last step is
|
|
2089
|
+
* automatically selected.
|
|
1770
2090
|
*/
|
|
1771
2091
|
class StepsMapComponent {
|
|
1772
2092
|
constructor() {
|
|
2093
|
+
this.lines = [];
|
|
1773
2094
|
/**
|
|
1774
2095
|
* The font size of the steps text.
|
|
1775
2096
|
*/
|
|
1776
|
-
this.textFontSize = '0.
|
|
2097
|
+
this.textFontSize = '0.8em';
|
|
1777
2098
|
/**
|
|
1778
|
-
* Emitted when the
|
|
2099
|
+
* Emitted when the selected step has changed by user, or when
|
|
2100
|
+
* the steps are set for the first time.
|
|
1779
2101
|
*/
|
|
1780
2102
|
this.selectedStepChange = new EventEmitter();
|
|
1781
2103
|
}
|
|
@@ -1789,19 +2111,29 @@ class StepsMapComponent {
|
|
|
1789
2111
|
if (this._steps === value) {
|
|
1790
2112
|
return;
|
|
1791
2113
|
}
|
|
2114
|
+
const first = this._steps === undefined;
|
|
1792
2115
|
this.selectedStep = undefined;
|
|
1793
2116
|
this._steps = value || undefined;
|
|
2117
|
+
if (first && this._steps?.length) {
|
|
2118
|
+
this.selectedStep = this._steps[this._steps.length - 1];
|
|
2119
|
+
this.selectedStepChange.emit(this.selectedStep);
|
|
2120
|
+
}
|
|
2121
|
+
this.updateLines();
|
|
2122
|
+
}
|
|
2123
|
+
updateLines() {
|
|
2124
|
+
this.lines = this.selectedStep?.result?.split('\n') || [];
|
|
1794
2125
|
}
|
|
1795
|
-
|
|
2126
|
+
onStepClick(step) {
|
|
1796
2127
|
this.selectedStep = step;
|
|
2128
|
+
this.updateLines();
|
|
1797
2129
|
this.selectedStepChange.emit(step);
|
|
1798
2130
|
}
|
|
1799
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1800
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2131
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: StepsMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2132
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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\">\r\n @for (line of lines; track $index) {\r\n <div class=\"line\">{{ line }}</div>\r\n }\r\n <div></div>\r\n </div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#ffc}.step{border: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
2133
|
}
|
|
1802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2134
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: StepsMapComponent, decorators: [{
|
|
1803
2135
|
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)=\"
|
|
2136
|
+
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\">\r\n @for (line of lines; track $index) {\r\n <div class=\"line\">{{ line }}</div>\r\n }\r\n <div></div>\r\n </div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#ffc}.step{border: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
2137
|
}], propDecorators: { steps: [{
|
|
1806
2138
|
type: Input
|
|
1807
2139
|
}], selectedStep: [{
|
|
@@ -1813,7 +2145,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1813
2145
|
}] } });
|
|
1814
2146
|
|
|
1815
2147
|
/**
|
|
1816
|
-
*
|
|
2148
|
+
* 🔑 `gve-chain-result-view`
|
|
2149
|
+
*
|
|
2150
|
+
* Component to display a chain result. This provides a version picker
|
|
2151
|
+
* by staged version or simple step, and shows the selected step's text
|
|
2152
|
+
* and features, plus a steps map. User can pick a step from both the
|
|
2153
|
+
* pickers or the map.
|
|
2154
|
+
* Used by the `gve-snapshot-editor` component.
|
|
2155
|
+
*
|
|
2156
|
+
* - ▶️ `result` (`CharChainResult`): the result to display.
|
|
2157
|
+
* - 🔥 `stepPick` (`ChainOperationContextStep`): emitted when a
|
|
2158
|
+
* result's step is picked by the user.
|
|
1817
2159
|
*/
|
|
1818
2160
|
class ChainResultViewComponent {
|
|
1819
2161
|
/**
|
|
@@ -1828,18 +2170,30 @@ class ChainResultViewComponent {
|
|
|
1828
2170
|
}
|
|
1829
2171
|
this._result = value || undefined;
|
|
1830
2172
|
this.updateForm(this._result);
|
|
1831
|
-
// select
|
|
1832
|
-
|
|
1833
|
-
this._stepPickFrozen = true;
|
|
1834
|
-
this.step = this._result.steps[this._result.steps.length - 1];
|
|
1835
|
-
this.tag.setValue(this.step.outputTag);
|
|
1836
|
-
this._stepPickFrozen = false;
|
|
1837
|
-
}
|
|
2173
|
+
// select the initial step if set
|
|
2174
|
+
this.selectInitialStep();
|
|
1838
2175
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2176
|
+
/**
|
|
2177
|
+
* The index of the initial step to display after the result is set.
|
|
2178
|
+
* If the index is negative, it is counted from the end of the steps.
|
|
2179
|
+
*/
|
|
2180
|
+
get initialStepIndex() {
|
|
2181
|
+
return this._initialStepIndex;
|
|
2182
|
+
}
|
|
2183
|
+
set initialStepIndex(value) {
|
|
2184
|
+
if (value === undefined || value === null) {
|
|
2185
|
+
this._initialStepIndex = undefined;
|
|
2186
|
+
this.step = undefined;
|
|
2187
|
+
}
|
|
2188
|
+
else {
|
|
2189
|
+
this._initialStepIndex = value;
|
|
2190
|
+
this.selectInitialStep();
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
constructor(formBuilder) {
|
|
2194
|
+
/**
|
|
2195
|
+
* Emitted when a result's step is picked by the user.
|
|
2196
|
+
*/
|
|
1843
2197
|
this.stepPick = new EventEmitter();
|
|
1844
2198
|
this.versionTags = [];
|
|
1845
2199
|
this.tags = [];
|
|
@@ -1849,21 +2203,31 @@ class ChainResultViewComponent {
|
|
|
1849
2203
|
}
|
|
1850
2204
|
ngOnInit() {
|
|
1851
2205
|
this._subs = [
|
|
2206
|
+
// whenever the staged version tag changes, update the form
|
|
1852
2207
|
this.versionTag.valueChanges
|
|
1853
2208
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
1854
2209
|
.subscribe((value) => {
|
|
1855
2210
|
this.tag.setValue(this.getTagFromVersion(value) || null);
|
|
1856
2211
|
}),
|
|
2212
|
+
// whenever the tag changes, update the step
|
|
1857
2213
|
this.tag.valueChanges
|
|
1858
2214
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
1859
2215
|
.subscribe((value) => {
|
|
1860
2216
|
this.step = this._result?.steps.find((step) => step.outputTag === value);
|
|
1861
|
-
if (this.step
|
|
2217
|
+
if (this.step) {
|
|
1862
2218
|
this.stepPick.emit(this.step);
|
|
1863
2219
|
}
|
|
1864
2220
|
}),
|
|
1865
2221
|
];
|
|
1866
2222
|
}
|
|
2223
|
+
selectInitialStep() {
|
|
2224
|
+
if (this._initialStepIndex !== undefined && this._result?.steps) {
|
|
2225
|
+
this.step =
|
|
2226
|
+
this._result.steps[this._initialStepIndex < 0
|
|
2227
|
+
? this._result.steps.length + this._initialStepIndex
|
|
2228
|
+
: this._initialStepIndex];
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
1867
2231
|
ngOnDestroy() {
|
|
1868
2232
|
this._subs?.forEach((sub) => sub.unsubscribe());
|
|
1869
2233
|
}
|
|
@@ -1960,13 +2324,14 @@ class ChainResultViewComponent {
|
|
|
1960
2324
|
this.selectionFeatures = features;
|
|
1961
2325
|
}
|
|
1962
2326
|
onStepChange(step) {
|
|
2327
|
+
// setting the tag will trigger the step change
|
|
1963
2328
|
this.tag.setValue(step.outputTag);
|
|
1964
2329
|
this.selectionFeatures = [];
|
|
1965
2330
|
}
|
|
1966
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
1967
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2331
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: ChainResultViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2332
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
2333
|
}
|
|
1969
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: ChainResultViewComponent, decorators: [{
|
|
1970
2335
|
type: Component,
|
|
1971
2336
|
args: [{ selector: 'gve-chain-result-view', standalone: true, imports: [
|
|
1972
2337
|
CommonModule,
|
|
@@ -1981,11 +2346,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
1981
2346
|
], 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
2347
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { result: [{
|
|
1983
2348
|
type: Input
|
|
2349
|
+
}], initialStepIndex: [{
|
|
2350
|
+
type: Input
|
|
1984
2351
|
}], stepPick: [{
|
|
1985
2352
|
type: Output
|
|
1986
2353
|
}] } });
|
|
1987
2354
|
|
|
2355
|
+
/**
|
|
2356
|
+
* 🔑 `gve-ln-heights-editor`
|
|
2357
|
+
*
|
|
2358
|
+
* A component to edit line heights.
|
|
2359
|
+
* Used by the `gve-snapshot-editor` component.
|
|
2360
|
+
*
|
|
2361
|
+
* - ▶️ `lineCount` (`number`): the number of lines.
|
|
2362
|
+
* - ▶️ `heights` (`Record<number, number>`): the line heights.
|
|
2363
|
+
* - 🔥 `heightsChange` (`EventEmitter<Record<number, number>>`): the event
|
|
2364
|
+
* emitted when the heights change.
|
|
2365
|
+
*/
|
|
1988
2366
|
class LnHeightsEditorComponent {
|
|
2367
|
+
/**
|
|
2368
|
+
* The total number of lines in the text.
|
|
2369
|
+
*/
|
|
1989
2370
|
get lineCount() {
|
|
1990
2371
|
return this._lineCount;
|
|
1991
2372
|
}
|
|
@@ -1995,6 +2376,10 @@ class LnHeightsEditorComponent {
|
|
|
1995
2376
|
this._lineCount = value;
|
|
1996
2377
|
this.lineNumbers = Array.from({ length: value }, (_, i) => i + 1);
|
|
1997
2378
|
}
|
|
2379
|
+
/**
|
|
2380
|
+
* The heights map of the lines. Each key is a line number and the value is
|
|
2381
|
+
* the height of the line.
|
|
2382
|
+
*/
|
|
1998
2383
|
get heights() {
|
|
1999
2384
|
return this._heights;
|
|
2000
2385
|
}
|
|
@@ -2005,20 +2390,33 @@ class LnHeightsEditorComponent {
|
|
|
2005
2390
|
}
|
|
2006
2391
|
constructor(formBuilder) {
|
|
2007
2392
|
this._lineCount = 0;
|
|
2393
|
+
/**
|
|
2394
|
+
* The event emitted when the heights change.
|
|
2395
|
+
*/
|
|
2008
2396
|
this.heightsChange = new EventEmitter();
|
|
2009
2397
|
this.lineNumbers = [];
|
|
2010
2398
|
this.lineNumber = formBuilder.control(0, { nonNullable: true });
|
|
2011
2399
|
this.height = formBuilder.control(0, { nonNullable: true });
|
|
2012
2400
|
}
|
|
2401
|
+
pruneHeights() {
|
|
2402
|
+
// remove all the heigths with value=0
|
|
2403
|
+
this._heights = Object.fromEntries(Object.entries(this._heights || {}).filter(([_, v]) => v !== 0));
|
|
2404
|
+
// if heights are now empty, set to undefined
|
|
2405
|
+
if (Object.keys(this._heights).length === 0) {
|
|
2406
|
+
this._heights = undefined;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2013
2409
|
ngOnInit() {
|
|
2014
2410
|
this._subs = [];
|
|
2015
2411
|
// update heights[ln] when height changes and emit heightsChange
|
|
2016
2412
|
this._subs.push(this.height.valueChanges
|
|
2017
2413
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
2018
2414
|
.subscribe((value) => {
|
|
2019
|
-
if (!this._heights)
|
|
2020
|
-
|
|
2415
|
+
if (!this._heights) {
|
|
2416
|
+
this._heights = {};
|
|
2417
|
+
}
|
|
2021
2418
|
this._heights[this.lineNumber.value] = value;
|
|
2419
|
+
this.pruneHeights();
|
|
2022
2420
|
this.heightsChange.emit(this._heights);
|
|
2023
2421
|
}));
|
|
2024
2422
|
// update height when line number changes
|
|
@@ -2037,10 +2435,10 @@ class LnHeightsEditorComponent {
|
|
|
2037
2435
|
this._heights = undefined;
|
|
2038
2436
|
this.heightsChange.emit(undefined);
|
|
2039
2437
|
}
|
|
2040
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2041
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2438
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: LnHeightsEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2439
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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
2440
|
}
|
|
2043
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: LnHeightsEditorComponent, decorators: [{
|
|
2044
2442
|
type: Component,
|
|
2045
2443
|
args: [{ selector: 'gve-ln-heights-editor', standalone: true, imports: [
|
|
2046
2444
|
CommonModule,
|
|
@@ -2051,7 +2449,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2051
2449
|
MatInputModule,
|
|
2052
2450
|
MatSelectModule,
|
|
2053
2451
|
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"] }]
|
|
2452
|
+
], 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
2453
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { lineCount: [{
|
|
2056
2454
|
type: Input
|
|
2057
2455
|
}], heights: [{
|
|
@@ -2060,7 +2458,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2060
2458
|
type: Output
|
|
2061
2459
|
}] } });
|
|
2062
2460
|
|
|
2461
|
+
/**
|
|
2462
|
+
* 🔑 `gve-animation-timeline-set`
|
|
2463
|
+
*
|
|
2464
|
+
* A component to edit a set of animation timelines.
|
|
2465
|
+
* Used by the `gve-snapshot-editor` component.
|
|
2466
|
+
*
|
|
2467
|
+
* - ▶️ `timelines` (`GveAnimationTimeline[]`): the animation timelines to edit.
|
|
2468
|
+
* - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected by the tween.
|
|
2469
|
+
* - ▶️ `tags` (`string[]`): the tags that can be used by the timeline.
|
|
2470
|
+
* - 🔥 `timelinesChange` (`GveAnimationTimeline[]`): emitted when the timelines are changed.
|
|
2471
|
+
* - 🔥 `timelinesCancel` (`void`): emitted when the timeline editing is canceled.
|
|
2472
|
+
*/
|
|
2063
2473
|
class AnimationTimelineSetComponent {
|
|
2474
|
+
/**
|
|
2475
|
+
* The animation timelines to edit.
|
|
2476
|
+
*/
|
|
2064
2477
|
get timelines() {
|
|
2065
2478
|
return this._timelines;
|
|
2066
2479
|
}
|
|
@@ -2074,9 +2487,18 @@ class AnimationTimelineSetComponent {
|
|
|
2074
2487
|
constructor(formBuilder, _dialogService) {
|
|
2075
2488
|
this._dialogService = _dialogService;
|
|
2076
2489
|
this._timelines = [];
|
|
2490
|
+
/**
|
|
2491
|
+
* The tags that can be used by the timeline.
|
|
2492
|
+
*/
|
|
2077
2493
|
this.tags = [];
|
|
2494
|
+
/**
|
|
2495
|
+
* Emitted when the timelines are changed.
|
|
2496
|
+
*/
|
|
2078
2497
|
this.timelinesChange = new EventEmitter();
|
|
2079
|
-
|
|
2498
|
+
/**
|
|
2499
|
+
* Emitted when the timeline editing is canceled.
|
|
2500
|
+
*/
|
|
2501
|
+
this.timelinesCancel = new EventEmitter();
|
|
2080
2502
|
this.set = formBuilder.control([], {
|
|
2081
2503
|
nonNullable: true,
|
|
2082
2504
|
});
|
|
@@ -2105,7 +2527,7 @@ class AnimationTimelineSetComponent {
|
|
|
2105
2527
|
editTimeline(index) {
|
|
2106
2528
|
this.editedTimeline = this._timelines[index];
|
|
2107
2529
|
}
|
|
2108
|
-
|
|
2530
|
+
onTimelineChange(timeline) {
|
|
2109
2531
|
const timelines = [...this.set.value];
|
|
2110
2532
|
const i = timelines.findIndex((t) => t.tag === timeline.tag);
|
|
2111
2533
|
if (i === -1) {
|
|
@@ -2138,10 +2560,10 @@ class AnimationTimelineSetComponent {
|
|
|
2138
2560
|
}
|
|
2139
2561
|
});
|
|
2140
2562
|
}
|
|
2141
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.
|
|
2142
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.
|
|
2563
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTimelineSetComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.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
2565
|
}
|
|
2144
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
2566
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: AnimationTimelineSetComponent, decorators: [{
|
|
2145
2567
|
type: Component,
|
|
2146
2568
|
args: [{ selector: 'gve-animation-timeline-set', standalone: true, imports: [
|
|
2147
2569
|
CommonModule,
|
|
@@ -2157,7 +2579,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2157
2579
|
MatTooltipModule,
|
|
2158
2580
|
NgToolsModule,
|
|
2159
2581
|
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)=\"
|
|
2582
|
+
], 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
2583
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.DialogService }], propDecorators: { timelines: [{
|
|
2162
2584
|
type: Input
|
|
2163
2585
|
}], elementIds: [{
|
|
@@ -2166,73 +2588,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2166
2588
|
type: Input
|
|
2167
2589
|
}], timelinesChange: [{
|
|
2168
2590
|
type: Output
|
|
2169
|
-
}],
|
|
2591
|
+
}], timelinesCancel: [{
|
|
2170
2592
|
type: Output
|
|
2171
2593
|
}] } });
|
|
2172
2594
|
|
|
2173
|
-
|
|
2174
|
-
* Service to interact with the GVE API.
|
|
2175
|
-
*/
|
|
2176
|
-
class GveApiService {
|
|
2177
|
-
constructor(_http, _error, _env) {
|
|
2178
|
-
this._http = _http;
|
|
2179
|
-
this._error = _error;
|
|
2180
|
-
this._env = _env;
|
|
2181
|
-
}
|
|
2182
|
-
/**
|
|
2183
|
-
* Parse the specified text into operations. Each operation is a line
|
|
2184
|
-
* of the text.
|
|
2185
|
-
* @param text The text to parse.
|
|
2186
|
-
* @returns The operations.
|
|
2187
|
-
*/
|
|
2188
|
-
parseOperations(text) {
|
|
2189
|
-
return this._http
|
|
2190
|
-
.post(`${this._env.get('apiUrl')}text/operations/parse`, {
|
|
2191
|
-
text,
|
|
2192
|
-
})
|
|
2193
|
-
.pipe(catchError(this._error.handleError));
|
|
2194
|
-
}
|
|
2195
|
-
/**
|
|
2196
|
-
* Run operations on the specified text.
|
|
2197
|
-
*
|
|
2198
|
-
* @param text The base text.
|
|
2199
|
-
* @param operations The operations to run.
|
|
2200
|
-
* @returns Result wrapper.
|
|
2201
|
-
*/
|
|
2202
|
-
runOperations(text, operations) {
|
|
2203
|
-
return this._http
|
|
2204
|
-
.post(`${this._env.get('apiUrl')}text/operations/run`, {
|
|
2205
|
-
text,
|
|
2206
|
-
operations,
|
|
2207
|
-
})
|
|
2208
|
-
.pipe(catchError(this._error.handleError));
|
|
2209
|
-
}
|
|
2210
|
-
/**
|
|
2211
|
-
* Get the input and output tags for the given operations.
|
|
2212
|
-
*
|
|
2213
|
-
* @param operations The operations.
|
|
2214
|
-
* @returns Result wrapper.
|
|
2215
|
-
*/
|
|
2216
|
-
getTags(operations) {
|
|
2217
|
-
return this._http
|
|
2218
|
-
.post(`${this._env.get('apiUrl')}text/operations/tags`, {
|
|
2219
|
-
operations,
|
|
2220
|
-
})
|
|
2221
|
-
.pipe(catchError(this._error.handleError));
|
|
2222
|
-
}
|
|
2223
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: GveApiService, deps: [{ token: i1$1.HttpClient }, { token: i2$1.ErrorService }, { token: i2$1.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2224
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
|
|
2225
|
-
}
|
|
2226
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: GveApiService, decorators: [{
|
|
2227
|
-
type: Injectable,
|
|
2228
|
-
args: [{
|
|
2229
|
-
providedIn: 'root',
|
|
2230
|
-
}]
|
|
2231
|
-
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2$1.ErrorService }, { type: i2$1.EnvService }] });
|
|
2232
|
-
|
|
2595
|
+
// default snapshot view title
|
|
2233
2596
|
const VIEW_TITLE = 'preview';
|
|
2597
|
+
// suffix for IDs of SVG elements to be made transparent at first rendition
|
|
2598
|
+
const SVG_TRANSP_SUFFIX = '_t';
|
|
2234
2599
|
/**
|
|
2235
|
-
*
|
|
2600
|
+
* 🔑 `gve-snapshot-editor`
|
|
2601
|
+
*
|
|
2602
|
+
* A component to edit a text snapshot. This is the top level component in the GVE core
|
|
2603
|
+
* library.
|
|
2604
|
+
*
|
|
2605
|
+
* - ▶️ `snapshot` (`Snapshot`): the snapshot to edit.
|
|
2606
|
+
* - ▶️ `batchOps` (`string`): the batch operations text to set for the editor.
|
|
2607
|
+
* - ▶️ `noSave` (`boolean`): true to disable saving.
|
|
2608
|
+
* - 🔥 `snapshotChange` (`Snapshot`): emitted when the user saves the edited snapshot.
|
|
2609
|
+
* - 🔥 `snapshotCancel` (`void`): emitted when the user cancels the snapshot editing.
|
|
2236
2610
|
*/
|
|
2237
2611
|
class SnapshotEditorComponent {
|
|
2238
2612
|
/**
|
|
@@ -2247,10 +2621,13 @@ class SnapshotEditorComponent {
|
|
|
2247
2621
|
}
|
|
2248
2622
|
this._snapshot = value || undefined;
|
|
2249
2623
|
this.updateForm(this._snapshot);
|
|
2250
|
-
|
|
2624
|
+
setTimeout(() => {
|
|
2625
|
+
this.runToLast();
|
|
2626
|
+
}, 0);
|
|
2251
2627
|
}
|
|
2252
|
-
constructor(formBuilder, _api, _dialogService, _snackbar) {
|
|
2628
|
+
constructor(formBuilder, _api, _dialog, _dialogService, _snackbar) {
|
|
2253
2629
|
this._api = _api;
|
|
2630
|
+
this._dialog = _dialog;
|
|
2254
2631
|
this._dialogService = _dialogService;
|
|
2255
2632
|
this._snackbar = _snackbar;
|
|
2256
2633
|
this._nanoid = customAlphabet('1234567890abcdef', 10);
|
|
@@ -2262,9 +2639,13 @@ class SnapshotEditorComponent {
|
|
|
2262
2639
|
* Emitted when the user cancels the snapshot editing.
|
|
2263
2640
|
*/
|
|
2264
2641
|
this.snapshotCancel = new EventEmitter();
|
|
2265
|
-
|
|
2642
|
+
// list of operations output tags
|
|
2266
2643
|
this.opTags = [];
|
|
2644
|
+
// list of operation diplomatic.g element IDs
|
|
2267
2645
|
this.opElementIds = [];
|
|
2646
|
+
// the lines count for the current base text
|
|
2647
|
+
this.lineCount = 0;
|
|
2648
|
+
this.editedOpIndex = -1;
|
|
2268
2649
|
this.opTypeMap = {
|
|
2269
2650
|
0: 'replace',
|
|
2270
2651
|
1: 'delete',
|
|
@@ -2275,9 +2656,10 @@ class SnapshotEditorComponent {
|
|
|
2275
2656
|
6: 'swap',
|
|
2276
2657
|
7: 'annotate',
|
|
2277
2658
|
};
|
|
2278
|
-
|
|
2659
|
+
// snapshot view data
|
|
2279
2660
|
this.viewTitle = VIEW_TITLE;
|
|
2280
2661
|
this.rulers = true;
|
|
2662
|
+
this.initialStepIndex = -1;
|
|
2281
2663
|
// general
|
|
2282
2664
|
this.width = new FormControl(800, { nonNullable: true });
|
|
2283
2665
|
this.height = new FormControl(600, { nonNullable: true });
|
|
@@ -2298,7 +2680,6 @@ class SnapshotEditorComponent {
|
|
|
2298
2680
|
this.operations = new FormControl([], {
|
|
2299
2681
|
nonNullable: true,
|
|
2300
2682
|
});
|
|
2301
|
-
this.inputOps = new FormControl(null);
|
|
2302
2683
|
this.opStyle = new FormControl(null);
|
|
2303
2684
|
// image
|
|
2304
2685
|
this.imageUrl = new FormControl(null);
|
|
@@ -2312,6 +2693,7 @@ class SnapshotEditorComponent {
|
|
|
2312
2693
|
this.timelines = new FormControl([], {
|
|
2313
2694
|
nonNullable: true,
|
|
2314
2695
|
});
|
|
2696
|
+
// form
|
|
2315
2697
|
this.form = formBuilder.group({
|
|
2316
2698
|
width: this.width,
|
|
2317
2699
|
height: this.height,
|
|
@@ -2325,7 +2707,6 @@ class SnapshotEditorComponent {
|
|
|
2325
2707
|
spcWidthOffset: this.spcWidthOffset,
|
|
2326
2708
|
textStyle: this.textStyle,
|
|
2327
2709
|
operations: this.operations,
|
|
2328
|
-
inputOps: this.inputOps,
|
|
2329
2710
|
opStyle: this.opStyle,
|
|
2330
2711
|
// image
|
|
2331
2712
|
imageUrl: this.imageUrl,
|
|
@@ -2339,43 +2720,38 @@ class SnapshotEditorComponent {
|
|
|
2339
2720
|
timelines: this.timelines,
|
|
2340
2721
|
});
|
|
2341
2722
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2723
|
+
/**
|
|
2724
|
+
* Set the view data for the snapshot view.
|
|
2725
|
+
*
|
|
2726
|
+
* @param snapshot The optional snapshot data to use. If not provided,
|
|
2727
|
+
* a snapshot will be created from the form data.
|
|
2728
|
+
* @param title The optional title to set for the view.
|
|
2729
|
+
*/
|
|
2730
|
+
setViewData(snapshot, title = VIEW_TITLE) {
|
|
2731
|
+
console.log('set view data');
|
|
2732
|
+
// get the snapshot to use
|
|
2350
2733
|
if (!snapshot) {
|
|
2351
2734
|
snapshot = this.getSnapshot();
|
|
2352
2735
|
}
|
|
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
2736
|
// update view data
|
|
2364
2737
|
this.viewTitle = title;
|
|
2365
2738
|
this.visualInfo = undefined;
|
|
2366
2739
|
this.viewData = {
|
|
2367
2740
|
snapshot: snapshot,
|
|
2368
2741
|
options: {
|
|
2369
|
-
|
|
2742
|
+
debug: this.debug,
|
|
2370
2743
|
delayedRender: true,
|
|
2371
2744
|
showRulers: true,
|
|
2372
2745
|
showGrid: true,
|
|
2373
2746
|
panZoom: true,
|
|
2747
|
+
transparentIds: this._transparentIds,
|
|
2374
2748
|
},
|
|
2375
2749
|
};
|
|
2376
|
-
console.log(
|
|
2750
|
+
console.log('view data: ', this.viewData);
|
|
2377
2751
|
}
|
|
2378
2752
|
updateForm(snapshot) {
|
|
2753
|
+
this._transparentIds = undefined;
|
|
2754
|
+
this.initialStepIndex = -1;
|
|
2379
2755
|
if (!snapshot) {
|
|
2380
2756
|
this.form.reset();
|
|
2381
2757
|
}
|
|
@@ -2399,7 +2775,6 @@ class SnapshotEditorComponent {
|
|
|
2399
2775
|
this.spcWidthOffset.setValue(snapshot.textOptions?.spcWidthOffset);
|
|
2400
2776
|
this.textStyle.setValue(snapshot.textStyle || null);
|
|
2401
2777
|
this.operations.setValue(snapshot.operations);
|
|
2402
|
-
this.inputOps.reset();
|
|
2403
2778
|
this.opStyle.setValue(snapshot.opStyle || null);
|
|
2404
2779
|
// timelines
|
|
2405
2780
|
const timelines = [];
|
|
@@ -2410,9 +2785,76 @@ class SnapshotEditorComponent {
|
|
|
2410
2785
|
}
|
|
2411
2786
|
this.timelines.setValue(timelines);
|
|
2412
2787
|
}
|
|
2788
|
+
this.updateLineCount(this.baseText.value);
|
|
2789
|
+
this.updateOpLists();
|
|
2413
2790
|
this.form.markAsPristine();
|
|
2414
2791
|
}
|
|
2792
|
+
// #region base text
|
|
2793
|
+
/**
|
|
2794
|
+
* Update the line count based on the received text.
|
|
2795
|
+
* @param text The text to use for counting lines.
|
|
2796
|
+
*/
|
|
2797
|
+
updateLineCount(text) {
|
|
2798
|
+
if (!text.length) {
|
|
2799
|
+
this.lineCount = 0;
|
|
2800
|
+
}
|
|
2801
|
+
else {
|
|
2802
|
+
this.lineCount = text.filter((c) => c.data === '\n').length + 1;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* Handle the event fired by the base text editor to pick a text range.
|
|
2807
|
+
* @param range The picked range.
|
|
2808
|
+
*/
|
|
2809
|
+
onRangePick(range) {
|
|
2810
|
+
this.textRange = range;
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Handle the event fired by the base text editor to change the base text.
|
|
2814
|
+
* @param text The text to set.
|
|
2815
|
+
*/
|
|
2816
|
+
onTextChange(text) {
|
|
2817
|
+
console.log('text change', text);
|
|
2818
|
+
// update the form control and reset the current text range
|
|
2819
|
+
this.baseText.setValue(text);
|
|
2820
|
+
this.baseText.updateValueAndValidity();
|
|
2821
|
+
this.baseText.markAsDirty();
|
|
2822
|
+
this.textRange = undefined;
|
|
2823
|
+
// update the line count
|
|
2824
|
+
this.updateLineCount(text);
|
|
2825
|
+
// remove all operations and update the view data
|
|
2826
|
+
this.removeAllOperations();
|
|
2827
|
+
// remove all timelines
|
|
2828
|
+
this.timelines.reset();
|
|
2829
|
+
}
|
|
2830
|
+
// #endregion
|
|
2831
|
+
// #region operations
|
|
2832
|
+
/**
|
|
2833
|
+
* Update the lists of operation output tags and element IDs by collecting
|
|
2834
|
+
* all the operation tags and the IDs of the elements in their diplomatic.g
|
|
2835
|
+
* SVG code if any.
|
|
2836
|
+
*/
|
|
2837
|
+
updateOpLists() {
|
|
2838
|
+
const tags = new Set();
|
|
2839
|
+
const ids = new Set();
|
|
2840
|
+
let n = 0;
|
|
2841
|
+
for (const op of this.operations.value) {
|
|
2842
|
+
// output tag
|
|
2843
|
+
n++;
|
|
2844
|
+
tags.add(op.outputTag || `v${n}`);
|
|
2845
|
+
// element IDs
|
|
2846
|
+
if (op.diplomatics?.g) {
|
|
2847
|
+
this.parseSvgIds(op.diplomatics.g)?.forEach((id) => ids.add(id));
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
this.opTags = [...tags];
|
|
2851
|
+
this.opElementIds = [...ids];
|
|
2852
|
+
}
|
|
2853
|
+
/**
|
|
2854
|
+
* Edit a new operation.
|
|
2855
|
+
*/
|
|
2415
2856
|
editNewOperation() {
|
|
2857
|
+
// create a new operation and edit it
|
|
2416
2858
|
this.editedOp = {
|
|
2417
2859
|
id: this._nanoid(),
|
|
2418
2860
|
type: 0,
|
|
@@ -2421,228 +2863,455 @@ class SnapshotEditorComponent {
|
|
|
2421
2863
|
};
|
|
2422
2864
|
this.editedOpIndex = -1;
|
|
2423
2865
|
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Edit (a copy of) the operation at the specified index.
|
|
2868
|
+
* @param index The operation index.
|
|
2869
|
+
*/
|
|
2424
2870
|
editOperation(index) {
|
|
2425
2871
|
this.editedOpIndex = index;
|
|
2426
2872
|
this.editedOp = deepCopy(this.operations.value[index]);
|
|
2427
2873
|
}
|
|
2428
|
-
|
|
2874
|
+
/**
|
|
2875
|
+
* Close the currently edited operation.
|
|
2876
|
+
*/
|
|
2877
|
+
closeEditedOperation() {
|
|
2429
2878
|
this.editedOpIndex = -1;
|
|
2430
2879
|
this.editedOp = undefined;
|
|
2431
2880
|
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Handle the event fired by the operation editor to cancel
|
|
2883
|
+
* the current operation edits.
|
|
2884
|
+
*/
|
|
2432
2885
|
onOperationCancel() {
|
|
2433
|
-
this.
|
|
2886
|
+
this.closeEditedOperation();
|
|
2434
2887
|
}
|
|
2435
|
-
|
|
2888
|
+
/**
|
|
2889
|
+
* Handle the event fired by the operation editor to change
|
|
2890
|
+
* the currently edited operation, or add a new one if that
|
|
2891
|
+
* was a new operation.
|
|
2892
|
+
* @param op The changed operation.
|
|
2893
|
+
*/
|
|
2894
|
+
async onOperationChange(op) {
|
|
2436
2895
|
console.log('operation change');
|
|
2437
2896
|
const operations = [...this.operations.value];
|
|
2897
|
+
// replace or add the operation
|
|
2898
|
+
let i = this.editedOpIndex;
|
|
2438
2899
|
if (this.editedOpIndex > -1) {
|
|
2439
2900
|
operations.splice(this.editedOpIndex, 1, op);
|
|
2440
2901
|
}
|
|
2441
2902
|
else {
|
|
2442
2903
|
operations.push(op);
|
|
2904
|
+
i = operations.length - 1;
|
|
2443
2905
|
}
|
|
2906
|
+
// update the operations form control
|
|
2444
2907
|
this.operations.setValue(operations);
|
|
2445
2908
|
this.operations.markAsDirty();
|
|
2446
2909
|
this.operations.updateValueAndValidity();
|
|
2447
|
-
|
|
2910
|
+
// update the operation lists
|
|
2911
|
+
this.updateOpLists();
|
|
2912
|
+
// update the view data
|
|
2913
|
+
this.result = await this.runTo(i);
|
|
2914
|
+
// close the edited operation
|
|
2915
|
+
this.closeEditedOperation();
|
|
2448
2916
|
}
|
|
2917
|
+
/**
|
|
2918
|
+
* Delete the operation at the specified index.
|
|
2919
|
+
* @param index The index of the operation to delete.
|
|
2920
|
+
*/
|
|
2449
2921
|
deleteOperation(index) {
|
|
2450
2922
|
this._dialogService
|
|
2451
2923
|
.confirm('Confirm Deletion', `Delete operation "${this.operations.value[index].id}"?`)
|
|
2452
2924
|
.subscribe((yes) => {
|
|
2453
2925
|
if (yes) {
|
|
2926
|
+
// close the edited operation if it is the one being deleted
|
|
2927
|
+
if (this.editedOpIndex === index) {
|
|
2928
|
+
this.closeEditedOperation();
|
|
2929
|
+
}
|
|
2930
|
+
// reset the result operation ID if it is the one being deleted
|
|
2454
2931
|
if (this.resultOperationId === this.operations.value[index].id) {
|
|
2455
2932
|
this.resultOperationId = undefined;
|
|
2456
2933
|
}
|
|
2934
|
+
// delete the operation and update the form control
|
|
2457
2935
|
const operations = [...this.operations.value];
|
|
2458
2936
|
operations.splice(index, 1);
|
|
2459
2937
|
this.operations.setValue(operations);
|
|
2460
2938
|
this.operations.markAsDirty();
|
|
2461
2939
|
this.operations.updateValueAndValidity();
|
|
2940
|
+
// update the operation lists
|
|
2941
|
+
this.updateOpLists();
|
|
2942
|
+
// update the view data
|
|
2943
|
+
setTimeout(() => {
|
|
2944
|
+
this.runToLast();
|
|
2945
|
+
}, 0);
|
|
2462
2946
|
}
|
|
2463
2947
|
});
|
|
2464
2948
|
}
|
|
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
|
-
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Parse the operations from their text and append them to the current
|
|
2951
|
+
* snapshot operations.
|
|
2952
|
+
*/
|
|
2484
2953
|
parseOperations() {
|
|
2485
|
-
if (this.busy
|
|
2954
|
+
if (this.busy) {
|
|
2486
2955
|
return;
|
|
2487
2956
|
}
|
|
2488
|
-
this.
|
|
2489
|
-
.
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
this.parseError = error.message;
|
|
2505
|
-
},
|
|
2506
|
-
complete: () => {
|
|
2507
|
-
this.busy = false;
|
|
2508
|
-
},
|
|
2509
|
-
});
|
|
2957
|
+
const dialogRef = this._dialog.open(BatchOperationEditorComponent, {
|
|
2958
|
+
data: { preset: this.batchOps },
|
|
2959
|
+
});
|
|
2960
|
+
dialogRef.afterClosed().subscribe((batchOps) => {
|
|
2961
|
+
if (batchOps) {
|
|
2962
|
+
const operations = [...this.operations.value];
|
|
2963
|
+
operations.push(...batchOps);
|
|
2964
|
+
this.operations.setValue(operations);
|
|
2965
|
+
this.operations.markAsDirty();
|
|
2966
|
+
this.operations.updateValueAndValidity();
|
|
2967
|
+
// update the operation lists
|
|
2968
|
+
this.updateOpLists();
|
|
2969
|
+
// update the view data
|
|
2970
|
+
setTimeout(() => {
|
|
2971
|
+
this.runToLast();
|
|
2972
|
+
}, 0);
|
|
2510
2973
|
}
|
|
2511
2974
|
});
|
|
2512
2975
|
}
|
|
2976
|
+
removeAllOperations() {
|
|
2977
|
+
this.resultOperationId = undefined;
|
|
2978
|
+
this.closeEditedOperation();
|
|
2979
|
+
this.operations.reset();
|
|
2980
|
+
this.operations.markAsDirty();
|
|
2981
|
+
this.operations.updateValueAndValidity();
|
|
2982
|
+
this.opTags = [];
|
|
2983
|
+
this.opElementIds = [];
|
|
2984
|
+
this.setViewData();
|
|
2985
|
+
this.result = undefined;
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Remove all the operations.
|
|
2989
|
+
*/
|
|
2513
2990
|
clearOperations() {
|
|
2514
2991
|
this._dialogService
|
|
2515
2992
|
.confirm('Confirm', 'Remove all operations?')
|
|
2516
2993
|
.subscribe((yes) => {
|
|
2517
2994
|
if (yes) {
|
|
2518
|
-
this.
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2995
|
+
this.removeAllOperations();
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Parse all the id attributes from the received SVG code and
|
|
3001
|
+
* return them as an array.
|
|
3002
|
+
*
|
|
3003
|
+
* @param svg The SVG content to parse or undefined.
|
|
3004
|
+
* @returns Array of IDs found in the SVG content or undefined.
|
|
3005
|
+
*/
|
|
3006
|
+
parseSvgIds(svg) {
|
|
3007
|
+
if (!svg) {
|
|
3008
|
+
return undefined;
|
|
3009
|
+
}
|
|
3010
|
+
const regex = /<[^>]+\bid=(["'])(.*?)\1/g;
|
|
3011
|
+
const ids = [];
|
|
3012
|
+
let match;
|
|
3013
|
+
while ((match = regex.exec(svg)) !== null) {
|
|
3014
|
+
ids.push(match[2]);
|
|
3015
|
+
}
|
|
3016
|
+
return ids;
|
|
3017
|
+
}
|
|
3018
|
+
getTransparentIds(g) {
|
|
3019
|
+
if (!g) {
|
|
3020
|
+
return undefined;
|
|
3021
|
+
}
|
|
3022
|
+
return this.parseSvgIds(g)?.filter((id) => id.endsWith(SVG_TRANSP_SUFFIX));
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Run the operations up to the specified index (included).
|
|
3026
|
+
* This is called when:
|
|
3027
|
+
* - a preview is requested by the operation editor.
|
|
3028
|
+
* - the currently edited operation is saved.
|
|
3029
|
+
* - the user picks a step in the chain result view.
|
|
3030
|
+
* - runToLast is called, which happens when:
|
|
3031
|
+
* - setting the snapshot.
|
|
3032
|
+
* - parsing a batch of operations.
|
|
3033
|
+
* - deleting an operation.
|
|
3034
|
+
*
|
|
3035
|
+
* @param index The index of the operation to run to.
|
|
3036
|
+
* @param lastOperation The operation to use in place of the existing
|
|
3037
|
+
* operation in the snapshot at index. This is used when previewing
|
|
3038
|
+
* the edited operation from within the operation editor.
|
|
3039
|
+
*/
|
|
3040
|
+
runTo(index, lastOperation) {
|
|
3041
|
+
return new Promise((resolve, reject) => {
|
|
3042
|
+
if (this.busy) {
|
|
3043
|
+
return reject('Busy');
|
|
3044
|
+
}
|
|
3045
|
+
console.log('run to: ' + index);
|
|
3046
|
+
// get the snapshot (=text and ops) to run operations on
|
|
3047
|
+
const snapshot = this.getSnapshot();
|
|
3048
|
+
if (!snapshot.text) {
|
|
3049
|
+
return reject('No snapshot text');
|
|
3050
|
+
}
|
|
3051
|
+
// remove from the snapshot the operations past the specified index,
|
|
3052
|
+
// also replacing the last operation when this was received as a parameter
|
|
3053
|
+
snapshot.operations = [...this.operations.value];
|
|
3054
|
+
if (lastOperation) {
|
|
3055
|
+
snapshot.operations = snapshot.operations.slice(0, index);
|
|
3056
|
+
snapshot.operations.push(lastOperation);
|
|
3057
|
+
}
|
|
3058
|
+
else {
|
|
3059
|
+
snapshot.operations = snapshot.operations.slice(0, index + 1);
|
|
2523
3060
|
}
|
|
3061
|
+
// run the operations
|
|
3062
|
+
this.busy = true;
|
|
3063
|
+
this.initialStepIndex = index;
|
|
3064
|
+
this._api
|
|
3065
|
+
.runOperations(snapshot.text, snapshot.operations)
|
|
3066
|
+
.subscribe({
|
|
3067
|
+
next: (wrapper) => {
|
|
3068
|
+
// handle operation (non-fatal) error or result
|
|
3069
|
+
if (wrapper.error) {
|
|
3070
|
+
this._snackbar.open(wrapper.error, 'OK');
|
|
3071
|
+
reject(wrapper.error);
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
// extract the IDs from the last operation's diplomatics and filter
|
|
3075
|
+
// them so that only the ones ending with _t are kept. By convention,
|
|
3076
|
+
// all the IDs ending with this suffix are to be made invisible at
|
|
3077
|
+
// their first rendition (opacity=0). An animation will then make
|
|
3078
|
+
// them visible.
|
|
3079
|
+
const lastOp = snapshot.operations[snapshot.operations.length - 1];
|
|
3080
|
+
this._transparentIds = this.getTransparentIds(lastOp.diplomatics?.g);
|
|
3081
|
+
// update the view data
|
|
3082
|
+
this.setViewData(snapshot, lastOp.outputTag);
|
|
3083
|
+
// return the result
|
|
3084
|
+
resolve(wrapper.result);
|
|
3085
|
+
},
|
|
3086
|
+
error: (error) => {
|
|
3087
|
+
console.error(error);
|
|
3088
|
+
this._transparentIds = undefined;
|
|
3089
|
+
this._snackbar.open('Error running operations', 'OK');
|
|
3090
|
+
reject(error);
|
|
3091
|
+
},
|
|
3092
|
+
complete: () => {
|
|
3093
|
+
this.busy = false;
|
|
3094
|
+
},
|
|
3095
|
+
});
|
|
2524
3096
|
});
|
|
2525
3097
|
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Run the operations up to the last operation if any, updating the
|
|
3100
|
+
* execution result. The execution result is always the result from
|
|
3101
|
+
* all the operations, as users must be able to browse across its steps.
|
|
3102
|
+
*/
|
|
3103
|
+
async runToLast() {
|
|
3104
|
+
if (this.operations.value.length) {
|
|
3105
|
+
this.result = await this.runTo(this.operations.value.length - 1);
|
|
3106
|
+
}
|
|
3107
|
+
else {
|
|
3108
|
+
this.setViewData();
|
|
3109
|
+
this.result = undefined;
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
/**
|
|
3113
|
+
* Update the snapshot view by running the operations up to the
|
|
3114
|
+
* currently edited operation if any.
|
|
3115
|
+
*
|
|
3116
|
+
* @param operation The operation being previewed.
|
|
3117
|
+
*/
|
|
2526
3118
|
onOperationPreview(operation) {
|
|
2527
|
-
|
|
3119
|
+
// no multiple previews or previewing a new operation
|
|
3120
|
+
if (this._previewing || this.editedOpIndex < 0) {
|
|
2528
3121
|
return;
|
|
2529
3122
|
}
|
|
2530
3123
|
this._previewing = true;
|
|
2531
3124
|
setTimeout(() => {
|
|
2532
|
-
this.
|
|
3125
|
+
this.runTo(this.editedOpIndex, operation);
|
|
2533
3126
|
this._previewing = false;
|
|
2534
3127
|
}, 0);
|
|
2535
3128
|
}
|
|
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) {
|
|
3129
|
+
// #endregion
|
|
3130
|
+
/**
|
|
3131
|
+
* Build the snapshot at the specified execution step. This implies adding
|
|
3132
|
+
* the nodes introduced by the operations up to the specified step, and
|
|
3133
|
+
* updating the features of the nodes introduced by the specified step.
|
|
3134
|
+
* Also, the operations after the specified step are removed from the
|
|
3135
|
+
* snapshot.
|
|
3136
|
+
*
|
|
3137
|
+
* @param step The step to build the snapshot at.
|
|
3138
|
+
* @returns The snapshot at the specified step.
|
|
3139
|
+
*/
|
|
3140
|
+
buildSnapshotAtStep(step) {
|
|
3141
|
+
// no result, nothing to do
|
|
2567
3142
|
if (!this.result) {
|
|
2568
|
-
return;
|
|
3143
|
+
return null;
|
|
2569
3144
|
}
|
|
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.
|
|
3145
|
+
// get the currently edited snapshot
|
|
2578
3146
|
const snapshot = this.getSnapshot();
|
|
2579
|
-
|
|
3147
|
+
// find the max ID in snapshot.text (=original) nodes,
|
|
3148
|
+
// so that any ID greater than it belongs to new nodes
|
|
3149
|
+
const maxOriginalId = snapshot.text.reduce((max, n) => Math.max(max, n.id), 0);
|
|
3150
|
+
// get a copy of the nodes of the snapshot base text
|
|
3151
|
+
const snapNodes = deepCopy(snapshot.text);
|
|
3152
|
+
// get the nodes of the picked step
|
|
3153
|
+
const stepNodes = this.result.taggedNodes[step.outputTag];
|
|
3154
|
+
// update the snapshot text nodes copies with the picked step nodes,
|
|
3155
|
+
// by updating their features if existing, or adding new nodes if not.
|
|
2580
3156
|
for (const sn of stepNodes) {
|
|
2581
|
-
|
|
3157
|
+
// find the node in the original snapshot text
|
|
3158
|
+
const i = snapNodes.findIndex((n) => n.id === sn.id);
|
|
3159
|
+
// if found, update its features, else add it
|
|
2582
3160
|
if (i > -1) {
|
|
2583
|
-
|
|
3161
|
+
snapNodes[i].features = sn.features;
|
|
2584
3162
|
}
|
|
2585
3163
|
else {
|
|
2586
|
-
|
|
3164
|
+
// new node: we assume that it has a manually set position (x,y features)
|
|
3165
|
+
snapNodes.push(sn);
|
|
2587
3166
|
}
|
|
2588
3167
|
}
|
|
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
|
|
3168
|
+
// append nodes introduced *before* the picked step, if any
|
|
2593
3169
|
// (those introduced by the picked step have already been added)
|
|
2594
3170
|
const stepIndex = this.result.steps.indexOf(step);
|
|
2595
3171
|
for (let i = 0; i < stepIndex; i++) {
|
|
2596
3172
|
const stepNodes = this.result.taggedNodes[this.result.steps[i].outputTag];
|
|
2597
3173
|
for (const n of stepNodes) {
|
|
2598
|
-
if (n.id > maxOriginalId && !
|
|
2599
|
-
|
|
3174
|
+
if (n.id > maxOriginalId && !snapNodes.find((x) => x.id === n.id)) {
|
|
3175
|
+
snapNodes.push(n);
|
|
2600
3176
|
}
|
|
2601
3177
|
}
|
|
2602
3178
|
}
|
|
2603
|
-
snapshot
|
|
3179
|
+
// update the snapshot text nodes
|
|
3180
|
+
snapshot.text = snapNodes;
|
|
3181
|
+
// remove from the snapshot the operations after the picked step
|
|
2604
3182
|
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
2605
3183
|
if (i > -1) {
|
|
2606
3184
|
snapshot.operations = snapshot.operations.slice(0, i + 1);
|
|
2607
3185
|
}
|
|
2608
|
-
|
|
3186
|
+
return snapshot;
|
|
3187
|
+
}
|
|
3188
|
+
playTimeline(tl) {
|
|
3189
|
+
const shadowRoot = this.snapshotView?.nativeElement?.shadowRoot;
|
|
3190
|
+
if (tl && shadowRoot) {
|
|
3191
|
+
console.log('play timeline', tl);
|
|
3192
|
+
this._renderer?.playTimeline(tl, undefined, shadowRoot);
|
|
3193
|
+
}
|
|
3194
|
+
else {
|
|
3195
|
+
if (!this.snapshotView) {
|
|
3196
|
+
console.warn('no snapshotView for timeline');
|
|
3197
|
+
}
|
|
3198
|
+
else {
|
|
3199
|
+
console.warn('no shadowRoot for timeline');
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Handle the event fired by the chain result view to pick a step.
|
|
3205
|
+
*
|
|
3206
|
+
* @param step The step to pick.
|
|
3207
|
+
*/
|
|
3208
|
+
async onStepPick(step) {
|
|
3209
|
+
if (!this.result || this._stepPickFrozen) {
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
// get the currently edited snapshot
|
|
3213
|
+
const snapshot = this.buildSnapshotAtStep(step);
|
|
3214
|
+
// update result operation ID
|
|
3215
|
+
this.resultOperationId = step.operation.id;
|
|
3216
|
+
// update transparent IDs
|
|
3217
|
+
this._transparentIds = this.getTransparentIds(step.operation.diplomatics?.g);
|
|
3218
|
+
// update view data
|
|
3219
|
+
this.setViewData(snapshot, step.outputTag);
|
|
3220
|
+
// play animation if any
|
|
3221
|
+
const tl = this.timelines.value.find((t) => t.tag === step.outputTag);
|
|
3222
|
+
if (tl) {
|
|
3223
|
+
this._stepPickFrozen = true;
|
|
3224
|
+
// find the index of the picked step in the snapshot operations
|
|
3225
|
+
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
3226
|
+
// run the operations up to the picked step as we need to hide
|
|
3227
|
+
// those visuals to be revealed by the animation
|
|
3228
|
+
await this.runTo(i);
|
|
3229
|
+
this._stepPickFrozen = false;
|
|
3230
|
+
// play the timeline
|
|
3231
|
+
setTimeout(() => {
|
|
3232
|
+
this.playTimeline(tl);
|
|
3233
|
+
}, 0);
|
|
3234
|
+
}
|
|
2609
3235
|
}
|
|
3236
|
+
/**
|
|
3237
|
+
* Handle the event fired by a visual in the snapshot view.
|
|
3238
|
+
*
|
|
3239
|
+
* @param event The event.
|
|
3240
|
+
*/
|
|
2610
3241
|
onVisualEvent(event) {
|
|
2611
3242
|
const d = event.detail;
|
|
2612
3243
|
if (d.event.type === 'mouseover') {
|
|
2613
3244
|
const visual = d.source;
|
|
2614
3245
|
const sb = [];
|
|
3246
|
+
// id (type)
|
|
2615
3247
|
sb.push('#' + visual.id);
|
|
2616
3248
|
sb.push(` (${visual.type})`);
|
|
3249
|
+
// : label and features
|
|
2617
3250
|
if (visual.data?.label) {
|
|
2618
3251
|
sb.push(': ');
|
|
2619
3252
|
sb.push(visual.data.label);
|
|
2620
3253
|
}
|
|
2621
3254
|
if (visual.data?.features?.length) {
|
|
3255
|
+
sb.push(' ');
|
|
2622
3256
|
sb.push(visual.data.features
|
|
2623
3257
|
.map((f) => `${f.name}=${f.value}`)
|
|
2624
3258
|
.join('\n'));
|
|
2625
3259
|
}
|
|
3260
|
+
// (@x,y width×height)
|
|
3261
|
+
sb.push(` (@${visual.x.toFixed(1)},${visual.y.toFixed(1)} ` +
|
|
3262
|
+
`◻${visual.width.toFixed(1)}×${visual.height.toFixed(1)})`);
|
|
2626
3263
|
this.visualInfo = sb.join('');
|
|
2627
3264
|
}
|
|
2628
3265
|
}
|
|
3266
|
+
/**
|
|
3267
|
+
* Handle the change of line heights by updating the form control.
|
|
3268
|
+
*
|
|
3269
|
+
* @param heights The line heights.
|
|
3270
|
+
*/
|
|
2629
3271
|
onHeightsChange(heights) {
|
|
2630
3272
|
this.lnHeights.setValue(heights || null);
|
|
2631
3273
|
this.lnHeights.markAsDirty();
|
|
2632
3274
|
this.lnHeights.updateValueAndValidity();
|
|
2633
3275
|
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Handle the change of timelines by updating the form control.
|
|
3278
|
+
*
|
|
3279
|
+
* @param timelines The timelines.
|
|
3280
|
+
*/
|
|
3281
|
+
onTimelinesChange(timelines) {
|
|
3282
|
+
this.timelines.setValue(timelines);
|
|
3283
|
+
this.timelines.markAsDirty();
|
|
3284
|
+
this.timelines.updateValueAndValidity();
|
|
3285
|
+
}
|
|
3286
|
+
/**
|
|
3287
|
+
* Emit the cancel event for this snapshot edit.
|
|
3288
|
+
*/
|
|
2634
3289
|
close() {
|
|
2635
3290
|
this.snapshotCancel.emit();
|
|
2636
3291
|
}
|
|
3292
|
+
/**
|
|
3293
|
+
* Handle the render event from the snapshot view to get a reference
|
|
3294
|
+
* to the renderer.
|
|
3295
|
+
*
|
|
3296
|
+
* @param event The rendition event.
|
|
3297
|
+
*/
|
|
2637
3298
|
onSnapshotRender(event) {
|
|
2638
3299
|
this._renderer = event.detail.renderer;
|
|
2639
3300
|
}
|
|
3301
|
+
/**
|
|
3302
|
+
* Toggle rulers in the snapshot view.
|
|
3303
|
+
*/
|
|
2640
3304
|
toggleRulers() {
|
|
2641
3305
|
if (!this._renderer) {
|
|
2642
3306
|
return;
|
|
2643
3307
|
}
|
|
2644
3308
|
this.rulers = this._renderer.toggleRulers();
|
|
2645
3309
|
}
|
|
3310
|
+
/**
|
|
3311
|
+
* Get a snapshot from the form data.
|
|
3312
|
+
*
|
|
3313
|
+
* @returns New snapshot object.
|
|
3314
|
+
*/
|
|
2646
3315
|
getSnapshot() {
|
|
2647
3316
|
const snapshot = {
|
|
2648
3317
|
size: {
|
|
@@ -2665,7 +3334,6 @@ class SnapshotEditorComponent {
|
|
|
2665
3334
|
},
|
|
2666
3335
|
operations: [...this.operations.value],
|
|
2667
3336
|
opStyle: this.opStyle.value || undefined,
|
|
2668
|
-
timelines: {}, // TODO
|
|
2669
3337
|
};
|
|
2670
3338
|
// image
|
|
2671
3339
|
if (this.imageUrl.value) {
|
|
@@ -2697,11 +3365,10 @@ class SnapshotEditorComponent {
|
|
|
2697
3365
|
}
|
|
2698
3366
|
return snapshot;
|
|
2699
3367
|
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
}
|
|
3368
|
+
/**
|
|
3369
|
+
* Get a snapshot from the form data and emit it
|
|
3370
|
+
* in the snapshot change event.
|
|
3371
|
+
*/
|
|
2705
3372
|
save() {
|
|
2706
3373
|
if (this.form.invalid || this.noSave) {
|
|
2707
3374
|
return;
|
|
@@ -2709,10 +3376,10 @@ class SnapshotEditorComponent {
|
|
|
2709
3376
|
this._snapshot = this.getSnapshot();
|
|
2710
3377
|
this.snapshotChange.emit(this._snapshot);
|
|
2711
3378
|
}
|
|
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"] }] }); }
|
|
3379
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SnapshotEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$1.MatDialog }, { token: i2$2.DialogService }, { token: i5$2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3380
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", 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 <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\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 <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\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 </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 </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 <!-- snapshot view -->\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 class=\"button-row\">\r\n <!-- toggle ruler -->\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 <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Run all operations\"\r\n [disabled]=\"!operations.value.length\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>play_circle</mat-icon>\r\n </button>\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}#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#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: i8$1.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: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.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: i13.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: i14.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i14.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i14.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
3381
|
}
|
|
2715
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.
|
|
3382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SnapshotEditorComponent, decorators: [{
|
|
2716
3383
|
type: Component,
|
|
2717
3384
|
args: [{ selector: 'gve-snapshot-editor', standalone: true, imports: [
|
|
2718
3385
|
CommonModule,
|
|
@@ -2736,13 +3403,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2736
3403
|
ChainResultViewComponent,
|
|
2737
3404
|
LnHeightsEditorComponent,
|
|
2738
3405
|
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:
|
|
3406
|
+
], 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 <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\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 <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\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 </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 </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 <!-- snapshot view -->\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 class=\"button-row\">\r\n <!-- toggle ruler -->\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 <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Run all operations\"\r\n [disabled]=\"!operations.value.length\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>play_circle</mat-icon>\r\n </button>\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}#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#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
|
|
3407
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$1.MatDialog }, { type: i2$2.DialogService }, { type: i5$2.MatSnackBar }], propDecorators: { snapshotView: [{
|
|
3408
|
+
type: ViewChild,
|
|
3409
|
+
args: ['snapshotView', { static: false }]
|
|
3410
|
+
}], snapshot: [{
|
|
2741
3411
|
type: Input
|
|
2742
3412
|
}], batchOps: [{
|
|
2743
3413
|
type: Input
|
|
2744
3414
|
}], noSave: [{
|
|
2745
3415
|
type: Input
|
|
3416
|
+
}], debug: [{
|
|
3417
|
+
type: Input
|
|
2746
3418
|
}], snapshotChange: [{
|
|
2747
3419
|
type: Output
|
|
2748
3420
|
}], snapshotCancel: [{
|
|
@@ -2757,5 +3429,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
|
|
|
2757
3429
|
* Generated bundle index. Do not edit.
|
|
2758
3430
|
*/
|
|
2759
3431
|
|
|
2760
|
-
export { AnimationTimelineComponent, AnimationTweenComponent,
|
|
3432
|
+
export { AnimationTimelineComponent, AnimationTweenComponent, BaseTextCharComponent, BaseTextEditorComponent, BaseTextViewComponent, BatchOperationEditorComponent, ChainOperationEditorComponent, ChainResultViewComponent, FeatureEditorComponent, FeatureSetEditorComponent, FeatureSetViewComponent, GveApiService, LnHeightsEditorComponent, OperationSourceEditorComponent, SettingsService, SimpleTreeComponent, SnapshotEditorComponent, StepsMapComponent };
|
|
2761
3433
|
//# sourceMappingURL=myrmidon-gve-core.mjs.map
|