@myrmidon/gve-core 1.0.0 → 3.0.2
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 +32 -2
- package/fesm2022/myrmidon-gve-core.mjs +1277 -927
- package/fesm2022/myrmidon-gve-core.mjs.map +1 -1
- package/lib/components/animation-timeline/animation-timeline.component.d.ts +7 -10
- package/lib/components/animation-timeline-set/animation-timeline-set.component.d.ts +9 -12
- package/lib/components/animation-tween/animation-tween.component.d.ts +6 -8
- package/lib/components/base-text-char/base-text-char.component.d.ts +3 -6
- package/lib/components/base-text-editor/base-text-editor.component.d.ts +6 -8
- package/lib/components/base-text-view/base-text-view.component.d.ts +15 -12
- package/lib/components/batch-operation-editor/batch-operation-editor.component.d.ts +5 -7
- package/lib/components/chain-operation-editor/chain-operation-editor.component.d.ts +14 -15
- package/lib/components/chain-result-view/chain-result-view.component.d.ts +8 -11
- package/lib/components/chain-view/chain-view.component.d.ts +42 -0
- package/lib/components/feature-editor/feature-editor.component.d.ts +8 -12
- package/lib/components/feature-set-editor/feature-set-editor.component.d.ts +11 -13
- package/lib/components/feature-set-view/feature-set-view.component.d.ts +5 -7
- package/lib/components/ln-heights-editor/ln-heights-editor.component.d.ts +5 -8
- package/lib/components/operation-source-editor/operation-source-editor.component.d.ts +8 -11
- package/lib/components/snapshot-editor/snapshot-editor.component.d.ts +46 -35
- package/lib/components/snapshot-text-editor/snapshot-text-editor.component.d.ts +37 -0
- package/lib/components/steps-map/steps-map.component.d.ts +9 -9
- package/lib/services/gve-api.service.d.ts +48 -4
- package/lib/services/gve-graphviz.service.d.ts +20 -0
- package/lib/services/settings.service.d.ts +40 -5
- package/package.json +5 -5
- package/public-api.d.ts +3 -1
- package/lib/components/simple-tree/simple-tree.component.d.ts +0 -35
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { model, input, output, effect, Component, ViewChild, Injectable, Optional, Inject, computed, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import { Validators, ReactiveFormsModule, FormControl, FormsModule } from '@angular/forms';
|
|
4
|
+
import { Validators, ReactiveFormsModule, FormControl, FormGroup, 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';
|
|
@@ -18,33 +18,37 @@ 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 i16 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
|
-
import * as i2$
|
|
26
|
-
import {
|
|
27
|
-
import { debounceTime, distinctUntilChanged, catchError } from 'rxjs';
|
|
25
|
+
import * as i2$2 from '@myrmidon/ngx-tools';
|
|
26
|
+
import { NgxToolsValidators, ColorToContrastPipe, FlatLookupPipe, SafeHtmlPipe, deepCopy } from '@myrmidon/ngx-tools';
|
|
27
|
+
import { debounceTime, distinctUntilChanged, catchError, BehaviorSubject } from 'rxjs';
|
|
28
28
|
import * as i9 from '@angular/material/core';
|
|
29
29
|
import { MatRippleModule } from '@angular/material/core';
|
|
30
|
-
import {
|
|
31
|
-
import * as i2$
|
|
32
|
-
import { NgMatToolsModule } from '@myrmidon/ng-mat-tools';
|
|
30
|
+
import { FeatureSetPolicy, SnapshotViewService, OperationType, DEFAULT_SVG_BASE_TEXT_OPTIONS } from '@myrmidon/gve-snapshot-view';
|
|
31
|
+
import * as i2$1 from '@myrmidon/ngx-mat-tools';
|
|
33
32
|
import * as i3$1 from '@angular/material/dialog';
|
|
34
33
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
35
34
|
import * as i1$1 from '@angular/common/http';
|
|
36
35
|
import { filter, debounceTime as debounceTime$1 } from 'rxjs/operators';
|
|
37
36
|
import * as i2$3 from '@angular/cdk/clipboard';
|
|
38
37
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
|
39
|
-
import * as
|
|
38
|
+
import * as i6$1 from '@angular/material/badge';
|
|
39
|
+
import { MatBadgeModule } from '@angular/material/badge';
|
|
40
|
+
import * as i17 from '@cisstech/nge/monaco';
|
|
40
41
|
import { NgeMonacoModule } from '@cisstech/nge/monaco';
|
|
41
42
|
import { customAlphabet } from 'nanoid';
|
|
43
|
+
import { VizComponent } from '@myrmidon/ngx-viz';
|
|
44
|
+
import * as i4$2 from '@angular/material/snack-bar';
|
|
45
|
+
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
42
46
|
import * as i8$1 from '@angular/material/button-toggle';
|
|
43
47
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
44
|
-
import * as
|
|
48
|
+
import * as i14 from '@angular/material/progress-bar';
|
|
45
49
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
46
|
-
import * as
|
|
47
|
-
import {
|
|
50
|
+
import * as i15 from '@angular/material/slider';
|
|
51
|
+
import { MatSliderModule } from '@angular/material/slider';
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
54
|
* 🔑 `gve-animation-tween`
|
|
@@ -58,28 +62,24 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
|
58
62
|
* - 🔥 `tweenCancel` (`void`): emitted when the user cancels the edit.
|
|
59
63
|
*/
|
|
60
64
|
class AnimationTweenComponent {
|
|
61
|
-
/**
|
|
62
|
-
* The tween to edit.
|
|
63
|
-
*/
|
|
64
|
-
get tween() {
|
|
65
|
-
return this._tween;
|
|
66
|
-
}
|
|
67
|
-
set tween(value) {
|
|
68
|
-
if (this._tween === value) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
this._tween = value || undefined;
|
|
72
|
-
this.updateForm(this._tween);
|
|
73
|
-
}
|
|
74
65
|
constructor(formBuilder) {
|
|
66
|
+
/**
|
|
67
|
+
* The tween to edit.
|
|
68
|
+
*/
|
|
69
|
+
this.tween = model();
|
|
70
|
+
/**
|
|
71
|
+
* The IDs of the elements that can be selected by the tween.
|
|
72
|
+
* This list is used to allow the user to select an element from a dropdown.
|
|
73
|
+
*/
|
|
74
|
+
this.elementIds = input();
|
|
75
75
|
/**
|
|
76
76
|
* Emitted when the tween is changed.
|
|
77
77
|
*/
|
|
78
|
-
this.tweenChange =
|
|
78
|
+
this.tweenChange = output();
|
|
79
79
|
/**
|
|
80
80
|
* Emitted when the user cancels the edit.
|
|
81
81
|
*/
|
|
82
|
-
this.tweenCancel =
|
|
82
|
+
this.tweenCancel = output();
|
|
83
83
|
this.types = ['to', 'from', 'fromTo', 'set'];
|
|
84
84
|
this.label = formBuilder.control('', {
|
|
85
85
|
nonNullable: true,
|
|
@@ -107,6 +107,9 @@ class AnimationTweenComponent {
|
|
|
107
107
|
position: this.position,
|
|
108
108
|
});
|
|
109
109
|
this.elementId = formBuilder.control(null);
|
|
110
|
+
effect(() => {
|
|
111
|
+
this.updateForm(this.tween());
|
|
112
|
+
});
|
|
110
113
|
}
|
|
111
114
|
ngOnInit() {
|
|
112
115
|
// set selector when elementId changes
|
|
@@ -178,13 +181,13 @@ class AnimationTweenComponent {
|
|
|
178
181
|
if (!this.form.valid) {
|
|
179
182
|
return;
|
|
180
183
|
}
|
|
181
|
-
this.
|
|
182
|
-
this.tweenChange.emit(this.
|
|
184
|
+
this.tween.set(this.getTween());
|
|
185
|
+
this.tweenChange.emit(this.tween());
|
|
183
186
|
}
|
|
184
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
185
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
187
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTweenComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
188
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: AnimationTweenComponent, isStandalone: true, selector: "gve-animation-tween", inputs: { tween: { classPropertyName: "tween", publicName: "tween", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tween: "tweenChange", 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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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"] }] }); }
|
|
186
189
|
}
|
|
187
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
190
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTweenComponent, decorators: [{
|
|
188
191
|
type: Component,
|
|
189
192
|
args: [{ selector: 'gve-animation-tween', imports: [
|
|
190
193
|
CommonModule,
|
|
@@ -197,16 +200,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
197
200
|
MatSelectModule,
|
|
198
201
|
MatTabsModule,
|
|
199
202
|
MatTooltipModule,
|
|
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"] }]
|
|
201
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
202
|
-
type: Input
|
|
203
|
-
}], elementIds: [{
|
|
204
|
-
type: Input
|
|
205
|
-
}], tweenChange: [{
|
|
206
|
-
type: Output
|
|
207
|
-
}], tweenCancel: [{
|
|
208
|
-
type: Output
|
|
209
|
-
}] } });
|
|
203
|
+
], 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"] }]
|
|
204
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
210
205
|
|
|
211
206
|
/**
|
|
212
207
|
* 🔑 `gve-animation-timeline`
|
|
@@ -223,29 +218,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
223
218
|
* - 🔥 `timelineCancel` (`void`): emitted when the timeline editing is canceled.
|
|
224
219
|
*/
|
|
225
220
|
class AnimationTimelineComponent {
|
|
226
|
-
/**
|
|
227
|
-
* The animation timeline to edit.
|
|
228
|
-
*/
|
|
229
|
-
get timeline() {
|
|
230
|
-
return this._timeline;
|
|
231
|
-
}
|
|
232
|
-
set timeline(value) {
|
|
233
|
-
this._timeline = value || undefined;
|
|
234
|
-
this.updateForm(this._timeline);
|
|
235
|
-
}
|
|
236
221
|
constructor(formBuilder) {
|
|
222
|
+
/**
|
|
223
|
+
* The animation timeline to edit.
|
|
224
|
+
*/
|
|
225
|
+
this.timeline = model();
|
|
226
|
+
/**
|
|
227
|
+
* The IDs of the elements that can be selected by the tween.
|
|
228
|
+
* This list is used to allow the user to select an element from a dropdown.
|
|
229
|
+
*/
|
|
230
|
+
this.elementIds = input();
|
|
237
231
|
/**
|
|
238
232
|
* The tags that can be used by the timeline.
|
|
239
233
|
*/
|
|
240
|
-
this.tags = [];
|
|
234
|
+
this.tags = input([]);
|
|
241
235
|
/**
|
|
242
236
|
* Emitted when the timeline is changed.
|
|
243
237
|
*/
|
|
244
|
-
this.timelineChange =
|
|
238
|
+
this.timelineChange = output();
|
|
245
239
|
/**
|
|
246
240
|
* Emitted when the timeline editing is canceled.
|
|
247
241
|
*/
|
|
248
|
-
this.timelineCancel =
|
|
242
|
+
this.timelineCancel = output();
|
|
249
243
|
this.editedTweenIndex = -1;
|
|
250
244
|
this.tag = formBuilder.control('', {
|
|
251
245
|
nonNullable: true,
|
|
@@ -253,7 +247,7 @@ class AnimationTimelineComponent {
|
|
|
253
247
|
});
|
|
254
248
|
this.tweens = formBuilder.control([], {
|
|
255
249
|
nonNullable: true,
|
|
256
|
-
validators:
|
|
250
|
+
validators: NgxToolsValidators.strictMinLengthValidator(1),
|
|
257
251
|
});
|
|
258
252
|
this.vars = formBuilder.control(null, this.jsonValidator);
|
|
259
253
|
this.form = formBuilder.group({
|
|
@@ -261,6 +255,10 @@ class AnimationTimelineComponent {
|
|
|
261
255
|
tweens: this.tweens,
|
|
262
256
|
vars: this.vars,
|
|
263
257
|
});
|
|
258
|
+
// when the timeline changes, update the form
|
|
259
|
+
effect(() => {
|
|
260
|
+
this.updateForm(this.timeline());
|
|
261
|
+
});
|
|
264
262
|
}
|
|
265
263
|
jsonValidator(control) {
|
|
266
264
|
if (!control.value) {
|
|
@@ -354,14 +352,14 @@ class AnimationTimelineComponent {
|
|
|
354
352
|
if (this.form.invalid) {
|
|
355
353
|
return;
|
|
356
354
|
}
|
|
357
|
-
this.
|
|
358
|
-
this.timelineChange.emit(this.
|
|
355
|
+
this.timeline.set(this.getTimeline());
|
|
356
|
+
this.timelineChange.emit(this.timeline());
|
|
359
357
|
this.form.markAsPristine();
|
|
360
358
|
}
|
|
361
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
362
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", 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", "disabledInteractive"], 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"] }] }); }
|
|
359
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
360
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: { classPropertyName: "timeline", publicName: "timeline", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeline: "timelineChange", 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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "tweenCancel"] }] }); }
|
|
363
361
|
}
|
|
364
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
362
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
|
|
365
363
|
type: Component,
|
|
366
364
|
args: [{ selector: 'gve-animation-timeline', imports: [
|
|
367
365
|
CommonModule,
|
|
@@ -375,20 +373,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
375
373
|
MatSelectModule,
|
|
376
374
|
MatTabsModule,
|
|
377
375
|
MatTooltipModule,
|
|
378
|
-
NgToolsModule,
|
|
379
376
|
AnimationTweenComponent,
|
|
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"] }]
|
|
381
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
382
|
-
type: Input
|
|
383
|
-
}], elementIds: [{
|
|
384
|
-
type: Input
|
|
385
|
-
}], tags: [{
|
|
386
|
-
type: Input
|
|
387
|
-
}], timelineChange: [{
|
|
388
|
-
type: Output
|
|
389
|
-
}], timelineCancel: [{
|
|
390
|
-
type: Output
|
|
391
|
-
}] } });
|
|
377
|
+
], 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"] }]
|
|
378
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
392
379
|
|
|
393
380
|
/**
|
|
394
381
|
* 🔑 `gve-base-text-char`
|
|
@@ -401,34 +388,25 @@ class BaseTextCharComponent {
|
|
|
401
388
|
this.defaultColor = '#D8D8D8';
|
|
402
389
|
this.defaultBorderColor = '#D8D8D8';
|
|
403
390
|
this.defaultEmSize = 1.5;
|
|
391
|
+
/**
|
|
392
|
+
* The character to display.
|
|
393
|
+
*/
|
|
394
|
+
this.char = input();
|
|
404
395
|
/**
|
|
405
396
|
* Emitted when the character is clicked.
|
|
406
397
|
*/
|
|
407
|
-
this.charPick =
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* The character to display.
|
|
411
|
-
*/
|
|
412
|
-
get char() {
|
|
413
|
-
return this._char;
|
|
414
|
-
}
|
|
415
|
-
set char(value) {
|
|
416
|
-
this._char = value || undefined;
|
|
398
|
+
this.charPick = output();
|
|
417
399
|
}
|
|
418
400
|
onCharClick(event) {
|
|
419
|
-
this.charPick.emit({ char: this.
|
|
401
|
+
this.charPick.emit({ char: this.char(), event });
|
|
420
402
|
}
|
|
421
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
422
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
403
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextCharComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
404
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: BaseTextCharComponent, isStandalone: true, selector: "gve-base-text-char", inputs: { char: { classPropertyName: "char", publicName: "char", isSignal: true, isRequired: false, transformFunction: null } }, 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: "pipe", type: ColorToContrastPipe, name: "colorToContrast" }] }); }
|
|
423
405
|
}
|
|
424
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
406
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextCharComponent, decorators: [{
|
|
425
407
|
type: Component,
|
|
426
|
-
args: [{ selector: 'gve-base-text-char', imports: [MatRippleModule,
|
|
427
|
-
}]
|
|
428
|
-
type: Input
|
|
429
|
-
}], charPick: [{
|
|
430
|
-
type: Output
|
|
431
|
-
}] } });
|
|
408
|
+
args: [{ selector: 'gve-base-text-char', imports: [MatRippleModule, ColorToContrastPipe], 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"] }]
|
|
409
|
+
}] });
|
|
432
410
|
|
|
433
411
|
/**
|
|
434
412
|
* 🔑 `gve-base-text-view`
|
|
@@ -449,79 +427,66 @@ class BaseTextViewComponent {
|
|
|
449
427
|
/**
|
|
450
428
|
* The default color for the text.
|
|
451
429
|
*/
|
|
452
|
-
this.defaultColor = '#DBDBDB';
|
|
430
|
+
this.defaultColor = input('#DBDBDB');
|
|
453
431
|
/**
|
|
454
432
|
* The default border color for the text.
|
|
455
433
|
*/
|
|
456
|
-
this.defaultBorderColor = '#DBDBDB';
|
|
434
|
+
this.defaultBorderColor = input('#DBDBDB');
|
|
457
435
|
/**
|
|
458
436
|
* The color for the selected text.
|
|
459
437
|
*/
|
|
460
|
-
this.selectionColor = '#3E92CC';
|
|
438
|
+
this.selectionColor = input('#3E92CC');
|
|
461
439
|
/**
|
|
462
440
|
* True if line numbers should be displayed next to each line.
|
|
463
441
|
*/
|
|
464
|
-
this.hasLineNumber = false;
|
|
442
|
+
this.hasLineNumber = input(false);
|
|
443
|
+
/**
|
|
444
|
+
* The text to display.
|
|
445
|
+
*/
|
|
446
|
+
this.text = input([]);
|
|
447
|
+
/**
|
|
448
|
+
* An optional callback to get the color of a character node.
|
|
449
|
+
* Return null to use the default color.
|
|
450
|
+
*/
|
|
451
|
+
this.borderColorCallback = input();
|
|
465
452
|
/**
|
|
466
453
|
* Emitted when a character is picked.
|
|
467
454
|
*/
|
|
468
|
-
this.charPick =
|
|
455
|
+
this.charPick = output();
|
|
469
456
|
/**
|
|
470
457
|
* Emitted when a range is picked. This is preceded by a character pick event.
|
|
471
458
|
* The range is defined by the starting character and the number of characters.
|
|
472
459
|
* The range is inclusive.
|
|
473
460
|
*/
|
|
474
|
-
this.rangePick =
|
|
461
|
+
this.rangePick = output();
|
|
475
462
|
this.lines = [];
|
|
463
|
+
// build lines when text or border color callback change
|
|
464
|
+
effect(() => {
|
|
465
|
+
this.buildLines(this.text(), this.borderColorCallback());
|
|
466
|
+
});
|
|
476
467
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
*/
|
|
480
|
-
get text() {
|
|
481
|
-
return this._text;
|
|
482
|
-
}
|
|
483
|
-
set text(value) {
|
|
484
|
-
this._text = value || undefined;
|
|
485
|
-
this.buildLines();
|
|
486
|
-
}
|
|
487
|
-
buildLines() {
|
|
488
|
-
if (!this._text) {
|
|
468
|
+
buildLines(text, borderColorCallback) {
|
|
469
|
+
if (!text) {
|
|
489
470
|
this.lines = [];
|
|
490
471
|
return;
|
|
491
472
|
}
|
|
492
473
|
const newLines = [];
|
|
493
474
|
newLines.push([]);
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
for (let i = 0; i < this._text.length; i++) {
|
|
513
|
-
const char = {
|
|
514
|
-
id: i + 1,
|
|
515
|
-
value: this._text[i],
|
|
516
|
-
label: SnapshotViewService.translateSpecialChar(this._text[i]),
|
|
517
|
-
color: this.defaultColor,
|
|
518
|
-
borderColor: this.defaultBorderColor,
|
|
519
|
-
emSize: 1.5,
|
|
520
|
-
};
|
|
521
|
-
newLines[newLines.length - 1].push(char);
|
|
522
|
-
if (char.value === '\n') {
|
|
523
|
-
newLines.push([]);
|
|
524
|
-
}
|
|
475
|
+
const nodes = text;
|
|
476
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
477
|
+
const char = {
|
|
478
|
+
id: nodes[i].id,
|
|
479
|
+
value: nodes[i].data,
|
|
480
|
+
label: nodes[i].label,
|
|
481
|
+
color: this.defaultColor(),
|
|
482
|
+
borderColor: borderColorCallback
|
|
483
|
+
? borderColorCallback(nodes[i]) || this.defaultBorderColor()
|
|
484
|
+
: this.defaultBorderColor(),
|
|
485
|
+
emSize: 1.5,
|
|
486
|
+
};
|
|
487
|
+
newLines[newLines.length - 1].push(char);
|
|
488
|
+
if (char.value === '\n') {
|
|
489
|
+
newLines.push([]);
|
|
525
490
|
}
|
|
526
491
|
}
|
|
527
492
|
this.lines = newLines;
|
|
@@ -529,8 +494,8 @@ class BaseTextViewComponent {
|
|
|
529
494
|
resetColors() {
|
|
530
495
|
for (const line of this.lines) {
|
|
531
496
|
for (const c of line) {
|
|
532
|
-
c.color = this.defaultColor;
|
|
533
|
-
c.borderColor = this.defaultBorderColor;
|
|
497
|
+
c.color = this.defaultColor();
|
|
498
|
+
c.borderColor = this.defaultBorderColor();
|
|
534
499
|
}
|
|
535
500
|
}
|
|
536
501
|
}
|
|
@@ -544,8 +509,8 @@ class BaseTextViewComponent {
|
|
|
544
509
|
for (const line of this.lines) {
|
|
545
510
|
for (const c of line) {
|
|
546
511
|
if (c.id >= minId && c.id <= maxId) {
|
|
547
|
-
c.color = this.selectionColor;
|
|
548
|
-
c.borderColor = this.selectionColor;
|
|
512
|
+
c.color = this.selectionColor();
|
|
513
|
+
c.borderColor = this.selectionColor();
|
|
549
514
|
}
|
|
550
515
|
}
|
|
551
516
|
}
|
|
@@ -555,34 +520,20 @@ class BaseTextViewComponent {
|
|
|
555
520
|
}
|
|
556
521
|
else {
|
|
557
522
|
// select current char
|
|
558
|
-
event.char.color = this.selectionColor;
|
|
559
|
-
event.char.borderColor = this.selectionColor;
|
|
523
|
+
event.char.color = this.selectionColor();
|
|
524
|
+
event.char.borderColor = this.selectionColor();
|
|
560
525
|
this.charPick.emit(event);
|
|
561
526
|
this.rangePick.emit({ at: event.char.id, run: 1 });
|
|
562
527
|
}
|
|
563
528
|
this._lastSelectedChar = event.char;
|
|
564
529
|
}
|
|
565
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
566
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
530
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
531
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: BaseTextViewComponent, isStandalone: true, selector: "gve-base-text-view", inputs: { defaultColor: { classPropertyName: "defaultColor", publicName: "defaultColor", isSignal: true, isRequired: false, transformFunction: null }, defaultBorderColor: { classPropertyName: "defaultBorderColor", publicName: "defaultBorderColor", isSignal: true, isRequired: false, transformFunction: null }, selectionColor: { classPropertyName: "selectionColor", publicName: "selectionColor", isSignal: true, isRequired: false, transformFunction: null }, hasLineNumber: { classPropertyName: "hasLineNumber", publicName: "hasLineNumber", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, borderColorCallback: { classPropertyName: "borderColorCallback", publicName: "borderColorCallback", isSignal: true, isRequired: false, transformFunction: null } }, 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"] }] }); }
|
|
567
532
|
}
|
|
568
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
533
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextViewComponent, decorators: [{
|
|
569
534
|
type: Component,
|
|
570
|
-
args: [{ selector: 'gve-base-text-view', 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"] }]
|
|
571
|
-
}],
|
|
572
|
-
type: Input
|
|
573
|
-
}], defaultBorderColor: [{
|
|
574
|
-
type: Input
|
|
575
|
-
}], selectionColor: [{
|
|
576
|
-
type: Input
|
|
577
|
-
}], hasLineNumber: [{
|
|
578
|
-
type: Input
|
|
579
|
-
}], text: [{
|
|
580
|
-
type: Input
|
|
581
|
-
}], charPick: [{
|
|
582
|
-
type: Output
|
|
583
|
-
}], rangePick: [{
|
|
584
|
-
type: Output
|
|
585
|
-
}] } });
|
|
535
|
+
args: [{ selector: 'gve-base-text-view', 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"] }]
|
|
536
|
+
}], ctorParameters: () => [] });
|
|
586
537
|
|
|
587
538
|
/**
|
|
588
539
|
* 🔑 `gve-feature-editor`
|
|
@@ -604,48 +555,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
604
555
|
* - 🔥 `featureCancel`: emitted when the user cancels the feature editing.
|
|
605
556
|
*/
|
|
606
557
|
class FeatureEditorComponent {
|
|
607
|
-
/**
|
|
608
|
-
* The feature values map. When specified and the user selects a feature
|
|
609
|
-
* name present in the map keys, the corresponding values will be used
|
|
610
|
-
* to populate the value selection.
|
|
611
|
-
*/
|
|
612
|
-
get featValues() {
|
|
613
|
-
return this._map;
|
|
614
|
-
}
|
|
615
|
-
set featValues(value) {
|
|
616
|
-
this._map = value || undefined;
|
|
617
|
-
this.nameIds = this.getLabeledIdsFor(this.name.value);
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* The feature to edit.
|
|
621
|
-
*/
|
|
622
|
-
get feature() {
|
|
623
|
-
return this._feature;
|
|
624
|
-
}
|
|
625
|
-
set feature(value) {
|
|
626
|
-
if (this._feature === value) {
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
this._feature = value || undefined;
|
|
630
|
-
this.updateForm(this._feature);
|
|
631
|
-
setTimeout(() => {
|
|
632
|
-
this.nameControl?.nativeElement?.focus();
|
|
633
|
-
}, 500);
|
|
634
|
-
}
|
|
635
558
|
constructor(formBuilder) {
|
|
559
|
+
/**
|
|
560
|
+
* The list of feature names to display in the name selection.
|
|
561
|
+
* This is used when you have a closed list of features.
|
|
562
|
+
*/
|
|
563
|
+
this.featNames = input();
|
|
564
|
+
/**
|
|
565
|
+
* The feature values map. When specified and the user selects a feature
|
|
566
|
+
* name present in the map keys, the corresponding values will be used
|
|
567
|
+
* to populate the value selection.
|
|
568
|
+
*/
|
|
569
|
+
this.featValues = input();
|
|
570
|
+
/**
|
|
571
|
+
* The feature to edit.
|
|
572
|
+
*/
|
|
573
|
+
this.feature = model();
|
|
636
574
|
/**
|
|
637
575
|
* True if the feature is a variant operation feature, which has
|
|
638
576
|
* additional properties like negation, global, and short-lived.
|
|
639
577
|
*/
|
|
640
|
-
this.isVar = false;
|
|
578
|
+
this.isVar = input(false);
|
|
641
579
|
/**
|
|
642
580
|
* Event emitted when the user cancels the feature editing.
|
|
643
581
|
*/
|
|
644
|
-
this.featureCancel =
|
|
582
|
+
this.featureCancel = output();
|
|
645
583
|
/**
|
|
646
584
|
* Event emitted when the user saves the edited feature.
|
|
647
585
|
*/
|
|
648
|
-
this.featureChange =
|
|
586
|
+
this.featureChange = output();
|
|
649
587
|
this.name = formBuilder.control('', {
|
|
650
588
|
validators: [Validators.required, Validators.maxLength(50)],
|
|
651
589
|
nonNullable: true,
|
|
@@ -668,26 +606,35 @@ class FeatureEditorComponent {
|
|
|
668
606
|
isGlobal: this.isGlobal,
|
|
669
607
|
isShortLived: this.isShortLived,
|
|
670
608
|
});
|
|
609
|
+
// when feature changes, update the form
|
|
610
|
+
effect(() => {
|
|
611
|
+
this.updateForm(this.feature());
|
|
612
|
+
setTimeout(() => {
|
|
613
|
+
this.nameControl?.nativeElement?.focus();
|
|
614
|
+
this.nameIds = this.getLabeledIdsFor(this.name.value, this.featValues());
|
|
615
|
+
}, 500);
|
|
616
|
+
});
|
|
671
617
|
}
|
|
672
618
|
ngOnInit() {
|
|
673
619
|
// whenever the name changes, update the value selection
|
|
674
620
|
this._sub = this.name.valueChanges
|
|
675
621
|
.pipe(debounceTime(300), distinctUntilChanged())
|
|
676
622
|
.subscribe((name) => {
|
|
677
|
-
if (!this._frozen && this.featNames?.length) {
|
|
623
|
+
if (!this._frozen && this.featNames()?.length) {
|
|
678
624
|
this.value.reset();
|
|
679
|
-
this.nameIds = this.getLabeledIdsFor(name);
|
|
625
|
+
this.nameIds = this.getLabeledIdsFor(name, this.featValues());
|
|
680
626
|
}
|
|
681
627
|
});
|
|
682
628
|
}
|
|
683
629
|
ngOnDestroy() {
|
|
684
630
|
this._sub?.unsubscribe();
|
|
685
631
|
}
|
|
686
|
-
getLabeledIdsFor(id) {
|
|
632
|
+
getLabeledIdsFor(id, featValues) {
|
|
687
633
|
if (!id) {
|
|
688
634
|
return undefined;
|
|
689
635
|
}
|
|
690
|
-
|
|
636
|
+
console.log('getLabeledIdsFor', id, featValues);
|
|
637
|
+
return featValues ? featValues[id] : undefined;
|
|
691
638
|
}
|
|
692
639
|
updateForm(feature) {
|
|
693
640
|
if (!feature) {
|
|
@@ -711,7 +658,7 @@ class FeatureEditorComponent {
|
|
|
711
658
|
if (!this.form.valid) {
|
|
712
659
|
return;
|
|
713
660
|
}
|
|
714
|
-
this.feature
|
|
661
|
+
this.feature.set(this.isVar()
|
|
715
662
|
? {
|
|
716
663
|
name: this.name.value.trim(),
|
|
717
664
|
value: this.value.value.trim(),
|
|
@@ -724,13 +671,13 @@ class FeatureEditorComponent {
|
|
|
724
671
|
name: this.name.value.trim(),
|
|
725
672
|
value: this.value.value.trim(),
|
|
726
673
|
setPolicy: this.setPolicy.value,
|
|
727
|
-
};
|
|
728
|
-
this.featureChange.emit(this.feature);
|
|
674
|
+
});
|
|
675
|
+
this.featureChange.emit(this.feature());
|
|
729
676
|
}
|
|
730
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
731
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
677
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
678
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: FeatureEditorComponent, isStandalone: true, selector: "gve-feature-editor", inputs: { featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, feature: { classPropertyName: "feature", publicName: "feature", isSignal: true, isRequired: false, transformFunction: null }, isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { feature: "featureChange", 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", "aria-expanded", "aria-controls", "aria-owns", "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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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"] }] }); }
|
|
732
679
|
}
|
|
733
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
680
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureEditorComponent, decorators: [{
|
|
734
681
|
type: Component,
|
|
735
682
|
args: [{ selector: 'gve-feature-editor', imports: [
|
|
736
683
|
CommonModule,
|
|
@@ -742,22 +689,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
742
689
|
MatInputModule,
|
|
743
690
|
MatSelectModule,
|
|
744
691
|
MatTooltipModule,
|
|
745
|
-
], 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"] }]
|
|
692
|
+
], 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"] }]
|
|
746
693
|
}], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { nameControl: [{
|
|
747
694
|
type: ViewChild,
|
|
748
695
|
args: ['nameCtl']
|
|
749
|
-
}], featNames: [{
|
|
750
|
-
type: Input
|
|
751
|
-
}], featValues: [{
|
|
752
|
-
type: Input
|
|
753
|
-
}], feature: [{
|
|
754
|
-
type: Input
|
|
755
|
-
}], isVar: [{
|
|
756
|
-
type: Input
|
|
757
|
-
}], featureCancel: [{
|
|
758
|
-
type: Output
|
|
759
|
-
}], featureChange: [{
|
|
760
|
-
type: Output
|
|
761
696
|
}] } });
|
|
762
697
|
|
|
763
698
|
/**
|
|
@@ -776,58 +711,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
776
711
|
* - ▶️ `filterThreshold` (`number`): the threshold at which the features filter
|
|
777
712
|
* should become visible. If set to 0, the filter is always visible; if set to -1,
|
|
778
713
|
* it is always invisible; otherwise, it gets visible when the number of features
|
|
779
|
-
* is greater than the threshold. Default is 5.
|
|
714
|
+
* is equal to or greater than the threshold. Default is 5.
|
|
780
715
|
* - ▶️ `features` (`Feature[]`): the features to edit.
|
|
781
716
|
* - ▶️ `isVar`: true if the feature is a variant operation feature, which
|
|
782
717
|
* has additional properties like negation, global, and short-lived.
|
|
783
718
|
* - 🔥 `featuresChange` (`Feature[]`): emitted when features have changed.
|
|
784
719
|
*/
|
|
785
720
|
class FeatureSetEditorComponent {
|
|
786
|
-
/**
|
|
787
|
-
* The features to edit.
|
|
788
|
-
*/
|
|
789
|
-
get features() {
|
|
790
|
-
return this._features;
|
|
791
|
-
}
|
|
792
|
-
set features(value) {
|
|
793
|
-
if (this._features === value) {
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
this._features = value;
|
|
797
|
-
this.applyFeatureFilter(this.filter.value);
|
|
798
|
-
}
|
|
799
721
|
constructor(formBuilder) {
|
|
800
722
|
this.POLICIES = ['M', 'S', 'SF'];
|
|
801
723
|
/**
|
|
802
724
|
* True if the features are variable features.
|
|
803
725
|
*/
|
|
804
|
-
this.isVar = false;
|
|
726
|
+
this.isVar = input(false);
|
|
727
|
+
/**
|
|
728
|
+
* The list of feature names to display in the name selection.
|
|
729
|
+
* This is used when you have a closed list of features.
|
|
730
|
+
*/
|
|
731
|
+
this.featNames = input();
|
|
732
|
+
/**
|
|
733
|
+
* The feature values map. When specified and the user selects a feature
|
|
734
|
+
* name present in the map keys, the corresponding values will be used
|
|
735
|
+
* to populate the value selection.
|
|
736
|
+
*/
|
|
737
|
+
this.featValues = input();
|
|
805
738
|
/**
|
|
806
739
|
* The threshold at which the features filter should become visible.
|
|
807
740
|
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
808
741
|
* invisible; otherwise, it gets visible when the number of features
|
|
809
|
-
* is greater than the threshold. Default is 5.
|
|
742
|
+
* is greater than or equal to the threshold. Default is 5.
|
|
810
743
|
*/
|
|
811
|
-
this.filterThreshold = 5;
|
|
744
|
+
this.filterThreshold = input(5);
|
|
745
|
+
/**
|
|
746
|
+
* The features to edit.
|
|
747
|
+
*/
|
|
748
|
+
this.features = model([]);
|
|
812
749
|
/**
|
|
813
750
|
* Emitted when the features change.
|
|
814
751
|
*/
|
|
815
|
-
this.featuresChange =
|
|
816
|
-
this._features = [];
|
|
752
|
+
this.featuresChange = output();
|
|
817
753
|
this.filteredFeatures = [];
|
|
818
754
|
this.filter = formBuilder.control(null);
|
|
819
755
|
this._editedFeatureIndex = -1;
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
return !name || feature.name.toLowerCase().includes(name.toLowerCase());
|
|
756
|
+
// when features change, apply the filter
|
|
757
|
+
effect(() => {
|
|
758
|
+
this.applyFeatureFilter(this.features(), this.filter.value);
|
|
824
759
|
});
|
|
825
760
|
}
|
|
761
|
+
applyFeatureFilter(features, name) {
|
|
762
|
+
this.filteredFeatures =
|
|
763
|
+
features.filter((feature) => {
|
|
764
|
+
return !name || feature.name.toLowerCase().includes(name.toLowerCase());
|
|
765
|
+
}) || [];
|
|
766
|
+
}
|
|
826
767
|
ngOnInit() {
|
|
768
|
+
// when the filter changes, apply the filter
|
|
827
769
|
this._sub = this.filter.valueChanges
|
|
828
770
|
.pipe(distinctUntilChanged(), debounceTime(300))
|
|
829
771
|
.subscribe((value) => {
|
|
830
|
-
this.applyFeatureFilter(value);
|
|
772
|
+
this.applyFeatureFilter(this.features(), value);
|
|
831
773
|
});
|
|
832
774
|
}
|
|
833
775
|
ngOnDestroy() {
|
|
@@ -843,20 +785,23 @@ class FeatureSetEditorComponent {
|
|
|
843
785
|
}
|
|
844
786
|
editFeature(feature) {
|
|
845
787
|
this.editedFeature = feature;
|
|
846
|
-
this._editedFeatureIndex = this.features.indexOf(feature);
|
|
788
|
+
this._editedFeatureIndex = this.features().indexOf(feature);
|
|
847
789
|
}
|
|
848
790
|
deleteFeature(feature) {
|
|
849
|
-
const index = this.features.indexOf(feature);
|
|
791
|
+
const index = this.features().indexOf(feature);
|
|
850
792
|
if (index < 0) {
|
|
851
793
|
return;
|
|
852
794
|
}
|
|
853
|
-
const features = [...this.features];
|
|
795
|
+
const features = [...this.features()];
|
|
854
796
|
features.splice(index, 1);
|
|
855
|
-
this.features
|
|
856
|
-
this.featuresChange.emit(this.features);
|
|
797
|
+
this.features.set(features);
|
|
798
|
+
this.featuresChange.emit(this.features());
|
|
857
799
|
}
|
|
858
800
|
onFeatureChange(feature) {
|
|
859
|
-
|
|
801
|
+
if (!feature) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const features = [...this.features()];
|
|
860
805
|
for (let i = features.length - 1; i >= 0; i--) {
|
|
861
806
|
if (features[i].name === feature.name) {
|
|
862
807
|
if (feature.setPolicy === FeatureSetPolicy.single ||
|
|
@@ -871,19 +816,18 @@ class FeatureSetEditorComponent {
|
|
|
871
816
|
else {
|
|
872
817
|
features.splice(this._editedFeatureIndex, 1, feature);
|
|
873
818
|
}
|
|
874
|
-
this.
|
|
875
|
-
this.
|
|
876
|
-
this.
|
|
877
|
-
this.featuresChange.emit(this.features);
|
|
819
|
+
this.onFeatureCancel();
|
|
820
|
+
this.features.set(features);
|
|
821
|
+
this.featuresChange.emit(this.features());
|
|
878
822
|
}
|
|
879
823
|
onFeatureCancel() {
|
|
880
824
|
this.editedFeature = undefined;
|
|
881
825
|
this._editedFeatureIndex = -1;
|
|
882
826
|
}
|
|
883
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
884
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
827
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureSetEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
828
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: FeatureSetEditorComponent, isStandalone: true, selector: "gve-feature-set-editor", inputs: { isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { features: "featuresChange", 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", "disabledInteractive"], 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: "pipe", type: FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: FeatureEditorComponent, selector: "gve-feature-editor", inputs: ["featNames", "featValues", "feature", "isVar"], outputs: ["featureChange", "featureCancel"] }] }); }
|
|
885
829
|
}
|
|
886
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
830
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureSetEditorComponent, decorators: [{
|
|
887
831
|
type: Component,
|
|
888
832
|
args: [{ selector: 'gve-feature-set-editor', imports: [
|
|
889
833
|
CommonModule,
|
|
@@ -895,22 +839,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
895
839
|
MatInputModule,
|
|
896
840
|
MatSelectModule,
|
|
897
841
|
MatTooltipModule,
|
|
898
|
-
|
|
842
|
+
FlatLookupPipe,
|
|
899
843
|
FeatureEditorComponent,
|
|
900
|
-
], template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold === 0 || features.length
|
|
901
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
902
|
-
type: Input
|
|
903
|
-
}], featNames: [{
|
|
904
|
-
type: Input
|
|
905
|
-
}], featValues: [{
|
|
906
|
-
type: Input
|
|
907
|
-
}], filterThreshold: [{
|
|
908
|
-
type: Input
|
|
909
|
-
}], features: [{
|
|
910
|
-
type: Input
|
|
911
|
-
}], featuresChange: [{
|
|
912
|
-
type: Output
|
|
913
|
-
}] } });
|
|
844
|
+
], 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"] }]
|
|
845
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
914
846
|
|
|
915
847
|
/**
|
|
916
848
|
* 🔑 `gve-base-text-editor`
|
|
@@ -922,34 +854,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
922
854
|
* Used by the `gve-snapshot-editor` component.
|
|
923
855
|
*/
|
|
924
856
|
class BaseTextEditorComponent {
|
|
925
|
-
/**
|
|
926
|
-
* The text to edit. In input this can be a string, an array of `CharNode`
|
|
927
|
-
* objects. In output this will be an array of `CharNode` objects.
|
|
928
|
-
*/
|
|
929
|
-
get text() {
|
|
930
|
-
return this._text;
|
|
931
|
-
}
|
|
932
|
-
set text(value) {
|
|
933
|
-
this.resetSelectedChar();
|
|
934
|
-
if (this._text === value) {
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
if (!value) {
|
|
938
|
-
this._text = [];
|
|
939
|
-
}
|
|
940
|
-
else {
|
|
941
|
-
this._text = Array.isArray(value)
|
|
942
|
-
? value
|
|
943
|
-
: SnapshotViewService.stringToBaseChars(value);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
857
|
constructor(formBuilder, _dialogService) {
|
|
947
858
|
this._dialogService = _dialogService;
|
|
948
|
-
|
|
859
|
+
/**
|
|
860
|
+
* The text to edit. In input this can be a string, an array of `CharNode`
|
|
861
|
+
* objects, or undefined. In output this will be an array of `CharNode`
|
|
862
|
+
* objects.
|
|
863
|
+
*/
|
|
864
|
+
this.text = input.required({
|
|
865
|
+
transform: (value) => {
|
|
866
|
+
if (value === undefined) {
|
|
867
|
+
return undefined;
|
|
868
|
+
}
|
|
869
|
+
if (Array.isArray(value)) {
|
|
870
|
+
return value;
|
|
871
|
+
}
|
|
872
|
+
if (typeof value === 'string') {
|
|
873
|
+
return SnapshotViewService.stringToBaseChars(value);
|
|
874
|
+
}
|
|
875
|
+
return undefined;
|
|
876
|
+
},
|
|
877
|
+
});
|
|
949
878
|
/**
|
|
950
879
|
* Emitted for the edited text as an array of `CharNode`'s whenever it changes.
|
|
951
880
|
*/
|
|
952
|
-
this.textChange =
|
|
881
|
+
this.textChange = output();
|
|
953
882
|
this.userText = formBuilder.control('', {
|
|
954
883
|
nonNullable: true,
|
|
955
884
|
validators: [Validators.required, Validators.maxLength(10000)],
|
|
@@ -957,18 +886,31 @@ class BaseTextEditorComponent {
|
|
|
957
886
|
this.form = formBuilder.group({
|
|
958
887
|
userText: this.userText,
|
|
959
888
|
});
|
|
889
|
+
effect(() => {
|
|
890
|
+
this.resetSelectedChar();
|
|
891
|
+
if (!this.text()) {
|
|
892
|
+
this.userText.reset();
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
this.userText.setValue(this.text()
|
|
896
|
+
.map((c) => c.label)
|
|
897
|
+
.join(''));
|
|
898
|
+
this.userText.markAsPristine();
|
|
899
|
+
this.userText.updateValueAndValidity();
|
|
900
|
+
}
|
|
901
|
+
});
|
|
960
902
|
}
|
|
961
903
|
resetSelectedChar() {
|
|
962
904
|
this.selectedChar = undefined;
|
|
963
905
|
this.selectedCharLabel = undefined;
|
|
964
906
|
}
|
|
965
907
|
onSelectedChar(event) {
|
|
966
|
-
this.selectedChar = this.
|
|
908
|
+
this.selectedChar = this.text().find((c) => c.id === event.char.id);
|
|
967
909
|
this.selectedCharLabel = event.char.label;
|
|
968
910
|
}
|
|
969
911
|
onFeaturesChange(features) {
|
|
970
912
|
this.selectedChar.features = features;
|
|
971
|
-
this.textChange.emit(this.
|
|
913
|
+
this.textChange.emit(this.text());
|
|
972
914
|
}
|
|
973
915
|
onRangePick(range) {
|
|
974
916
|
this.textRange = range;
|
|
@@ -981,15 +923,14 @@ class BaseTextEditorComponent {
|
|
|
981
923
|
.confirm('Confirm', 'Reset text?')
|
|
982
924
|
.subscribe((yes) => {
|
|
983
925
|
if (yes) {
|
|
984
|
-
this.
|
|
985
|
-
this.textChange.emit(this._text);
|
|
926
|
+
this.textChange.emit(SnapshotViewService.stringToBaseChars(this.userText.value));
|
|
986
927
|
}
|
|
987
928
|
});
|
|
988
929
|
}
|
|
989
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
990
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
930
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
931
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: BaseTextEditorComponent, isStandalone: true, selector: "gve-base-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null } }, 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", "disabledInteractive"], 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", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }] }); }
|
|
991
932
|
}
|
|
992
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
933
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BaseTextEditorComponent, decorators: [{
|
|
993
934
|
type: Component,
|
|
994
935
|
args: [{ selector: 'gve-base-text-editor', imports: [
|
|
995
936
|
CommonModule,
|
|
@@ -1001,12 +942,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1001
942
|
MatTooltipModule,
|
|
1002
943
|
BaseTextViewComponent,
|
|
1003
944
|
FeatureSetEditorComponent,
|
|
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"] }]
|
|
1005
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$
|
|
1006
|
-
type: Input
|
|
1007
|
-
}], textChange: [{
|
|
1008
|
-
type: Output
|
|
1009
|
-
}] } });
|
|
945
|
+
], 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"] }]
|
|
946
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$1.DialogService }] });
|
|
1010
947
|
|
|
1011
948
|
/**
|
|
1012
949
|
* Service to interact with the GVE API.
|
|
@@ -1058,15 +995,32 @@ class GveApiService {
|
|
|
1058
995
|
})
|
|
1059
996
|
.pipe(catchError(this._error.handleError));
|
|
1060
997
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
998
|
+
/**
|
|
999
|
+
* Get the chain corresponding to the operations run on the
|
|
1000
|
+
* specified text. This is used for diagnostic or demonstration
|
|
1001
|
+
* purposes.
|
|
1002
|
+
*
|
|
1003
|
+
* @param text The base text.
|
|
1004
|
+
* @param operations The operations to run.
|
|
1005
|
+
* @returns Result wrapper.
|
|
1006
|
+
*/
|
|
1007
|
+
getChain(text, operations) {
|
|
1008
|
+
return this._http
|
|
1009
|
+
.post(`${this._env.get('apiUrl')}text/operations/run/chain`, {
|
|
1010
|
+
text,
|
|
1011
|
+
operations,
|
|
1012
|
+
})
|
|
1013
|
+
.pipe(catchError(this._error.handleError));
|
|
1014
|
+
}
|
|
1015
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveApiService, deps: [{ token: i1$1.HttpClient }, { token: i2$2.ErrorService }, { token: i2$2.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1016
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
|
|
1063
1017
|
}
|
|
1064
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
1018
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveApiService, decorators: [{
|
|
1065
1019
|
type: Injectable,
|
|
1066
1020
|
args: [{
|
|
1067
1021
|
providedIn: 'root',
|
|
1068
1022
|
}]
|
|
1069
|
-
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2$
|
|
1023
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2$2.ErrorService }, { type: i2$2.EnvService }] });
|
|
1070
1024
|
|
|
1071
1025
|
/**
|
|
1072
1026
|
* 🔑 `gve-batch-operation-editor`
|
|
@@ -1078,30 +1032,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1078
1032
|
* - 🔥 `operationsChange` (`CharChainOperation[]`): event emitted when operations change.
|
|
1079
1033
|
*/
|
|
1080
1034
|
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
1035
|
constructor(formBuilder, _api,
|
|
1096
1036
|
// this component can be used as a dialog
|
|
1097
1037
|
dialogRef, data) {
|
|
1098
1038
|
this._api = _api;
|
|
1099
1039
|
this.dialogRef = dialogRef;
|
|
1100
1040
|
this.data = data;
|
|
1041
|
+
/**
|
|
1042
|
+
* The preset text to parse if any. Usually you start with a blank
|
|
1043
|
+
* text, but sometimes you might want to pre-set it.
|
|
1044
|
+
*/
|
|
1045
|
+
this.preset = input();
|
|
1101
1046
|
/**
|
|
1102
1047
|
* Emitted when operations change.
|
|
1103
1048
|
*/
|
|
1104
|
-
this.operationsChange =
|
|
1049
|
+
this.operationsChange = output();
|
|
1105
1050
|
this.text = formBuilder.control(null, [
|
|
1106
1051
|
Validators.required,
|
|
1107
1052
|
Validators.maxLength(2000),
|
|
@@ -1109,24 +1054,30 @@ class BatchOperationEditorComponent {
|
|
|
1109
1054
|
this.form = formBuilder.group({
|
|
1110
1055
|
inputOps: this.text,
|
|
1111
1056
|
});
|
|
1057
|
+
effect(() => {
|
|
1058
|
+
this.parseOperations(this.preset() || undefined);
|
|
1059
|
+
});
|
|
1112
1060
|
}
|
|
1113
1061
|
ngOnInit() {
|
|
1114
1062
|
if (this.data?.payload?.preset) {
|
|
1115
|
-
this.
|
|
1116
|
-
this.parseOperations();
|
|
1063
|
+
this.parseOperations(this.data?.payload?.preset);
|
|
1117
1064
|
}
|
|
1118
1065
|
}
|
|
1119
|
-
parseOperations() {
|
|
1120
|
-
if (this.busy || !
|
|
1066
|
+
parseOperations(text) {
|
|
1067
|
+
if (this.busy || !text) {
|
|
1121
1068
|
return;
|
|
1122
1069
|
}
|
|
1123
1070
|
this.busy = true;
|
|
1124
1071
|
this.parseError = undefined;
|
|
1125
|
-
this._api.parseOperations(
|
|
1072
|
+
this._api.parseOperations(text).subscribe({
|
|
1126
1073
|
next: (wrapper) => {
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1074
|
+
if (wrapper.error) {
|
|
1075
|
+
this.parseError = wrapper.error;
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
this.operationsChange.emit(wrapper.result || []);
|
|
1079
|
+
if (this.dialogRef) {
|
|
1080
|
+
this.dialogRef.close(wrapper.result);
|
|
1130
1081
|
}
|
|
1131
1082
|
},
|
|
1132
1083
|
error: (error) => {
|
|
@@ -1140,10 +1091,10 @@ class BatchOperationEditorComponent {
|
|
|
1140
1091
|
close() {
|
|
1141
1092
|
this.dialogRef?.close();
|
|
1142
1093
|
}
|
|
1143
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
1144
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
1094
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", 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 }); }
|
|
1095
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: BatchOperationEditorComponent, isStandalone: true, selector: "gve-batch-operation-editor", inputs: { preset: { classPropertyName: "preset", publicName: "preset", isSignal: true, isRequired: false, transformFunction: null } }, 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(text.value!)\"\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", "disabledInteractive"], exportAs: ["matInput"] }] }); }
|
|
1145
1096
|
}
|
|
1146
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
1097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: BatchOperationEditorComponent, decorators: [{
|
|
1147
1098
|
type: Component,
|
|
1148
1099
|
args: [{ selector: 'gve-batch-operation-editor', imports: [
|
|
1149
1100
|
ReactiveFormsModule,
|
|
@@ -1151,7 +1102,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1151
1102
|
MatFormFieldModule,
|
|
1152
1103
|
MatIconModule,
|
|
1153
1104
|
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"] }]
|
|
1105
|
+
], 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(text.value!)\"\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
1106
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$1.MatDialogRef, decorators: [{
|
|
1156
1107
|
type: Optional
|
|
1157
1108
|
}] }, { type: undefined, decorators: [{
|
|
@@ -1159,11 +1110,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1159
1110
|
}, {
|
|
1160
1111
|
type: Inject,
|
|
1161
1112
|
args: [MAT_DIALOG_DATA]
|
|
1162
|
-
}] }]
|
|
1163
|
-
type: Input
|
|
1164
|
-
}], operationsChange: [{
|
|
1165
|
-
type: Output
|
|
1166
|
-
}] } });
|
|
1113
|
+
}] }] });
|
|
1167
1114
|
|
|
1168
1115
|
/**
|
|
1169
1116
|
* 🔑 `gve-operation-source-editor`
|
|
@@ -1182,28 +1129,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1182
1129
|
* edit is canceled.
|
|
1183
1130
|
*/
|
|
1184
1131
|
class OperationSourceEditorComponent {
|
|
1185
|
-
/**
|
|
1186
|
-
* The source to edit.
|
|
1187
|
-
*/
|
|
1188
|
-
get source() {
|
|
1189
|
-
return this._source;
|
|
1190
|
-
}
|
|
1191
|
-
set source(value) {
|
|
1192
|
-
if (this._source === value) {
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
|
-
this._source = value || undefined;
|
|
1196
|
-
this.updateForm(this._source);
|
|
1197
|
-
}
|
|
1198
1132
|
constructor(formBuilder) {
|
|
1133
|
+
/**
|
|
1134
|
+
* The source to edit.
|
|
1135
|
+
*/
|
|
1136
|
+
this.source = model();
|
|
1137
|
+
/**
|
|
1138
|
+
* The list of source IDs when it's a closed set.
|
|
1139
|
+
*/
|
|
1140
|
+
this.ids = input();
|
|
1141
|
+
/**
|
|
1142
|
+
* The list of source types when it's a closed set.
|
|
1143
|
+
*/
|
|
1144
|
+
this.types = input();
|
|
1199
1145
|
/**
|
|
1200
1146
|
* The event emitted when the source changes.
|
|
1201
1147
|
*/
|
|
1202
|
-
this.sourceChange =
|
|
1148
|
+
this.sourceChange = output();
|
|
1203
1149
|
/**
|
|
1204
1150
|
* The event emitted when the edit is canceled.
|
|
1205
1151
|
*/
|
|
1206
|
-
this.sourceCancel =
|
|
1152
|
+
this.sourceCancel = output();
|
|
1207
1153
|
this.id = new FormControl('', {
|
|
1208
1154
|
validators: [Validators.required, Validators.maxLength(50)],
|
|
1209
1155
|
nonNullable: true,
|
|
@@ -1224,6 +1170,10 @@ class OperationSourceEditorComponent {
|
|
|
1224
1170
|
rank: this.rank,
|
|
1225
1171
|
note: this.note,
|
|
1226
1172
|
});
|
|
1173
|
+
// when source changes, update form
|
|
1174
|
+
effect(() => {
|
|
1175
|
+
this.updateForm(this.source());
|
|
1176
|
+
});
|
|
1227
1177
|
}
|
|
1228
1178
|
updateForm(source) {
|
|
1229
1179
|
if (!source) {
|
|
@@ -1243,18 +1193,18 @@ class OperationSourceEditorComponent {
|
|
|
1243
1193
|
if (this.form.invalid) {
|
|
1244
1194
|
return;
|
|
1245
1195
|
}
|
|
1246
|
-
this.
|
|
1196
|
+
this.source.set({
|
|
1247
1197
|
id: this.id.value,
|
|
1248
1198
|
type: this.type.value,
|
|
1249
1199
|
rank: this.rank.value,
|
|
1250
1200
|
note: this.note.value || undefined,
|
|
1251
|
-
};
|
|
1252
|
-
this.sourceChange.emit(this.
|
|
1201
|
+
});
|
|
1202
|
+
this.sourceChange.emit(this.source());
|
|
1253
1203
|
}
|
|
1254
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
1255
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
1204
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: OperationSourceEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1205
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: OperationSourceEditorComponent, isStandalone: true, selector: "gve-operation-source-editor", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, ids: { classPropertyName: "ids", publicName: "ids", isSignal: true, isRequired: false, transformFunction: null }, types: { classPropertyName: "types", publicName: "types", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { source: "sourceChange", 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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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"] }] }); }
|
|
1256
1206
|
}
|
|
1257
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
1207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: OperationSourceEditorComponent, decorators: [{
|
|
1258
1208
|
type: Component,
|
|
1259
1209
|
args: [{ selector: 'gve-operation-source-editor', imports: [
|
|
1260
1210
|
CommonModule,
|
|
@@ -1265,18 +1215,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1265
1215
|
MatInputModule,
|
|
1266
1216
|
MatSelectModule,
|
|
1267
1217
|
MatTooltipModule,
|
|
1268
|
-
], 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"] }]
|
|
1269
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
1270
|
-
type: Input
|
|
1271
|
-
}], ids: [{
|
|
1272
|
-
type: Input
|
|
1273
|
-
}], types: [{
|
|
1274
|
-
type: Input
|
|
1275
|
-
}], sourceChange: [{
|
|
1276
|
-
type: Output
|
|
1277
|
-
}], sourceCancel: [{
|
|
1278
|
-
type: Output
|
|
1279
|
-
}] } });
|
|
1218
|
+
], 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"] }]
|
|
1219
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
1280
1220
|
|
|
1281
1221
|
/**
|
|
1282
1222
|
* Validators for SVG.
|
|
@@ -1313,12 +1253,22 @@ class SvgValidators {
|
|
|
1313
1253
|
}
|
|
1314
1254
|
|
|
1315
1255
|
/**
|
|
1316
|
-
* Service to store and retrieve dynamic settings.
|
|
1317
|
-
* be persisted in the local storage.
|
|
1256
|
+
* Service to store and retrieve dynamic settings.
|
|
1318
1257
|
*/
|
|
1319
1258
|
class SettingsService {
|
|
1320
1259
|
constructor() {
|
|
1321
1260
|
this._cache = new Map();
|
|
1261
|
+
this._subject = new BehaviorSubject(null);
|
|
1262
|
+
/**
|
|
1263
|
+
* True to persist the settings in the local storage by default.
|
|
1264
|
+
*/
|
|
1265
|
+
this.persistDefault = false;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Observable emitting the key of the setting that has changed.
|
|
1269
|
+
*/
|
|
1270
|
+
get settingChange$() {
|
|
1271
|
+
return this._subject.asObservable();
|
|
1322
1272
|
}
|
|
1323
1273
|
/**
|
|
1324
1274
|
* Scopes the key by prefixing it with the keyPrefix if any.
|
|
@@ -1329,6 +1279,9 @@ class SettingsService {
|
|
|
1329
1279
|
getScopedKey(key) {
|
|
1330
1280
|
return this.keyPrefix ? this.keyPrefix + key : key;
|
|
1331
1281
|
}
|
|
1282
|
+
isPersist(persist) {
|
|
1283
|
+
return persist === undefined ? this.persistDefault : persist;
|
|
1284
|
+
}
|
|
1332
1285
|
/**
|
|
1333
1286
|
* Sets a setting's value.
|
|
1334
1287
|
*
|
|
@@ -1336,11 +1289,33 @@ class SettingsService {
|
|
|
1336
1289
|
* @param value The value.
|
|
1337
1290
|
* @param persist True to persist this setting in local storage.
|
|
1338
1291
|
*/
|
|
1339
|
-
set(key, value, persist
|
|
1292
|
+
set(key, value, persist) {
|
|
1340
1293
|
key = this.getScopedKey(key);
|
|
1341
1294
|
this._cache.set(key, value);
|
|
1342
|
-
if (persist && value) {
|
|
1343
|
-
|
|
1295
|
+
if (this.isPersist(persist) && value !== undefined) {
|
|
1296
|
+
try {
|
|
1297
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1298
|
+
}
|
|
1299
|
+
catch (e) {
|
|
1300
|
+
console.error(`Error saving key "${key}" to localStorage`, e);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
this._subject.next(key);
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Set multiple settings at once from the specified settings object.
|
|
1307
|
+
*
|
|
1308
|
+
* @param settings The settings to set.
|
|
1309
|
+
* @param persist True to persist these settings in local storage.
|
|
1310
|
+
*/
|
|
1311
|
+
setFrom(settings, persist) {
|
|
1312
|
+
if (!settings) {
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
for (const key in settings) {
|
|
1316
|
+
if (settings.hasOwnProperty(key)) {
|
|
1317
|
+
this.set(key, settings[key], persist);
|
|
1318
|
+
}
|
|
1344
1319
|
}
|
|
1345
1320
|
}
|
|
1346
1321
|
/**
|
|
@@ -1351,18 +1326,25 @@ class SettingsService {
|
|
|
1351
1326
|
* is not found in the store.
|
|
1352
1327
|
* @returns The value.
|
|
1353
1328
|
*/
|
|
1354
|
-
get(key, defaultValue) {
|
|
1329
|
+
get(key, defaultValue, persist) {
|
|
1355
1330
|
key = this.getScopedKey(key);
|
|
1356
1331
|
// immediately return value from cache
|
|
1357
1332
|
if (this._cache.has(key)) {
|
|
1358
1333
|
return this._cache.get(key);
|
|
1359
1334
|
}
|
|
1360
1335
|
// try loading value from local storage
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1336
|
+
if (this.isPersist(persist)) {
|
|
1337
|
+
const value = localStorage.getItem(key);
|
|
1338
|
+
if (value) {
|
|
1339
|
+
try {
|
|
1340
|
+
const parsedValue = JSON.parse(value);
|
|
1341
|
+
this._cache.set(key, parsedValue);
|
|
1342
|
+
return parsedValue;
|
|
1343
|
+
}
|
|
1344
|
+
catch (e) {
|
|
1345
|
+
console.error(`Error parsing value for key "${key}" from localStorage`, e);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1366
1348
|
}
|
|
1367
1349
|
// not found, return default value
|
|
1368
1350
|
return defaultValue;
|
|
@@ -1371,26 +1353,91 @@ class SettingsService {
|
|
|
1371
1353
|
* Check if a key exists in the store.
|
|
1372
1354
|
*
|
|
1373
1355
|
* @param key The key.
|
|
1356
|
+
* @param persist True to check if the key exists in local storage.
|
|
1374
1357
|
* @returns True if the key exists in the store.
|
|
1375
1358
|
*/
|
|
1376
|
-
contains(key) {
|
|
1359
|
+
contains(key, persist) {
|
|
1377
1360
|
key = this.getScopedKey(key);
|
|
1378
|
-
|
|
1361
|
+
if (this._cache.has(key)) {
|
|
1362
|
+
return true;
|
|
1363
|
+
}
|
|
1364
|
+
return this.isPersist(persist) && localStorage.getItem(key) !== null;
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Get all the keys of the settings in the store.
|
|
1368
|
+
*
|
|
1369
|
+
* @param persist True to include keys from local storage.
|
|
1370
|
+
* @returns The keys of the settings in the store.
|
|
1371
|
+
*/
|
|
1372
|
+
getKeys(persist) {
|
|
1373
|
+
const keys = new Set();
|
|
1374
|
+
// collect keys from local storage
|
|
1375
|
+
if (this.isPersist(persist)) {
|
|
1376
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1377
|
+
const key = localStorage.key(i);
|
|
1378
|
+
if (key && (!this.keyPrefix || key.startsWith(this.keyPrefix))) {
|
|
1379
|
+
keys.add(key);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
// collect keys from cache
|
|
1384
|
+
this._cache.forEach((_, key) => keys.add(key));
|
|
1385
|
+
// return keys as array
|
|
1386
|
+
return Array.from(keys);
|
|
1379
1387
|
}
|
|
1380
1388
|
/**
|
|
1381
1389
|
* Remove a setting if present.
|
|
1382
1390
|
*
|
|
1383
1391
|
* @param key The key.
|
|
1392
|
+
* @param persist True to remove the setting from local storage.
|
|
1384
1393
|
*/
|
|
1385
|
-
remove(key) {
|
|
1394
|
+
remove(key, persist) {
|
|
1386
1395
|
key = this.getScopedKey(key);
|
|
1387
1396
|
this._cache.delete(key);
|
|
1388
|
-
|
|
1397
|
+
if (this.isPersist(persist)) {
|
|
1398
|
+
localStorage.removeItem(key);
|
|
1399
|
+
}
|
|
1400
|
+
this._subject.next(key);
|
|
1389
1401
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1402
|
+
/**
|
|
1403
|
+
* Clear all settings. Warning: this will remove all settings from the
|
|
1404
|
+
* local storage if no key prefix is set.
|
|
1405
|
+
*
|
|
1406
|
+
* @param persist True to remove all settings from local storage.
|
|
1407
|
+
*/
|
|
1408
|
+
clear(persist) {
|
|
1409
|
+
// remove from local storage all the settings
|
|
1410
|
+
const removedKeys = new Set();
|
|
1411
|
+
if (this.isPersist(persist)) {
|
|
1412
|
+
if (this.keyPrefix) {
|
|
1413
|
+
// collect keys to remove from local storage
|
|
1414
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1415
|
+
const key = localStorage.key(i);
|
|
1416
|
+
if (key && key.startsWith(this.keyPrefix)) {
|
|
1417
|
+
removedKeys.add(key);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
// remove collected keys from local storage
|
|
1421
|
+
removedKeys.forEach((key) => localStorage.removeItem(key));
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
localStorage.clear();
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
// remove from cache all the items emitting change events
|
|
1428
|
+
this._cache.forEach((_, key) => {
|
|
1429
|
+
this._cache.delete(key);
|
|
1430
|
+
this._subject.next(key);
|
|
1431
|
+
removedKeys.delete(key);
|
|
1432
|
+
});
|
|
1433
|
+
// emit changes for the remaining removed keys
|
|
1434
|
+
// for when the cache was not in sync with the local storage
|
|
1435
|
+
removedKeys.forEach((key) => this._subject.next(key));
|
|
1436
|
+
}
|
|
1437
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1438
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
|
|
1392
1439
|
}
|
|
1393
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
1440
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SettingsService, decorators: [{
|
|
1394
1441
|
type: Injectable,
|
|
1395
1442
|
args: [{
|
|
1396
1443
|
providedIn: 'root',
|
|
@@ -1448,36 +1495,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1448
1495
|
* as a whole.
|
|
1449
1496
|
*/
|
|
1450
1497
|
class ChainOperationEditorComponent {
|
|
1451
|
-
/**
|
|
1452
|
-
* The operation to edit.
|
|
1453
|
-
*/
|
|
1454
|
-
get operation() {
|
|
1455
|
-
return this._operation || undefined;
|
|
1456
|
-
}
|
|
1457
|
-
set operation(value) {
|
|
1458
|
-
if (this._operation === value) {
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
this._operation = value;
|
|
1462
|
-
this.updateForm(value);
|
|
1463
|
-
}
|
|
1464
|
-
/**
|
|
1465
|
-
* The snapshot the operation refers to.
|
|
1466
|
-
*/
|
|
1467
|
-
get snapshot() {
|
|
1468
|
-
return this._snapshot;
|
|
1469
|
-
}
|
|
1470
|
-
set snapshot(value) {
|
|
1471
|
-
if (this._snapshot === value) {
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
const dirty = this.hasTextChanges(value || undefined);
|
|
1475
|
-
this._snapshot = value || undefined;
|
|
1476
|
-
if (dirty) {
|
|
1477
|
-
this.requestPreview();
|
|
1478
|
-
this._editorModel?.setValue(this.svg.value || '');
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
1498
|
constructor(formBuilder, _clipboard, _settings, _dialogService) {
|
|
1482
1499
|
this._clipboard = _clipboard;
|
|
1483
1500
|
this._settings = _settings;
|
|
@@ -1486,18 +1503,30 @@ class ChainOperationEditorComponent {
|
|
|
1486
1503
|
this._disposables = [];
|
|
1487
1504
|
this._nanoid = customAlphabet('1234567890abcdef', 10);
|
|
1488
1505
|
this._editedSourceIndex = -1;
|
|
1506
|
+
/**
|
|
1507
|
+
* The operation to edit.
|
|
1508
|
+
*/
|
|
1509
|
+
this.operation = model();
|
|
1510
|
+
/**
|
|
1511
|
+
* The snapshot the operation refers to.
|
|
1512
|
+
*/
|
|
1513
|
+
this.snapshot = input();
|
|
1514
|
+
/**
|
|
1515
|
+
* Whether to hide the preview request button.
|
|
1516
|
+
*/
|
|
1517
|
+
this.hidePreview = input();
|
|
1489
1518
|
/**
|
|
1490
1519
|
* Emitted when the operation is changed.
|
|
1491
1520
|
*/
|
|
1492
|
-
this.operationChange =
|
|
1521
|
+
this.operationChange = output();
|
|
1493
1522
|
/**
|
|
1494
1523
|
* Emitted when the operation preview is requested.
|
|
1495
1524
|
*/
|
|
1496
|
-
this.operationPreview =
|
|
1525
|
+
this.operationPreview = output();
|
|
1497
1526
|
/**
|
|
1498
1527
|
* Emitted when operation editing is cancelled.
|
|
1499
1528
|
*/
|
|
1500
|
-
this.operationCancel =
|
|
1529
|
+
this.operationCancel = output();
|
|
1501
1530
|
this.tabIndex = 0;
|
|
1502
1531
|
this.hasTo = false;
|
|
1503
1532
|
this.hasToRun = false;
|
|
@@ -1535,6 +1564,9 @@ class ChainOperationEditorComponent {
|
|
|
1535
1564
|
nonNullable: true,
|
|
1536
1565
|
});
|
|
1537
1566
|
this.elementFeatures = formBuilder.control({}, { nonNullable: true });
|
|
1567
|
+
this.newTextHidden = formBuilder.control(false, {
|
|
1568
|
+
nonNullable: true,
|
|
1569
|
+
});
|
|
1538
1570
|
this.dpFeatures = formBuilder.control([], { nonNullable: true });
|
|
1539
1571
|
this.form = formBuilder.group({
|
|
1540
1572
|
rank: this.rank,
|
|
@@ -1553,10 +1585,24 @@ class ChainOperationEditorComponent {
|
|
|
1553
1585
|
value: this.value,
|
|
1554
1586
|
svg: this.svg,
|
|
1555
1587
|
elementFeatures: this.elementFeatures,
|
|
1588
|
+
newTextHidden: this.newTextHidden,
|
|
1556
1589
|
dpFeatures: this.dpFeatures,
|
|
1557
1590
|
});
|
|
1558
1591
|
// SVG elements
|
|
1559
1592
|
this.elements = [];
|
|
1593
|
+
// when operation changes, update form
|
|
1594
|
+
effect(() => {
|
|
1595
|
+
this.updateForm(this.operation());
|
|
1596
|
+
});
|
|
1597
|
+
// when snapshot changes, update SVG
|
|
1598
|
+
effect(() => {
|
|
1599
|
+
const snapshot = this.snapshot();
|
|
1600
|
+
const dirty = this.hasTextChanges(snapshot || undefined);
|
|
1601
|
+
if (dirty) {
|
|
1602
|
+
this.requestPreview();
|
|
1603
|
+
this._editorModel?.setValue(this.svg.value || '');
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1560
1606
|
}
|
|
1561
1607
|
ngOnInit() {
|
|
1562
1608
|
// whenever svg or base change, update preview
|
|
@@ -1575,25 +1621,25 @@ class ChainOperationEditorComponent {
|
|
|
1575
1621
|
}
|
|
1576
1622
|
}
|
|
1577
1623
|
hasTextChanges(snapshot) {
|
|
1578
|
-
if ((!snapshot && this.
|
|
1624
|
+
if ((!snapshot && this.snapshot()) || (snapshot && !this.snapshot())) {
|
|
1579
1625
|
return true;
|
|
1580
1626
|
}
|
|
1581
|
-
if (snapshot?.size?.width !== this.
|
|
1582
|
-
snapshot?.size?.height !== this.
|
|
1627
|
+
if (snapshot?.size?.width !== this.snapshot()?.size?.width ||
|
|
1628
|
+
snapshot?.size?.height !== this.snapshot()?.size?.height) {
|
|
1583
1629
|
return true;
|
|
1584
1630
|
}
|
|
1585
|
-
if (snapshot?.style !== this.
|
|
1586
|
-
snapshot?.text !== this.
|
|
1587
|
-
snapshot?.textStyle !== this.
|
|
1631
|
+
if (snapshot?.style !== this.snapshot()?.style ||
|
|
1632
|
+
snapshot?.text !== this.snapshot()?.text ||
|
|
1633
|
+
snapshot?.textStyle !== this.snapshot()?.textStyle) {
|
|
1588
1634
|
return true;
|
|
1589
1635
|
}
|
|
1590
1636
|
// compare textOptions returning true if any different
|
|
1591
1637
|
const options = snapshot?.textOptions;
|
|
1592
1638
|
if (options?.lineHeightOffset !==
|
|
1593
|
-
this.
|
|
1639
|
+
this.snapshot()?.textOptions?.lineHeightOffset ||
|
|
1594
1640
|
options?.charSpacingOffset !==
|
|
1595
|
-
this.
|
|
1596
|
-
options?.spcWidthOffset !== this.
|
|
1641
|
+
this.snapshot()?.textOptions?.charSpacingOffset ||
|
|
1642
|
+
options?.spcWidthOffset !== this.snapshot()?.textOptions?.spcWidthOffset) {
|
|
1597
1643
|
return true;
|
|
1598
1644
|
}
|
|
1599
1645
|
return false;
|
|
@@ -1611,7 +1657,9 @@ class ChainOperationEditorComponent {
|
|
|
1611
1657
|
this._editorModel || monaco.editor.createModel(this.svg.value, 'xml');
|
|
1612
1658
|
editor.setModel(this._editorModel);
|
|
1613
1659
|
this._editor = editor;
|
|
1614
|
-
this._disposables.push(
|
|
1660
|
+
this._disposables.push(
|
|
1661
|
+
// when the editor content changes, update the SVG control value
|
|
1662
|
+
this._editorModel.onDidChangeContent((e) => {
|
|
1615
1663
|
console.log('change content');
|
|
1616
1664
|
const code = this._editorModel.getValue();
|
|
1617
1665
|
if (code !== this.svg.value) {
|
|
@@ -1691,7 +1739,7 @@ class ChainOperationEditorComponent {
|
|
|
1691
1739
|
if (file) {
|
|
1692
1740
|
const reader = new FileReader();
|
|
1693
1741
|
reader.onload = (e) => {
|
|
1694
|
-
this.
|
|
1742
|
+
this._editorModel?.setValue(e.target?.result);
|
|
1695
1743
|
};
|
|
1696
1744
|
reader.readAsText(file);
|
|
1697
1745
|
}
|
|
@@ -1700,7 +1748,7 @@ class ChainOperationEditorComponent {
|
|
|
1700
1748
|
}
|
|
1701
1749
|
setSvgFromClipboard() {
|
|
1702
1750
|
navigator.clipboard.readText().then((text) => {
|
|
1703
|
-
this.
|
|
1751
|
+
this._editorModel?.setValue(text);
|
|
1704
1752
|
});
|
|
1705
1753
|
}
|
|
1706
1754
|
parseSvg(svg) {
|
|
@@ -1728,6 +1776,18 @@ class ChainOperationEditorComponent {
|
|
|
1728
1776
|
return [];
|
|
1729
1777
|
}
|
|
1730
1778
|
}
|
|
1779
|
+
removeDecimals() {
|
|
1780
|
+
const svg = this.svg.value;
|
|
1781
|
+
const newSvg = svg.replace(/(\d+)\.\d+/g, '$1');
|
|
1782
|
+
this._editorModel?.setValue(newSvg);
|
|
1783
|
+
}
|
|
1784
|
+
wrapInGroup() {
|
|
1785
|
+
// wrap SVG code in <g>...</g> if it does not already start with <g>
|
|
1786
|
+
const svg = this.svg.value;
|
|
1787
|
+
if (!svg.startsWith('<g')) {
|
|
1788
|
+
this._editorModel?.setValue(`<g>${svg}</g>`);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1731
1791
|
// #endregion
|
|
1732
1792
|
// #region element features
|
|
1733
1793
|
onTabIndexChange(index) {
|
|
@@ -1769,6 +1829,7 @@ class ChainOperationEditorComponent {
|
|
|
1769
1829
|
...this.elementFeatures.value,
|
|
1770
1830
|
[this.editedElementId]: features,
|
|
1771
1831
|
});
|
|
1832
|
+
this.editedElementId = undefined;
|
|
1772
1833
|
}
|
|
1773
1834
|
}
|
|
1774
1835
|
// #endregion
|
|
@@ -1819,6 +1880,7 @@ class ChainOperationEditorComponent {
|
|
|
1819
1880
|
this.groupId.setValue(operation.groupId || null);
|
|
1820
1881
|
this.features.setValue(operation.features || []);
|
|
1821
1882
|
this.sources.setValue(operation.sources || []);
|
|
1883
|
+
this.newTextHidden.setValue(operation.diplomatics?.isNewTextHidden || false);
|
|
1822
1884
|
this.dpFeatures.setValue(operation.diplomatics?.features || []);
|
|
1823
1885
|
this.type.setValue(operation.type);
|
|
1824
1886
|
this.at.setValue(operation.at);
|
|
@@ -1848,6 +1910,7 @@ class ChainOperationEditorComponent {
|
|
|
1848
1910
|
sources: this.sources.value?.length ? this.sources.value : undefined,
|
|
1849
1911
|
diplomatics: {
|
|
1850
1912
|
g: this.svg.value,
|
|
1913
|
+
isNewTextHidden: this.newTextHidden.value,
|
|
1851
1914
|
features: this.dpFeatures.value,
|
|
1852
1915
|
elementFeatures: this.elementFeatures.value,
|
|
1853
1916
|
},
|
|
@@ -1871,25 +1934,26 @@ class ChainOperationEditorComponent {
|
|
|
1871
1934
|
if (!this.form.valid) {
|
|
1872
1935
|
return;
|
|
1873
1936
|
}
|
|
1874
|
-
this.
|
|
1875
|
-
this.operationChange.emit(this.
|
|
1937
|
+
this.operation.set(this.getOperation());
|
|
1938
|
+
this.operationChange.emit(this.operation());
|
|
1876
1939
|
}
|
|
1877
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
1878
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", 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:
|
|
1940
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$3.Clipboard }, { token: SettingsService }, { token: i2$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1941
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: ChainOperationEditorComponent, isStandalone: true, selector: "gve-chain-operation-editor", inputs: { operation: { classPropertyName: "operation", publicName: "operation", isSignal: true, isRequired: false, transformFunction: null }, snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, hidePreview: { classPropertyName: "hidePreview", publicName: "hidePreview", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operation: "operationChange", 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\"\r\n ><mat-icon>layers</mat-icon> replace</mat-option\r\n >\r\n <mat-option [value]=\"1\"\r\n ><mat-icon>close</mat-icon>delete</mat-option\r\n >\r\n <mat-option [value]=\"2\"\r\n ><mat-icon>last_page</mat-icon>add-before</mat-option\r\n >\r\n <mat-option [value]=\"3\"\r\n ><mat-icon>first_page</mat-icon>add-after</mat-option\r\n >\r\n <mat-option [value]=\"4\"\r\n ><mat-icon>login</mat-icon>move-before</mat-option\r\n >\r\n <mat-option [value]=\"5\"\r\n ><mat-icon>logout</mat-icon>move-after</mat-option\r\n >\r\n <mat-option [value]=\"6\"\r\n ><mat-icon>compare_arrows</mat-icon>swap</mat-option\r\n >\r\n <mat-option [value]=\"7\"\r\n ><mat-icon>edit_note</mat-icon>annotate</mat-option\r\n >\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 <button\r\n id=\"btn-decimals\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Remove decimals\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"removeDecimals()\"\r\n >\r\n <mat-icon>pin</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-group\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Wrap code in group\"\r\n (click)=\"wrapInGroup()\"\r\n >\r\n <mat-icon>code</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 </td>\r\n <td>\r\n <span\r\n [matBadge]=\"$any(elementFeatures.value)[e.id]?.length || 0\"\r\n [matBadgeHidden]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n matBadgeOverlap=\"false\"\r\n >{{ e.id }}</span\r\n >\r\n </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>{{\r\n editedElementId\r\n }}</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 <div>\r\n <mat-checkbox [formControl]=\"newTextHidden\">hide new text</mat-checkbox>\r\n </div>\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:
|
|
1879
1942
|
// material
|
|
1880
|
-
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", "aria-expanded", "aria-controls", "aria-owns", "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", "disabledInteractive"], 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:
|
|
1943
|
+
ClipboardModule }, { kind: "directive", type: i2$3.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i6$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { 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", "aria-expanded", "aria-controls", "aria-owns", "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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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: i16.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i16.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-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:
|
|
1881
1944
|
// monaco
|
|
1882
|
-
NgeMonacoModule }, { kind: "component", type:
|
|
1945
|
+
NgeMonacoModule }, { kind: "component", type: i17.NgeMonacoEditorComponent, selector: "nge-monaco-editor", inputs: ["autoLayout", "options"], outputs: ["ready"] }, { kind: "pipe", type:
|
|
1883
1946
|
// myrmex
|
|
1884
|
-
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"] }] }); }
|
|
1947
|
+
SafeHtmlPipe, name: "safeHtml" }, { kind: "component", type: 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"] }] }); }
|
|
1885
1948
|
}
|
|
1886
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
1949
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainOperationEditorComponent, decorators: [{
|
|
1887
1950
|
type: Component,
|
|
1888
1951
|
args: [{ selector: 'gve-chain-operation-editor', imports: [
|
|
1889
1952
|
CommonModule,
|
|
1890
1953
|
ReactiveFormsModule,
|
|
1891
1954
|
// material
|
|
1892
1955
|
ClipboardModule,
|
|
1956
|
+
MatBadgeModule,
|
|
1893
1957
|
MatButtonModule,
|
|
1894
1958
|
MatCheckboxModule,
|
|
1895
1959
|
MatExpansionModule,
|
|
@@ -1901,25 +1965,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1901
1965
|
MatTooltipModule,
|
|
1902
1966
|
// monaco
|
|
1903
1967
|
NgeMonacoModule,
|
|
1904
|
-
NgToolsModule,
|
|
1905
|
-
NgMatToolsModule,
|
|
1906
1968
|
// myrmex
|
|
1969
|
+
SafeHtmlPipe,
|
|
1907
1970
|
FeatureSetEditorComponent,
|
|
1908
1971
|
OperationSourceEditorComponent,
|
|
1909
|
-
], 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"] }]
|
|
1910
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$3.Clipboard }, { type: SettingsService }, { type: i2$
|
|
1911
|
-
type: Input
|
|
1912
|
-
}], snapshot: [{
|
|
1913
|
-
type: Input
|
|
1914
|
-
}], hidePreview: [{
|
|
1915
|
-
type: Input
|
|
1916
|
-
}], operationChange: [{
|
|
1917
|
-
type: Output
|
|
1918
|
-
}], operationPreview: [{
|
|
1919
|
-
type: Output
|
|
1920
|
-
}], operationCancel: [{
|
|
1921
|
-
type: Output
|
|
1922
|
-
}] } });
|
|
1972
|
+
], 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\"\r\n ><mat-icon>layers</mat-icon> replace</mat-option\r\n >\r\n <mat-option [value]=\"1\"\r\n ><mat-icon>close</mat-icon>delete</mat-option\r\n >\r\n <mat-option [value]=\"2\"\r\n ><mat-icon>last_page</mat-icon>add-before</mat-option\r\n >\r\n <mat-option [value]=\"3\"\r\n ><mat-icon>first_page</mat-icon>add-after</mat-option\r\n >\r\n <mat-option [value]=\"4\"\r\n ><mat-icon>login</mat-icon>move-before</mat-option\r\n >\r\n <mat-option [value]=\"5\"\r\n ><mat-icon>logout</mat-icon>move-after</mat-option\r\n >\r\n <mat-option [value]=\"6\"\r\n ><mat-icon>compare_arrows</mat-icon>swap</mat-option\r\n >\r\n <mat-option [value]=\"7\"\r\n ><mat-icon>edit_note</mat-icon>annotate</mat-option\r\n >\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 <button\r\n id=\"btn-decimals\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Remove decimals\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"removeDecimals()\"\r\n >\r\n <mat-icon>pin</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-group\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Wrap code in group\"\r\n (click)=\"wrapInGroup()\"\r\n >\r\n <mat-icon>code</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 </td>\r\n <td>\r\n <span\r\n [matBadge]=\"$any(elementFeatures.value)[e.id]?.length || 0\"\r\n [matBadgeHidden]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n matBadgeOverlap=\"false\"\r\n >{{ e.id }}</span\r\n >\r\n </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>{{\r\n editedElementId\r\n }}</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 <div>\r\n <mat-checkbox [formControl]=\"newTextHidden\">hide new text</mat-checkbox>\r\n </div>\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"] }]
|
|
1973
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$3.Clipboard }, { type: SettingsService }, { type: i2$1.DialogService }] });
|
|
1923
1974
|
|
|
1924
1975
|
/**
|
|
1925
1976
|
* 🔑 `gve-feature-set-view`
|
|
@@ -1939,34 +1990,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1939
1990
|
* is greater than the threshold. Default is 5.
|
|
1940
1991
|
*/
|
|
1941
1992
|
class FeatureSetViewComponent {
|
|
1942
|
-
/**
|
|
1943
|
-
* The features.
|
|
1944
|
-
*/
|
|
1945
|
-
get features() {
|
|
1946
|
-
return this._features;
|
|
1947
|
-
}
|
|
1948
|
-
set features(value) {
|
|
1949
|
-
if (this._features === value) {
|
|
1950
|
-
return;
|
|
1951
|
-
}
|
|
1952
|
-
this._features = value || undefined;
|
|
1953
|
-
this.filter.reset();
|
|
1954
|
-
this.applyFeatureFilter();
|
|
1955
|
-
}
|
|
1956
1993
|
constructor(formBuilder) {
|
|
1957
|
-
|
|
1994
|
+
/**
|
|
1995
|
+
* The features.
|
|
1996
|
+
*/
|
|
1997
|
+
this.features = input([]);
|
|
1998
|
+
/**
|
|
1999
|
+
* The list of feature names to display in the name selection.
|
|
2000
|
+
* This is used when you have a closed list of features.
|
|
2001
|
+
*/
|
|
2002
|
+
this.featNames = input();
|
|
2003
|
+
/**
|
|
2004
|
+
* The feature values map. When specified and the user selects a feature
|
|
2005
|
+
* name present in the map keys, the corresponding values will be used
|
|
2006
|
+
* to populate the value selection.
|
|
2007
|
+
*/
|
|
2008
|
+
this.featValues = input();
|
|
1958
2009
|
/**
|
|
1959
2010
|
* The threshold at which the features filter should become visible.
|
|
1960
2011
|
* If set to 0, the filter is always visible; if set to -1, it is always
|
|
1961
2012
|
* invisible; otherwise, it gets visible when the number of features
|
|
1962
2013
|
* is greater than the threshold. Default is 5.
|
|
1963
2014
|
*/
|
|
1964
|
-
this.filterThreshold = 5;
|
|
2015
|
+
this.filterThreshold = input(5);
|
|
1965
2016
|
this.filteredFeatures = [];
|
|
1966
2017
|
this.filter = formBuilder.control(null);
|
|
2018
|
+
// when features change, reset the filter and apply it
|
|
2019
|
+
effect(() => {
|
|
2020
|
+
const features = this.features();
|
|
2021
|
+
this.filter.reset();
|
|
2022
|
+
this.applyFeatureFilter();
|
|
2023
|
+
});
|
|
1967
2024
|
}
|
|
1968
2025
|
applyFeatureFilter(name) {
|
|
1969
|
-
this.filteredFeatures = this.
|
|
2026
|
+
this.filteredFeatures = this.features().filter((feature) => {
|
|
1970
2027
|
return !name || feature.name.toLowerCase().includes(name.toLowerCase());
|
|
1971
2028
|
});
|
|
1972
2029
|
}
|
|
@@ -1981,10 +2038,10 @@ class FeatureSetViewComponent {
|
|
|
1981
2038
|
ngOnDestroy() {
|
|
1982
2039
|
this._sub?.unsubscribe();
|
|
1983
2040
|
}
|
|
1984
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
1985
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
2041
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureSetViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2042
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: FeatureSetViewComponent, isStandalone: true, selector: "gve-feature-set-view", inputs: { features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null } }, 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", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
|
|
1986
2043
|
}
|
|
1987
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
2044
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FeatureSetViewComponent, decorators: [{
|
|
1988
2045
|
type: Component,
|
|
1989
2046
|
args: [{ selector: 'gve-feature-set-view', imports: [
|
|
1990
2047
|
CommonModule,
|
|
@@ -1993,17 +2050,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
1993
2050
|
MatFormFieldModule,
|
|
1994
2051
|
MatIconModule,
|
|
1995
2052
|
MatInputModule,
|
|
1996
|
-
|
|
1997
|
-
], template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold === 0 || features
|
|
1998
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
1999
|
-
type: Input
|
|
2000
|
-
}], featNames: [{
|
|
2001
|
-
type: Input
|
|
2002
|
-
}], featValues: [{
|
|
2003
|
-
type: Input
|
|
2004
|
-
}], filterThreshold: [{
|
|
2005
|
-
type: Input
|
|
2006
|
-
}] } });
|
|
2053
|
+
FlatLookupPipe,
|
|
2054
|
+
], 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"] }]
|
|
2055
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
2007
2056
|
|
|
2008
2057
|
/**
|
|
2009
2058
|
* 🔑 `gve-steps-map`
|
|
@@ -2026,59 +2075,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2026
2075
|
*/
|
|
2027
2076
|
class StepsMapComponent {
|
|
2028
2077
|
constructor() {
|
|
2029
|
-
|
|
2078
|
+
// the lines of each version text, keyed under the output tag
|
|
2079
|
+
this.lines = {};
|
|
2080
|
+
/**
|
|
2081
|
+
* The steps to display.
|
|
2082
|
+
*/
|
|
2083
|
+
this.steps = input([]);
|
|
2084
|
+
/**
|
|
2085
|
+
* The step that is currently selected.
|
|
2086
|
+
*/
|
|
2087
|
+
this.selectedStep = model();
|
|
2030
2088
|
/**
|
|
2031
2089
|
* The font size of the steps text.
|
|
2032
2090
|
*/
|
|
2033
|
-
this.textFontSize = '0.8em';
|
|
2091
|
+
this.textFontSize = input('0.8em');
|
|
2034
2092
|
/**
|
|
2035
2093
|
* Emitted when the selected step has changed by user, or when
|
|
2036
2094
|
* the steps are set for the first time.
|
|
2037
2095
|
*/
|
|
2038
|
-
this.selectedStepChange =
|
|
2096
|
+
this.selectedStepChange = output();
|
|
2097
|
+
// when the steps change, update lines and selected step
|
|
2098
|
+
effect(() => {
|
|
2099
|
+
const steps = this.steps();
|
|
2100
|
+
const first = steps === undefined;
|
|
2101
|
+
if (first && steps.length) {
|
|
2102
|
+
this.selectedStep.set(steps[steps.length - 1]);
|
|
2103
|
+
this.updateLines(steps);
|
|
2104
|
+
this.selectedStepChange.emit(this.selectedStep());
|
|
2105
|
+
}
|
|
2106
|
+
else {
|
|
2107
|
+
this.selectedStep.set(undefined);
|
|
2108
|
+
this.selectedStepChange.emit(undefined);
|
|
2109
|
+
this.updateLines(steps);
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2039
2112
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
get steps() {
|
|
2044
|
-
return this._steps;
|
|
2045
|
-
}
|
|
2046
|
-
set steps(value) {
|
|
2047
|
-
if (this._steps === value) {
|
|
2113
|
+
updateLines(steps) {
|
|
2114
|
+
if (!steps) {
|
|
2115
|
+
this.lines = {};
|
|
2048
2116
|
return;
|
|
2049
2117
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
if (first && this._steps?.length) {
|
|
2054
|
-
this.selectedStep = this._steps[this._steps.length - 1];
|
|
2055
|
-
this.selectedStepChange.emit(this.selectedStep);
|
|
2118
|
+
for (let i = 0; i < steps.length; i++) {
|
|
2119
|
+
const step = this.steps()[i];
|
|
2120
|
+
this.lines[step.outputTag] = step.result?.split('\n') || [];
|
|
2056
2121
|
}
|
|
2057
|
-
this.updateLines();
|
|
2058
|
-
}
|
|
2059
|
-
updateLines() {
|
|
2060
|
-
this.lines = this.selectedStep?.result?.split('\n') || [];
|
|
2061
2122
|
}
|
|
2062
2123
|
onStepClick(step) {
|
|
2063
|
-
this.selectedStep
|
|
2064
|
-
this.
|
|
2065
|
-
this.selectedStepChange.emit(step);
|
|
2124
|
+
this.selectedStep.set(step);
|
|
2125
|
+
this.selectedStepChange.emit(this.selectedStep());
|
|
2066
2126
|
}
|
|
2067
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
2068
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
2127
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StepsMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2128
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: StepsMapComponent, isStandalone: true, selector: "gve-steps-map", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null }, selectedStep: { classPropertyName: "selectedStep", publicName: "selectedStep", isSignal: true, isRequired: false, transformFunction: null }, textFontSize: { classPropertyName: "textFontSize", publicName: "textFontSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedStep: "selectedStepChange", 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 <!-- tag -->\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} ▶ </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <!-- text lines -->\r\n <div class=\"text\" [style.fontSize]=\"textFontSize()\">\r\n @for (line of lines[step.outputTag]; 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 }] }); }
|
|
2069
2129
|
}
|
|
2070
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
2130
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StepsMapComponent, decorators: [{
|
|
2071
2131
|
type: Component,
|
|
2072
|
-
args: [{ selector: 'gve-steps-map', imports: [CommonModule, MatButtonModule, MatRippleModule, MatTooltipModule], template: "<div>\r\n @if (steps
|
|
2073
|
-
}],
|
|
2074
|
-
type: Input
|
|
2075
|
-
}], selectedStep: [{
|
|
2076
|
-
type: Input
|
|
2077
|
-
}], textFontSize: [{
|
|
2078
|
-
type: Input
|
|
2079
|
-
}], selectedStepChange: [{
|
|
2080
|
-
type: Output
|
|
2081
|
-
}] } });
|
|
2132
|
+
args: [{ selector: 'gve-steps-map', 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 <!-- tag -->\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} ▶ </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <!-- text lines -->\r\n <div class=\"text\" [style.fontSize]=\"textFontSize()\">\r\n @for (line of lines[step.outputTag]; 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"] }]
|
|
2133
|
+
}], ctorParameters: () => [] });
|
|
2082
2134
|
|
|
2083
2135
|
/**
|
|
2084
2136
|
* 🔑 `gve-chain-result-view`
|
|
@@ -2094,48 +2146,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2094
2146
|
* result's step is picked by the user.
|
|
2095
2147
|
*/
|
|
2096
2148
|
class ChainResultViewComponent {
|
|
2097
|
-
/**
|
|
2098
|
-
* The result to display.
|
|
2099
|
-
*/
|
|
2100
|
-
get result() {
|
|
2101
|
-
return this._result;
|
|
2102
|
-
}
|
|
2103
|
-
set result(value) {
|
|
2104
|
-
if (this._result === value) {
|
|
2105
|
-
return;
|
|
2106
|
-
}
|
|
2107
|
-
this._result = value || undefined;
|
|
2108
|
-
this.updateForm(this._result);
|
|
2109
|
-
// select the initial step if set
|
|
2110
|
-
this.selectInitialStep();
|
|
2111
|
-
}
|
|
2112
|
-
/**
|
|
2113
|
-
* The index of the initial step to display after the result is set.
|
|
2114
|
-
* If the index is negative, it is counted from the end of the steps.
|
|
2115
|
-
*/
|
|
2116
|
-
get initialStepIndex() {
|
|
2117
|
-
return this._initialStepIndex;
|
|
2118
|
-
}
|
|
2119
|
-
set initialStepIndex(value) {
|
|
2120
|
-
if (value === undefined || value === null) {
|
|
2121
|
-
this._initialStepIndex = undefined;
|
|
2122
|
-
this.step = undefined;
|
|
2123
|
-
}
|
|
2124
|
-
else {
|
|
2125
|
-
this._initialStepIndex = value;
|
|
2126
|
-
this.selectInitialStep();
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
2149
|
constructor(formBuilder) {
|
|
2150
|
+
/**
|
|
2151
|
+
* The result to display.
|
|
2152
|
+
*/
|
|
2153
|
+
this.result = input();
|
|
2154
|
+
/**
|
|
2155
|
+
* The index of the initial step to display after the result is set.
|
|
2156
|
+
* If the index is negative, it is counted from the end of the steps.
|
|
2157
|
+
*/
|
|
2158
|
+
this.initialStepIndex = input();
|
|
2130
2159
|
/**
|
|
2131
2160
|
* Emitted when a result's step is picked by the user.
|
|
2132
2161
|
*/
|
|
2133
|
-
this.stepPick =
|
|
2162
|
+
this.stepPick = output();
|
|
2134
2163
|
this.versionTags = [];
|
|
2135
2164
|
this.tags = [];
|
|
2136
2165
|
this.selectionFeatures = [];
|
|
2166
|
+
// bind callback methods
|
|
2167
|
+
this.getCharBorderColor = this.getCharBorderColor.bind(this);
|
|
2137
2168
|
this.versionTag = formBuilder.control(null);
|
|
2138
2169
|
this.tag = formBuilder.control(null);
|
|
2170
|
+
// when result changes, update the form
|
|
2171
|
+
effect(() => {
|
|
2172
|
+
this.updateForm(this.result());
|
|
2173
|
+
// select the initial step if set
|
|
2174
|
+
this.selectInitialStep(this.initialStepIndex());
|
|
2175
|
+
});
|
|
2176
|
+
// when initialStepIndex changes, update the selected step
|
|
2177
|
+
effect(() => {
|
|
2178
|
+
this.selectInitialStep(this.initialStepIndex());
|
|
2179
|
+
});
|
|
2139
2180
|
}
|
|
2140
2181
|
ngOnInit() {
|
|
2141
2182
|
this._subs = [
|
|
@@ -2149,32 +2190,38 @@ class ChainResultViewComponent {
|
|
|
2149
2190
|
this.tag.valueChanges
|
|
2150
2191
|
.pipe(distinctUntilChanged(), debounceTime(200))
|
|
2151
2192
|
.subscribe((value) => {
|
|
2152
|
-
this.step = this.
|
|
2193
|
+
this.step = this.result()?.steps.find((step) => step.outputTag === value);
|
|
2153
2194
|
if (this.step) {
|
|
2154
2195
|
this.stepPick.emit(this.step);
|
|
2155
2196
|
}
|
|
2156
2197
|
}),
|
|
2157
2198
|
];
|
|
2158
2199
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2200
|
+
getCharBorderColor(node) {
|
|
2201
|
+
return this.step?.refNodeIds.includes(node.id) ? 'orange' : null;
|
|
2202
|
+
}
|
|
2203
|
+
selectInitialStep(initialStepIndex) {
|
|
2204
|
+
if (initialStepIndex !== undefined && this.result()?.steps) {
|
|
2161
2205
|
this.step =
|
|
2162
|
-
this.
|
|
2163
|
-
? this.
|
|
2164
|
-
:
|
|
2206
|
+
this.result().steps[initialStepIndex < 0
|
|
2207
|
+
? this.result().steps.length + initialStepIndex
|
|
2208
|
+
: initialStepIndex];
|
|
2209
|
+
}
|
|
2210
|
+
else {
|
|
2211
|
+
this.step = undefined;
|
|
2165
2212
|
}
|
|
2166
2213
|
}
|
|
2167
2214
|
ngOnDestroy() {
|
|
2168
2215
|
this._subs?.forEach((sub) => sub.unsubscribe());
|
|
2169
2216
|
}
|
|
2170
2217
|
updateVersionTags() {
|
|
2171
|
-
if (!this.
|
|
2218
|
+
if (!this.result()) {
|
|
2172
2219
|
this.versionTags = [];
|
|
2173
2220
|
return;
|
|
2174
2221
|
}
|
|
2175
2222
|
// extract version tags from result steps features named 'version'
|
|
2176
2223
|
const tags = new Set();
|
|
2177
|
-
for (const step of this.
|
|
2224
|
+
for (const step of this.result().steps) {
|
|
2178
2225
|
for (const feature of step.featureSet.features) {
|
|
2179
2226
|
if (feature.name === 'version') {
|
|
2180
2227
|
tags.add(feature.value);
|
|
@@ -2184,10 +2231,10 @@ class ChainResultViewComponent {
|
|
|
2184
2231
|
this.versionTags = Array.from(tags).sort();
|
|
2185
2232
|
}
|
|
2186
2233
|
getTagFromVersion(version) {
|
|
2187
|
-
if (!this.
|
|
2234
|
+
if (!this.result() || !version) {
|
|
2188
2235
|
return undefined;
|
|
2189
2236
|
}
|
|
2190
|
-
for (const step of this.
|
|
2237
|
+
for (const step of this.result().steps) {
|
|
2191
2238
|
for (const feature of step.featureSet.features) {
|
|
2192
2239
|
if (feature.name === 'version' && feature.value === version) {
|
|
2193
2240
|
return step.outputTag;
|
|
@@ -2219,8 +2266,8 @@ class ChainResultViewComponent {
|
|
|
2219
2266
|
if (!this.step) {
|
|
2220
2267
|
return null;
|
|
2221
2268
|
}
|
|
2222
|
-
for (let key of Object.keys(this.
|
|
2223
|
-
const node = this.
|
|
2269
|
+
for (let key of Object.keys(this.result().taggedNodes)) {
|
|
2270
|
+
const node = this.result().taggedNodes[key].find((n) => n.id === id);
|
|
2224
2271
|
if (node) {
|
|
2225
2272
|
return node;
|
|
2226
2273
|
}
|
|
@@ -2241,7 +2288,7 @@ class ChainResultViewComponent {
|
|
|
2241
2288
|
this.selection = range.at + '×' + range.run;
|
|
2242
2289
|
const end = range.at + range.run;
|
|
2243
2290
|
const features = [];
|
|
2244
|
-
const tagNodes = this.
|
|
2291
|
+
const tagNodes = this.result().taggedNodes[this.tag.value];
|
|
2245
2292
|
for (let node of tagNodes) {
|
|
2246
2293
|
// if in range, intersect features
|
|
2247
2294
|
if (node.id >= range.at && node.id < end) {
|
|
@@ -2260,14 +2307,16 @@ class ChainResultViewComponent {
|
|
|
2260
2307
|
this.selectionFeatures = features;
|
|
2261
2308
|
}
|
|
2262
2309
|
onStepChange(step) {
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2310
|
+
if (step) {
|
|
2311
|
+
// setting the tag will trigger the step change
|
|
2312
|
+
this.tag.setValue(step.outputTag);
|
|
2313
|
+
this.selectionFeatures = [];
|
|
2314
|
+
}
|
|
2266
2315
|
}
|
|
2267
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
2268
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
2316
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainResultViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2317
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: ChainResultViewComponent, isStandalone: true, selector: "gve-chain-result-view", inputs: { result: { classPropertyName: "result", publicName: "result", isSignal: true, isRequired: false, transformFunction: null }, initialStepIndex: { classPropertyName: "initialStepIndex", publicName: "initialStepIndex", isSignal: true, isRequired: false, transformFunction: null } }, 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 [borderColorCallback]=\"getCharBorderColor\"\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", "canSelectNullableOptions"], 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", "borderColorCallback"], outputs: ["charPick", "rangePick"] }] }); }
|
|
2269
2318
|
}
|
|
2270
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
2319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainResultViewComponent, decorators: [{
|
|
2271
2320
|
type: Component,
|
|
2272
2321
|
args: [{ selector: 'gve-chain-result-view', imports: [
|
|
2273
2322
|
CommonModule,
|
|
@@ -2279,14 +2328,230 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2279
2328
|
FeatureSetViewComponent,
|
|
2280
2329
|
StepsMapComponent,
|
|
2281
2330
|
BaseTextViewComponent,
|
|
2282
|
-
], 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
|
|
2283
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2331
|
+
], 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 [borderColorCallback]=\"getCharBorderColor\"\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"] }]
|
|
2332
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
2333
|
+
|
|
2334
|
+
class GveGraphvizService {
|
|
2335
|
+
hashString(str) {
|
|
2336
|
+
let hash = 0;
|
|
2337
|
+
for (let i = 0; i < str.length; i++) {
|
|
2338
|
+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
2339
|
+
hash = hash & hash; // convert to 32bit integer
|
|
2340
|
+
}
|
|
2341
|
+
return Math.abs(hash);
|
|
2342
|
+
}
|
|
2343
|
+
hslToRgb(hue, saturation, lightness) {
|
|
2344
|
+
const chroma = ((1 - Math.abs((2 * lightness) / 100 - 1)) * saturation) / 100;
|
|
2345
|
+
const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1));
|
|
2346
|
+
const m = lightness / 100 - chroma / 2;
|
|
2347
|
+
let r = 0, g = 0, b = 0;
|
|
2348
|
+
if (hue >= 0 && hue < 60) {
|
|
2349
|
+
r = chroma;
|
|
2350
|
+
g = x;
|
|
2351
|
+
}
|
|
2352
|
+
else if (hue >= 60 && hue < 120) {
|
|
2353
|
+
r = x;
|
|
2354
|
+
g = chroma;
|
|
2355
|
+
}
|
|
2356
|
+
else if (hue >= 120 && hue < 180) {
|
|
2357
|
+
g = chroma;
|
|
2358
|
+
b = x;
|
|
2359
|
+
}
|
|
2360
|
+
else if (hue >= 180 && hue < 240) {
|
|
2361
|
+
g = x;
|
|
2362
|
+
b = chroma;
|
|
2363
|
+
}
|
|
2364
|
+
else if (hue >= 240 && hue < 300) {
|
|
2365
|
+
r = x;
|
|
2366
|
+
b = chroma;
|
|
2367
|
+
}
|
|
2368
|
+
else if (hue >= 300 && hue < 360) {
|
|
2369
|
+
r = chroma;
|
|
2370
|
+
b = x;
|
|
2371
|
+
}
|
|
2372
|
+
r = Math.round((r + m) * 255);
|
|
2373
|
+
g = Math.round((g + m) * 255);
|
|
2374
|
+
b = Math.round((b + m) * 255);
|
|
2375
|
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
2376
|
+
}
|
|
2377
|
+
getColorForTag(tag) {
|
|
2378
|
+
const hash = this.hashString(tag);
|
|
2379
|
+
const hue = (hash * 137) % 360; // Use a prime number to spread out the hues
|
|
2380
|
+
const saturation = 60 + (hash % 40); // Saturation between 60% and 100%
|
|
2381
|
+
const lightness = 50 + (hash % 30); // Lightness between 50% and 80%
|
|
2382
|
+
return this.hslToRgb(hue, saturation, lightness);
|
|
2383
|
+
}
|
|
2384
|
+
getExcludedNodeIds(chain, tags) {
|
|
2385
|
+
// get only the desired links
|
|
2386
|
+
const links = chain.links.filter((link) => tags ? tags.includes(link.tag) : true);
|
|
2387
|
+
// return all the nodes not referenced by the links
|
|
2388
|
+
const excluded = new Set();
|
|
2389
|
+
chain.nodes.forEach((node) => {
|
|
2390
|
+
excluded.add(node.id);
|
|
2391
|
+
});
|
|
2392
|
+
links.forEach((link) => {
|
|
2393
|
+
excluded.delete(link.sourceId);
|
|
2394
|
+
excluded.delete(link.targetId);
|
|
2395
|
+
});
|
|
2396
|
+
return excluded;
|
|
2397
|
+
}
|
|
2398
|
+
/**
|
|
2399
|
+
* Represent the received chain as a Graphviz digraph.
|
|
2400
|
+
*
|
|
2401
|
+
* @param chain The source chain if any.
|
|
2402
|
+
* @param tags The tags to show. When set, only the links with these tags are shown.
|
|
2403
|
+
* @param rankdir The rank direction.
|
|
2404
|
+
* @returns Graphviz representation of the chain.
|
|
2405
|
+
*/
|
|
2406
|
+
generateGraph(chain, tags, rankdir = 'LR') {
|
|
2407
|
+
if (!chain) {
|
|
2408
|
+
return 'digraph G {}';
|
|
2409
|
+
}
|
|
2410
|
+
const sb = [];
|
|
2411
|
+
const excludedNodeIds = this.getExcludedNodeIds(chain, tags);
|
|
2412
|
+
sb.push('digraph G {');
|
|
2413
|
+
sb.push(' bgcolor=transparent;');
|
|
2414
|
+
sb.push(' node [style=filled];');
|
|
2415
|
+
sb.push(` rankdir=${rankdir};`);
|
|
2416
|
+
chain.nodes.forEach((node) => {
|
|
2417
|
+
sb.push(` ${node.id} [label="${node.label}"` +
|
|
2418
|
+
(node.sourceTag
|
|
2419
|
+
? ` fillcolor="${this.getColorForTag(node.sourceTag)}"`
|
|
2420
|
+
: '') +
|
|
2421
|
+
(node.sourceTag && excludedNodeIds.has(node.id)
|
|
2422
|
+
? ` xlabel="${node.sourceTag}"`
|
|
2423
|
+
: '') +
|
|
2424
|
+
'];');
|
|
2425
|
+
});
|
|
2426
|
+
chain.links.forEach((link) => {
|
|
2427
|
+
if (!link.sourceId ||
|
|
2428
|
+
!link.targetId ||
|
|
2429
|
+
(tags && !tags.includes(link.tag))) {
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
const color = this.getColorForTag(link.tag);
|
|
2433
|
+
sb.push(` ${link.sourceId} -> ${link.targetId} [label="${link.tag}", color="${color}"];`);
|
|
2434
|
+
});
|
|
2435
|
+
sb.push('}');
|
|
2436
|
+
return sb.join('\n');
|
|
2437
|
+
}
|
|
2438
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveGraphvizService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2439
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveGraphvizService, providedIn: 'root' }); }
|
|
2440
|
+
}
|
|
2441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GveGraphvizService, decorators: [{
|
|
2442
|
+
type: Injectable,
|
|
2443
|
+
args: [{
|
|
2444
|
+
providedIn: 'root',
|
|
2445
|
+
}]
|
|
2446
|
+
}] });
|
|
2447
|
+
|
|
2448
|
+
class ChainViewComponent {
|
|
2449
|
+
constructor(_graphviz, _settings, _clipboard, _snackbar) {
|
|
2450
|
+
this._graphviz = _graphviz;
|
|
2451
|
+
this._settings = _settings;
|
|
2452
|
+
this._clipboard = _clipboard;
|
|
2453
|
+
this._snackbar = _snackbar;
|
|
2454
|
+
/**
|
|
2455
|
+
* The chain to display.
|
|
2456
|
+
*/
|
|
2457
|
+
this.chain = input();
|
|
2458
|
+
/**
|
|
2459
|
+
* The direction of the graph.
|
|
2460
|
+
*/
|
|
2461
|
+
this.direction = input('LR');
|
|
2462
|
+
/**
|
|
2463
|
+
* All the distinct tags in the chain.
|
|
2464
|
+
*/
|
|
2465
|
+
this.tags = computed(() => {
|
|
2466
|
+
const chain = this.chain();
|
|
2467
|
+
if (!chain) {
|
|
2468
|
+
return [];
|
|
2469
|
+
}
|
|
2470
|
+
const set = new Set();
|
|
2471
|
+
for (const link of chain.links) {
|
|
2472
|
+
set.add(link.tag);
|
|
2473
|
+
}
|
|
2474
|
+
return Array.from(set).sort();
|
|
2475
|
+
});
|
|
2476
|
+
/**
|
|
2477
|
+
* The tags to show, or empty to show all of them.
|
|
2478
|
+
*/
|
|
2479
|
+
this.selectedTags = model([]);
|
|
2480
|
+
/**
|
|
2481
|
+
* The Graphviz representation of the chain.
|
|
2482
|
+
*/
|
|
2483
|
+
this.graph = computed(() => {
|
|
2484
|
+
let tags = this.selectedTags();
|
|
2485
|
+
if (tags && tags.length === 0) {
|
|
2486
|
+
tags = undefined;
|
|
2487
|
+
}
|
|
2488
|
+
return this._graphviz.generateGraph(this.chain(), tags, this.direction());
|
|
2489
|
+
});
|
|
2490
|
+
this.userTags = new FormControl([], {
|
|
2491
|
+
nonNullable: true,
|
|
2492
|
+
});
|
|
2493
|
+
// when the user changes the tags, update the selected tags
|
|
2494
|
+
this._sub = this.userTags.valueChanges.subscribe((value) => {
|
|
2495
|
+
this.selectedTags.set([...value]);
|
|
2496
|
+
});
|
|
2497
|
+
effect(() => {
|
|
2498
|
+
// update the user tags when tags change
|
|
2499
|
+
const tags = this.tags();
|
|
2500
|
+
let userTags = [];
|
|
2501
|
+
if (tags.length > 1) {
|
|
2502
|
+
// pick first and last tag from tags
|
|
2503
|
+
userTags = [tags[0], tags[tags.length - 1]];
|
|
2504
|
+
}
|
|
2505
|
+
else {
|
|
2506
|
+
// if there is only one tag, pick it
|
|
2507
|
+
userTags = [...tags];
|
|
2508
|
+
}
|
|
2509
|
+
this.userTags.setValue(userTags);
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
ngOnDestroy() {
|
|
2513
|
+
this._sub?.unsubscribe();
|
|
2514
|
+
}
|
|
2515
|
+
copyGraph() {
|
|
2516
|
+
const graph = this.graph();
|
|
2517
|
+
if (!graph) {
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
this._clipboard.copy(graph);
|
|
2521
|
+
this._snackbar.open('Graph copied to clipboard', 'OK', {
|
|
2522
|
+
duration: 2000,
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
openExternalEditor() {
|
|
2526
|
+
let url = this._settings.get('graphviz-editor', 'https://dreampuf.github.io/GraphvizOnline?engine=dot#CODE');
|
|
2527
|
+
if (!url) {
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
const graph = this.graph();
|
|
2531
|
+
if (!graph) {
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
if (url.indexOf('CODE') > -1) {
|
|
2535
|
+
url = url.replace('CODE', encodeURIComponent(graph));
|
|
2536
|
+
}
|
|
2537
|
+
window.open(url, '_blank');
|
|
2538
|
+
}
|
|
2539
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainViewComponent, deps: [{ token: GveGraphvizService }, { token: SettingsService }, { token: i2$3.Clipboard }, { token: i4$2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2540
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: ChainViewComponent, isStandalone: true, selector: "gve-chain-view", inputs: { chain: { classPropertyName: "chain", publicName: "chain", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, selectedTags: { classPropertyName: "selectedTags", publicName: "selectedTags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedTags: "selectedTagsChange" }, ngImport: i0, template: "<div id=\"container\">\r\n <div>\r\n <ngx-viz [code]=\"graph()\" />\r\n </div>\r\n <div class=\"button-row\">\r\n <!-- select -->\r\n <mat-form-field>\r\n <mat-label>tags</mat-label>\r\n <mat-select [formControl]=\"userTags\" multiple>\r\n @for (tag of tags(); track tag) {\r\n <mat-option [value]=\"tag\">{{ tag }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- copy -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy graphviz code\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"copyGraph()\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <!--editor -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open in editor\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"openExternalEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["div#container{border:1px solid silver;border-radius:6px;padding:4px}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: "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: 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", "canSelectNullableOptions"], 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"] }, { kind: "component", type: VizComponent, selector: "ngx-viz", inputs: ["code"] }] }); }
|
|
2541
|
+
}
|
|
2542
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ChainViewComponent, decorators: [{
|
|
2543
|
+
type: Component,
|
|
2544
|
+
args: [{ selector: 'gve-chain-view', imports: [
|
|
2545
|
+
CommonModule,
|
|
2546
|
+
ReactiveFormsModule,
|
|
2547
|
+
MatButtonModule,
|
|
2548
|
+
MatFormFieldModule,
|
|
2549
|
+
MatIconModule,
|
|
2550
|
+
MatSelectModule,
|
|
2551
|
+
MatTooltipModule,
|
|
2552
|
+
VizComponent,
|
|
2553
|
+
], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div id=\"container\">\r\n <div>\r\n <ngx-viz [code]=\"graph()\" />\r\n </div>\r\n <div class=\"button-row\">\r\n <!-- select -->\r\n <mat-form-field>\r\n <mat-label>tags</mat-label>\r\n <mat-select [formControl]=\"userTags\" multiple>\r\n @for (tag of tags(); track tag) {\r\n <mat-option [value]=\"tag\">{{ tag }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- copy -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy graphviz code\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"copyGraph()\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <!--editor -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open in editor\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"openExternalEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["div#container{border:1px solid silver;border-radius:6px;padding:4px}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}\n"] }]
|
|
2554
|
+
}], ctorParameters: () => [{ type: GveGraphvizService }, { type: SettingsService }, { type: i2$3.Clipboard }, { type: i4$2.MatSnackBar }] });
|
|
2290
2555
|
|
|
2291
2556
|
/**
|
|
2292
2557
|
* 🔑 `gve-ln-heights-editor`
|
|
@@ -2300,39 +2565,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2300
2565
|
* emitted when the heights change.
|
|
2301
2566
|
*/
|
|
2302
2567
|
class LnHeightsEditorComponent {
|
|
2303
|
-
/**
|
|
2304
|
-
* The total number of lines in the text.
|
|
2305
|
-
*/
|
|
2306
|
-
get lineCount() {
|
|
2307
|
-
return this._lineCount;
|
|
2308
|
-
}
|
|
2309
|
-
set lineCount(value) {
|
|
2310
|
-
if (this._lineCount === value)
|
|
2311
|
-
return;
|
|
2312
|
-
this._lineCount = value;
|
|
2313
|
-
this.lineNumbers = Array.from({ length: value }, (_, i) => i + 1);
|
|
2314
|
-
}
|
|
2315
|
-
/**
|
|
2316
|
-
* The heights map of the lines. Each key is a line number and the value is
|
|
2317
|
-
* the height of the line.
|
|
2318
|
-
*/
|
|
2319
|
-
get heights() {
|
|
2320
|
-
return this._heights;
|
|
2321
|
-
}
|
|
2322
|
-
set heights(value) {
|
|
2323
|
-
if (this._heights === value)
|
|
2324
|
-
return;
|
|
2325
|
-
this._heights = value || undefined;
|
|
2326
|
-
}
|
|
2327
2568
|
constructor(formBuilder) {
|
|
2328
|
-
|
|
2569
|
+
/**
|
|
2570
|
+
* The total number of lines in the text.
|
|
2571
|
+
*/
|
|
2572
|
+
this.lineCount = input(0);
|
|
2573
|
+
/**
|
|
2574
|
+
* The heights map of the lines. Each key is a line number and the value is
|
|
2575
|
+
* the height of the line.
|
|
2576
|
+
*/
|
|
2577
|
+
this.heights = input();
|
|
2329
2578
|
/**
|
|
2330
2579
|
* The event emitted when the heights change.
|
|
2331
2580
|
*/
|
|
2332
|
-
this.heightsChange =
|
|
2581
|
+
this.heightsChange = output();
|
|
2333
2582
|
this.lineNumbers = [];
|
|
2334
2583
|
this.lineNumber = formBuilder.control(0, { nonNullable: true });
|
|
2335
2584
|
this.height = formBuilder.control(0, { nonNullable: true });
|
|
2585
|
+
// when lineCount changes, update lineNumbers
|
|
2586
|
+
effect(() => {
|
|
2587
|
+
this.lineNumbers = Array.from({ length: this.lineCount() }, (_, i) => i + 1);
|
|
2588
|
+
});
|
|
2336
2589
|
}
|
|
2337
2590
|
pruneHeights() {
|
|
2338
2591
|
// remove all the heigths with value=0
|
|
@@ -2371,10 +2624,10 @@ class LnHeightsEditorComponent {
|
|
|
2371
2624
|
this._heights = undefined;
|
|
2372
2625
|
this.heightsChange.emit(undefined);
|
|
2373
2626
|
}
|
|
2374
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
2375
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
2627
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: LnHeightsEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2628
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: LnHeightsEditorComponent, isStandalone: true, selector: "gve-ln-heights-editor", inputs: { lineCount: { classPropertyName: "lineCount", publicName: "lineCount", isSignal: true, isRequired: false, transformFunction: null }, heights: { classPropertyName: "heights", publicName: "heights", isSignal: true, isRequired: false, transformFunction: null } }, 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", "disabledInteractive"], 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", "canSelectNullableOptions"], 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"] }] }); }
|
|
2376
2629
|
}
|
|
2377
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
2630
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: LnHeightsEditorComponent, decorators: [{
|
|
2378
2631
|
type: Component,
|
|
2379
2632
|
args: [{ selector: 'gve-ln-heights-editor', imports: [
|
|
2380
2633
|
CommonModule,
|
|
@@ -2386,76 +2639,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2386
2639
|
MatSelectModule,
|
|
2387
2640
|
MatTooltipModule,
|
|
2388
2641
|
], 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"] }]
|
|
2389
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }]
|
|
2390
|
-
type: Input
|
|
2391
|
-
}], heights: [{
|
|
2392
|
-
type: Input
|
|
2393
|
-
}], heightsChange: [{
|
|
2394
|
-
type: Output
|
|
2395
|
-
}] } });
|
|
2396
|
-
|
|
2397
|
-
/**
|
|
2398
|
-
* 🔑 `gve-simple-tree`
|
|
2399
|
-
*
|
|
2400
|
-
* A simple tree component.
|
|
2401
|
-
*
|
|
2402
|
-
* - ▶️ `node` (`TreeNode<any>`): the node to display.
|
|
2403
|
-
* - ▶️ `selectedNode` (`TreeNode<any> | null`): the selected node.
|
|
2404
|
-
* - 🔥 `selectedNodeChange` (`EventEmitter<TreeNode<any> | null>`): the event
|
|
2405
|
-
* emitted when the selected node changes.
|
|
2406
|
-
*/
|
|
2407
|
-
class SimpleTreeComponent {
|
|
2408
|
-
constructor() {
|
|
2409
|
-
/**
|
|
2410
|
-
* The event emitted when the selected node changes.
|
|
2411
|
-
*/
|
|
2412
|
-
this.selectedNodeChange = new EventEmitter();
|
|
2413
|
-
this.level = 0;
|
|
2414
|
-
}
|
|
2415
|
-
/**
|
|
2416
|
-
* The node to display.
|
|
2417
|
-
*/
|
|
2418
|
-
get node() {
|
|
2419
|
-
return this._node;
|
|
2420
|
-
}
|
|
2421
|
-
set node(value) {
|
|
2422
|
-
if (this._node === value) {
|
|
2423
|
-
return;
|
|
2424
|
-
}
|
|
2425
|
-
this._node = value;
|
|
2426
|
-
this.level = this.calculateLevel(value);
|
|
2427
|
-
}
|
|
2428
|
-
ngOnInit() {
|
|
2429
|
-
this.level = this.calculateLevel(this.node);
|
|
2430
|
-
}
|
|
2431
|
-
calculateLevel(node) {
|
|
2432
|
-
let level = 0;
|
|
2433
|
-
if (node) {
|
|
2434
|
-
let n = node;
|
|
2435
|
-
while (n.parent) {
|
|
2436
|
-
level++;
|
|
2437
|
-
n = n.parent;
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
return level;
|
|
2441
|
-
}
|
|
2442
|
-
selectNode(node) {
|
|
2443
|
-
this.selectedNode = node;
|
|
2444
|
-
this.selectedNodeChange.emit(node);
|
|
2445
|
-
}
|
|
2446
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SimpleTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2447
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", 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 }] }); }
|
|
2448
|
-
}
|
|
2449
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SimpleTreeComponent, decorators: [{
|
|
2450
|
-
type: Component,
|
|
2451
|
-
args: [{ selector: 'gve-simple-tree', 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"] }]
|
|
2452
|
-
}], propDecorators: { node: [{
|
|
2453
|
-
type: Input
|
|
2454
|
-
}], selectedNode: [{
|
|
2455
|
-
type: Input
|
|
2456
|
-
}], selectedNodeChange: [{
|
|
2457
|
-
type: Output
|
|
2458
|
-
}] } });
|
|
2642
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
2459
2643
|
|
|
2460
2644
|
/**
|
|
2461
2645
|
* 🔑 `gve-animation-timeline-set`
|
|
@@ -2470,43 +2654,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2470
2654
|
* - 🔥 `timelinesCancel` (`void`): emitted when the timeline editing is canceled.
|
|
2471
2655
|
*/
|
|
2472
2656
|
class AnimationTimelineSetComponent {
|
|
2473
|
-
/**
|
|
2474
|
-
* The animation timelines to edit.
|
|
2475
|
-
*/
|
|
2476
|
-
get timelines() {
|
|
2477
|
-
return this._timelines;
|
|
2478
|
-
}
|
|
2479
|
-
set timelines(value) {
|
|
2480
|
-
if (this._timelines === value) {
|
|
2481
|
-
return;
|
|
2482
|
-
}
|
|
2483
|
-
this._timelines = value || [];
|
|
2484
|
-
this.updateForm(this._timelines);
|
|
2485
|
-
}
|
|
2486
2657
|
constructor(formBuilder, _dialogService) {
|
|
2487
2658
|
this._dialogService = _dialogService;
|
|
2488
|
-
|
|
2659
|
+
/**
|
|
2660
|
+
* The animation timelines to edit.
|
|
2661
|
+
*/
|
|
2662
|
+
this.timelines = model([]);
|
|
2663
|
+
/**
|
|
2664
|
+
* The IDs of the elements that can be selected by the tween.
|
|
2665
|
+
* This list is used to allow the user to select an element from a dropdown.
|
|
2666
|
+
*/
|
|
2667
|
+
this.elementIds = input();
|
|
2489
2668
|
/**
|
|
2490
2669
|
* The tags that can be used by the timeline.
|
|
2491
2670
|
*/
|
|
2492
|
-
this.tags = [];
|
|
2671
|
+
this.tags = input([]);
|
|
2493
2672
|
/**
|
|
2494
2673
|
* Emitted when the timelines are changed.
|
|
2495
2674
|
*/
|
|
2496
|
-
this.timelinesChange =
|
|
2675
|
+
this.timelinesChange = output();
|
|
2497
2676
|
/**
|
|
2498
2677
|
* Emitted when the timeline editing is canceled.
|
|
2499
2678
|
*/
|
|
2500
|
-
this.timelinesCancel =
|
|
2679
|
+
this.timelinesCancel = output();
|
|
2501
2680
|
this.set = formBuilder.control([], {
|
|
2502
2681
|
nonNullable: true,
|
|
2503
2682
|
});
|
|
2504
2683
|
this.form = formBuilder.group({
|
|
2505
2684
|
sets: this.set,
|
|
2506
2685
|
});
|
|
2686
|
+
effect(() => {
|
|
2687
|
+
this.updateForm(this.timelines());
|
|
2688
|
+
});
|
|
2507
2689
|
}
|
|
2508
2690
|
updateForm(animations) {
|
|
2509
|
-
if (!animations
|
|
2691
|
+
if (!animations?.length) {
|
|
2510
2692
|
this.form.reset();
|
|
2511
2693
|
}
|
|
2512
2694
|
else {
|
|
@@ -2524,7 +2706,7 @@ class AnimationTimelineSetComponent {
|
|
|
2524
2706
|
};
|
|
2525
2707
|
}
|
|
2526
2708
|
editTimeline(index) {
|
|
2527
|
-
this.editedTimeline = this.
|
|
2709
|
+
this.editedTimeline = this.timelines()[index];
|
|
2528
2710
|
}
|
|
2529
2711
|
onTimelineChange(timeline) {
|
|
2530
2712
|
const timelines = [...this.set.value];
|
|
@@ -2541,8 +2723,8 @@ class AnimationTimelineSetComponent {
|
|
|
2541
2723
|
this.set.setValue(timelines);
|
|
2542
2724
|
this.set.markAsDirty();
|
|
2543
2725
|
this.set.updateValueAndValidity();
|
|
2544
|
-
this.
|
|
2545
|
-
this.timelinesChange.emit(this.
|
|
2726
|
+
this.timelines.set(this.set.value);
|
|
2727
|
+
this.timelinesChange.emit(this.timelines());
|
|
2546
2728
|
}
|
|
2547
2729
|
deleteTimeline(index) {
|
|
2548
2730
|
this._dialogService
|
|
@@ -2554,15 +2736,15 @@ class AnimationTimelineSetComponent {
|
|
|
2554
2736
|
this.set.setValue(timelines);
|
|
2555
2737
|
this.set.markAsDirty();
|
|
2556
2738
|
this.set.updateValueAndValidity();
|
|
2557
|
-
this.
|
|
2558
|
-
this.timelinesChange.emit(this.
|
|
2739
|
+
this.timelines.set(this.set.value);
|
|
2740
|
+
this.timelinesChange.emit(this.timelines());
|
|
2559
2741
|
}
|
|
2560
2742
|
});
|
|
2561
2743
|
}
|
|
2562
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
2563
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.
|
|
2744
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTimelineSetComponent, deps: [{ token: i1.FormBuilder }, { token: i2$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2745
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: AnimationTimelineSetComponent, isStandalone: true, selector: "gve-animation-timeline-set", inputs: { timelines: { classPropertyName: "timelines", publicName: "timelines", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelines: "timelinesChange", 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: "component", type: AnimationTimelineComponent, selector: "gve-animation-timeline", inputs: ["timeline", "elementIds", "tags"], outputs: ["timelineChange", "timelineCancel"] }] }); }
|
|
2564
2746
|
}
|
|
2565
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
2747
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: AnimationTimelineSetComponent, decorators: [{
|
|
2566
2748
|
type: Component,
|
|
2567
2749
|
args: [{ selector: 'gve-animation-timeline-set', imports: [
|
|
2568
2750
|
CommonModule,
|
|
@@ -2576,20 +2758,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
2576
2758
|
MatSelectModule,
|
|
2577
2759
|
MatTabsModule,
|
|
2578
2760
|
MatTooltipModule,
|
|
2579
|
-
NgToolsModule,
|
|
2580
2761
|
AnimationTimelineComponent,
|
|
2581
|
-
], 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"] }]
|
|
2582
|
-
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2762
|
+
], 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"] }]
|
|
2763
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$1.DialogService }] });
|
|
2764
|
+
|
|
2765
|
+
/**
|
|
2766
|
+
* 🔑 `gve-snapshot-text-editor`
|
|
2767
|
+
*
|
|
2768
|
+
* A component to edit a snapshot text. This component can be used both in a dialog
|
|
2769
|
+
* and as a standalone component.
|
|
2770
|
+
*
|
|
2771
|
+
* - ▶️ `text` (`CharNode[]`): the text to edit.
|
|
2772
|
+
* - 🔥 `textChange` (`CharNode[]`): event emitted when text changes.
|
|
2773
|
+
*/
|
|
2774
|
+
class SnapshotTextEditorComponent {
|
|
2775
|
+
constructor(formBuilder,
|
|
2776
|
+
// this component can be used as a dialog
|
|
2777
|
+
dialogRef, data) {
|
|
2778
|
+
this.dialogRef = dialogRef;
|
|
2779
|
+
this.data = data;
|
|
2780
|
+
this.text = model([]);
|
|
2781
|
+
/**
|
|
2782
|
+
* Emitted when text changes.
|
|
2783
|
+
*/
|
|
2784
|
+
this.textChange = output();
|
|
2785
|
+
this.userText = formBuilder.control('', {
|
|
2786
|
+
nonNullable: true,
|
|
2787
|
+
validators: [Validators.required, Validators.maxLength(5000)],
|
|
2788
|
+
});
|
|
2789
|
+
this.form = new FormGroup({
|
|
2790
|
+
userText: this.userText,
|
|
2791
|
+
});
|
|
2792
|
+
// when text changes, update the user text
|
|
2793
|
+
effect(() => {
|
|
2794
|
+
this.updateUserText(this.text());
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
updateUserText(nodes) {
|
|
2798
|
+
this.userText.setValue(nodes.map((node) => node.label).join(''));
|
|
2799
|
+
this.userText.markAsPristine();
|
|
2800
|
+
this.userText.updateValueAndValidity();
|
|
2801
|
+
}
|
|
2802
|
+
ngOnInit() {
|
|
2803
|
+
if (this.data?.payload?.text) {
|
|
2804
|
+
this.text.set(this.data.payload.text);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
close() {
|
|
2808
|
+
this.dialogRef?.close();
|
|
2809
|
+
}
|
|
2810
|
+
save() {
|
|
2811
|
+
if (this.form.invalid) {
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
this.userText.setValue(this.userText.value.trim());
|
|
2815
|
+
if (!this.userText.value) {
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
this.text.set(SnapshotViewService.stringToBaseChars(this.userText.value));
|
|
2819
|
+
this.textChange.emit(this.text());
|
|
2820
|
+
this.dialogRef?.close(this.text());
|
|
2821
|
+
}
|
|
2822
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SnapshotTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i3$1.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2823
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SnapshotTextEditorComponent, isStandalone: true, selector: "gve-snapshot-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { text: "textChange", textChange: "textChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Set Base Text</h2>\r\n </div>\r\n }\r\n <form [formGroup]=\"form\" (submit)=\"save()\">\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>base text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"8\"></textarea>\r\n @if ($any(userText).errors?.required && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text required</mat-error>\r\n } @if ($any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\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=\"Set base text\"\r\n [disabled]=\"form.invalid\"\r\n (click)=\"save()\"\r\n >\r\n set\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".full-width{width:100%}.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}\n"], dependencies: [{ 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", "disabledInteractive"], exportAs: ["matInput"] }] }); }
|
|
2824
|
+
}
|
|
2825
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SnapshotTextEditorComponent, decorators: [{
|
|
2826
|
+
type: Component,
|
|
2827
|
+
args: [{ selector: 'gve-snapshot-text-editor', imports: [
|
|
2828
|
+
ReactiveFormsModule,
|
|
2829
|
+
MatButtonModule,
|
|
2830
|
+
MatFormFieldModule,
|
|
2831
|
+
MatIconModule,
|
|
2832
|
+
MatInputModule,
|
|
2833
|
+
], template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Set Base Text</h2>\r\n </div>\r\n }\r\n <form [formGroup]=\"form\" (submit)=\"save()\">\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>base text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"8\"></textarea>\r\n @if ($any(userText).errors?.required && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text required</mat-error>\r\n } @if ($any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\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=\"Set base text\"\r\n [disabled]=\"form.invalid\"\r\n (click)=\"save()\"\r\n >\r\n set\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".full-width{width:100%}.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}\n"] }]
|
|
2834
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i3$1.MatDialogRef, decorators: [{
|
|
2835
|
+
type: Optional
|
|
2836
|
+
}] }, { type: undefined, decorators: [{
|
|
2837
|
+
type: Optional
|
|
2838
|
+
}, {
|
|
2839
|
+
type: Inject,
|
|
2840
|
+
args: [MAT_DIALOG_DATA]
|
|
2841
|
+
}] }] });
|
|
2593
2842
|
|
|
2594
2843
|
// default snapshot view title
|
|
2595
2844
|
const VIEW_TITLE = 'preview';
|
|
@@ -2608,36 +2857,36 @@ const SVG_TRANSP_SUFFIX = '_t';
|
|
|
2608
2857
|
* - 🔥 `snapshotCancel` (`void`): emitted when the user cancels the snapshot editing.
|
|
2609
2858
|
*/
|
|
2610
2859
|
class SnapshotEditorComponent {
|
|
2611
|
-
/**
|
|
2612
|
-
* The snapshot to edit.
|
|
2613
|
-
*/
|
|
2614
|
-
get snapshot() {
|
|
2615
|
-
return this._snapshot;
|
|
2616
|
-
}
|
|
2617
|
-
set snapshot(value) {
|
|
2618
|
-
if (this._snapshot === value) {
|
|
2619
|
-
return;
|
|
2620
|
-
}
|
|
2621
|
-
this._snapshot = value || undefined;
|
|
2622
|
-
this.updateForm(this._snapshot);
|
|
2623
|
-
setTimeout(() => {
|
|
2624
|
-
this.runToLast();
|
|
2625
|
-
}, 0);
|
|
2626
|
-
}
|
|
2627
2860
|
constructor(formBuilder, _api, _dialog, _dialogService, _snackbar) {
|
|
2628
2861
|
this._api = _api;
|
|
2629
2862
|
this._dialog = _dialog;
|
|
2630
2863
|
this._dialogService = _dialogService;
|
|
2631
2864
|
this._snackbar = _snackbar;
|
|
2632
2865
|
this._nanoid = customAlphabet('1234567890abcdef', 10);
|
|
2866
|
+
/**
|
|
2867
|
+
* The snapshot to edit.
|
|
2868
|
+
*/
|
|
2869
|
+
this.snapshot = model();
|
|
2870
|
+
/**
|
|
2871
|
+
* The batch operations text to set for the editor.
|
|
2872
|
+
*/
|
|
2873
|
+
this.batchOps = input();
|
|
2874
|
+
/**
|
|
2875
|
+
* True to disable saving.
|
|
2876
|
+
*/
|
|
2877
|
+
this.noSave = input();
|
|
2878
|
+
/**
|
|
2879
|
+
* True to enable debug mode for view rendition.
|
|
2880
|
+
*/
|
|
2881
|
+
this.debug = input();
|
|
2633
2882
|
/**
|
|
2634
2883
|
* Emitted when the user saves the edited snapshot.
|
|
2635
2884
|
*/
|
|
2636
|
-
this.snapshotChange =
|
|
2885
|
+
this.snapshotChange = output();
|
|
2637
2886
|
/**
|
|
2638
2887
|
* Emitted when the user cancels the snapshot editing.
|
|
2639
2888
|
*/
|
|
2640
|
-
this.snapshotCancel =
|
|
2889
|
+
this.snapshotCancel = output();
|
|
2641
2890
|
// list of operations output tags
|
|
2642
2891
|
this.opTags = [];
|
|
2643
2892
|
// list of operation diplomatic.g element IDs
|
|
@@ -2659,6 +2908,7 @@ class SnapshotEditorComponent {
|
|
|
2659
2908
|
this.viewTitle = VIEW_TITLE;
|
|
2660
2909
|
this.rulers = true;
|
|
2661
2910
|
this.initialStepIndex = -1;
|
|
2911
|
+
this.showChain = new FormControl(false, { nonNullable: true });
|
|
2662
2912
|
// general
|
|
2663
2913
|
this.width = new FormControl(800, { nonNullable: true });
|
|
2664
2914
|
this.height = new FormControl(600, { nonNullable: true });
|
|
@@ -2718,6 +2968,13 @@ class SnapshotEditorComponent {
|
|
|
2718
2968
|
// timelines
|
|
2719
2969
|
timelines: this.timelines,
|
|
2720
2970
|
});
|
|
2971
|
+
// when snapshot changes, update the form
|
|
2972
|
+
effect(() => {
|
|
2973
|
+
this.updateForm(this.snapshot());
|
|
2974
|
+
setTimeout(() => {
|
|
2975
|
+
this.runToLast();
|
|
2976
|
+
}, 0);
|
|
2977
|
+
});
|
|
2721
2978
|
}
|
|
2722
2979
|
/**
|
|
2723
2980
|
* Set the view data for the snapshot view.
|
|
@@ -2738,7 +2995,7 @@ class SnapshotEditorComponent {
|
|
|
2738
2995
|
this.viewData = {
|
|
2739
2996
|
snapshot: snapshot,
|
|
2740
2997
|
options: {
|
|
2741
|
-
debug: this.debug,
|
|
2998
|
+
debug: this.debug(),
|
|
2742
2999
|
delayedRender: true,
|
|
2743
3000
|
showRulers: true,
|
|
2744
3001
|
showGrid: true,
|
|
@@ -2765,7 +3022,12 @@ class SnapshotEditorComponent {
|
|
|
2765
3022
|
this.imageWidth.setValue(snapshot.image?.canvas?.width || 0);
|
|
2766
3023
|
this.imageHeight.setValue(snapshot.image?.canvas?.height || 0);
|
|
2767
3024
|
this.defs.setValue(snapshot.defs || null);
|
|
2768
|
-
|
|
3025
|
+
if (Array.isArray(snapshot.text)) {
|
|
3026
|
+
this.baseText.setValue(snapshot.text);
|
|
3027
|
+
}
|
|
3028
|
+
else {
|
|
3029
|
+
this.baseText.setValue(SnapshotViewService.stringToBaseChars(snapshot.text));
|
|
3030
|
+
}
|
|
2769
3031
|
this.offsetX.setValue(snapshot.textOptions?.offset?.x || 0);
|
|
2770
3032
|
this.offsetY.setValue(snapshot.textOptions?.offset?.y || 0);
|
|
2771
3033
|
this.lineHeightOffset.setValue(snapshot.textOptions?.lineHeightOffset);
|
|
@@ -2789,6 +3051,30 @@ class SnapshotEditorComponent {
|
|
|
2789
3051
|
this.form.markAsPristine();
|
|
2790
3052
|
}
|
|
2791
3053
|
// #region base text
|
|
3054
|
+
inputBaseText() {
|
|
3055
|
+
if (this.busy) {
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
const dialogRef = this._dialog.open(SnapshotTextEditorComponent, {
|
|
3059
|
+
data: { payload: { text: this.baseText.value } },
|
|
3060
|
+
width: '80%',
|
|
3061
|
+
});
|
|
3062
|
+
dialogRef.afterClosed().subscribe((text) => {
|
|
3063
|
+
if (text?.length) {
|
|
3064
|
+
// update the form control and reset the current text range
|
|
3065
|
+
this.baseText.setValue(text);
|
|
3066
|
+
this.baseText.updateValueAndValidity();
|
|
3067
|
+
this.baseText.markAsDirty();
|
|
3068
|
+
this.textRange = undefined;
|
|
3069
|
+
// update the line count
|
|
3070
|
+
this.updateLineCount(text);
|
|
3071
|
+
// remove all operations and update the view data
|
|
3072
|
+
this.removeAllOperations();
|
|
3073
|
+
// remove all timelines
|
|
3074
|
+
this.timelines.reset();
|
|
3075
|
+
}
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
2792
3078
|
/**
|
|
2793
3079
|
* Update the line count based on the received text.
|
|
2794
3080
|
* @param text The text to use for counting lines.
|
|
@@ -2808,24 +3094,6 @@ class SnapshotEditorComponent {
|
|
|
2808
3094
|
onRangePick(range) {
|
|
2809
3095
|
this.textRange = range;
|
|
2810
3096
|
}
|
|
2811
|
-
/**
|
|
2812
|
-
* Handle the event fired by the base text editor to change the base text.
|
|
2813
|
-
* @param text The text to set.
|
|
2814
|
-
*/
|
|
2815
|
-
onTextChange(text) {
|
|
2816
|
-
console.log('text change', text);
|
|
2817
|
-
// update the form control and reset the current text range
|
|
2818
|
-
this.baseText.setValue(text);
|
|
2819
|
-
this.baseText.updateValueAndValidity();
|
|
2820
|
-
this.baseText.markAsDirty();
|
|
2821
|
-
this.textRange = undefined;
|
|
2822
|
-
// update the line count
|
|
2823
|
-
this.updateLineCount(text);
|
|
2824
|
-
// remove all operations and update the view data
|
|
2825
|
-
this.removeAllOperations();
|
|
2826
|
-
// remove all timelines
|
|
2827
|
-
this.timelines.reset();
|
|
2828
|
-
}
|
|
2829
3097
|
// #endregion
|
|
2830
3098
|
// #region operations
|
|
2831
3099
|
/**
|
|
@@ -2890,7 +3158,10 @@ class SnapshotEditorComponent {
|
|
|
2890
3158
|
* was a new operation.
|
|
2891
3159
|
* @param op The changed operation.
|
|
2892
3160
|
*/
|
|
2893
|
-
|
|
3161
|
+
onOperationChange(op) {
|
|
3162
|
+
if (!op) {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
2894
3165
|
console.log('operation change');
|
|
2895
3166
|
const operations = [...this.operations.value];
|
|
2896
3167
|
// replace or add the operation
|
|
@@ -2908,8 +3179,8 @@ class SnapshotEditorComponent {
|
|
|
2908
3179
|
this.operations.updateValueAndValidity();
|
|
2909
3180
|
// update the operation lists
|
|
2910
3181
|
this.updateOpLists();
|
|
2911
|
-
//
|
|
2912
|
-
this.
|
|
3182
|
+
// run to the edited operation
|
|
3183
|
+
this.runTo(i);
|
|
2913
3184
|
// close the edited operation
|
|
2914
3185
|
this.closeEditedOperation();
|
|
2915
3186
|
}
|
|
@@ -2972,6 +3243,9 @@ class SnapshotEditorComponent {
|
|
|
2972
3243
|
}
|
|
2973
3244
|
});
|
|
2974
3245
|
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Remove all the operations, close the edited operation and update the view data.
|
|
3248
|
+
*/
|
|
2975
3249
|
removeAllOperations() {
|
|
2976
3250
|
this.resultOperationId = undefined;
|
|
2977
3251
|
this.closeEditedOperation();
|
|
@@ -3020,6 +3294,53 @@ class SnapshotEditorComponent {
|
|
|
3020
3294
|
}
|
|
3021
3295
|
return this.parseSvgIds(g)?.filter((id) => id.endsWith(SVG_TRANSP_SUFFIX));
|
|
3022
3296
|
}
|
|
3297
|
+
/**
|
|
3298
|
+
* Supply operation nodes to the snapshot text nodes. An execution result is
|
|
3299
|
+
* a compact object, so that to build a snapshot accumulating all the nodes
|
|
3300
|
+
* and their features up to the last executed operation, we need to start
|
|
3301
|
+
* with the base text nodes, and then examine each execution step in the
|
|
3302
|
+
* result. Each step contains only those nodes used by the version of the
|
|
3303
|
+
* text corresponding to its stage. Among them, some are new nodes, and some
|
|
3304
|
+
* are base text nodes. Also, the step contains the full set of features
|
|
3305
|
+
* current at its time, both for the text as a whole and for each node.
|
|
3306
|
+
* This method accumulates the nodes and their features up to the specified
|
|
3307
|
+
* step, updating the features of the nodes already present in the snapshot
|
|
3308
|
+
* text nodes, and adding the new nodes.
|
|
3309
|
+
*
|
|
3310
|
+
* @param snapshot The snapshot to supply the nodes to.
|
|
3311
|
+
* @param result The operations execution result.
|
|
3312
|
+
* @param tag The output tag of the operation to supply the nodes from.
|
|
3313
|
+
*/
|
|
3314
|
+
supplyOpNodes(snapshot, result, tag) {
|
|
3315
|
+
// get the original nodes from the snapshot base text
|
|
3316
|
+
const nodes = snapshot.text;
|
|
3317
|
+
// find the max ID in the original nodes, so that any ID greater than it
|
|
3318
|
+
// belongs to new nodes
|
|
3319
|
+
const maxOriginalId = nodes.reduce((max, n) => Math.max(max, n.id), 0);
|
|
3320
|
+
const stepIndex = result.steps.findIndex((s) => s.outputTag === tag);
|
|
3321
|
+
// for each step up to the specified one, add the nodes introduced by the step
|
|
3322
|
+
// and update the features from each step node that is already present
|
|
3323
|
+
for (let i = 0; i <= stepIndex; i++) {
|
|
3324
|
+
// get the nodes of the step
|
|
3325
|
+
const stepNodes = result.taggedNodes[result.steps[i].outputTag];
|
|
3326
|
+
for (const stepNode of stepNodes) {
|
|
3327
|
+
// if the node was added, add it
|
|
3328
|
+
if (stepNode.id > maxOriginalId &&
|
|
3329
|
+
!nodes.find((n) => n.id === stepNode.id)) {
|
|
3330
|
+
nodes.push(stepNode);
|
|
3331
|
+
}
|
|
3332
|
+
else {
|
|
3333
|
+
// if the node was already in the original nodes, update its features
|
|
3334
|
+
const existing = nodes.findIndex((n) => n.id === stepNode.id);
|
|
3335
|
+
if (existing > -1) {
|
|
3336
|
+
nodes[existing].features = stepNode.features;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
// update the snapshot text nodes
|
|
3342
|
+
snapshot.text = nodes;
|
|
3343
|
+
}
|
|
3023
3344
|
/**
|
|
3024
3345
|
* Run the operations up to the specified index (included).
|
|
3025
3346
|
* This is called when:
|
|
@@ -3035,63 +3356,81 @@ class SnapshotEditorComponent {
|
|
|
3035
3356
|
* @param lastOperation The operation to use in place of the existing
|
|
3036
3357
|
* operation in the snapshot at index. This is used when previewing
|
|
3037
3358
|
* the edited operation from within the operation editor.
|
|
3359
|
+
* @returns A promise that resolves to the execution result.
|
|
3038
3360
|
*/
|
|
3039
3361
|
runTo(index, lastOperation) {
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
snapshot.operations =
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3362
|
+
if (this.busy) {
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
console.log('run to: ' + index);
|
|
3366
|
+
// get the snapshot to run operations on
|
|
3367
|
+
const snapshot = this.getSnapshot();
|
|
3368
|
+
if (!snapshot.text) {
|
|
3369
|
+
return;
|
|
3370
|
+
}
|
|
3371
|
+
// remove from the snapshot the operations past the specified index,
|
|
3372
|
+
// also replacing the last operation when this was received as a parameter
|
|
3373
|
+
if (lastOperation) {
|
|
3374
|
+
snapshot.operations = snapshot.operations.slice(0, index);
|
|
3375
|
+
snapshot.operations.push(lastOperation);
|
|
3376
|
+
}
|
|
3377
|
+
else {
|
|
3378
|
+
lastOperation = snapshot.operations[index];
|
|
3379
|
+
snapshot.operations = snapshot.operations.slice(0, index + 1);
|
|
3380
|
+
}
|
|
3381
|
+
// update result operation ID
|
|
3382
|
+
this.resultOperationId = snapshot.operations[index].id;
|
|
3383
|
+
// extract the IDs from the last operation's diplomatics and filter
|
|
3384
|
+
// them so that only the ones ending with _t are kept. By convention,
|
|
3385
|
+
// all the IDs ending with this suffix are to be made invisible at
|
|
3386
|
+
// their first rendition (opacity=0). An animation will then make
|
|
3387
|
+
// them visible.
|
|
3388
|
+
this._transparentIds = this.getTransparentIds(snapshot.operations[index].diplomatics?.g);
|
|
3389
|
+
// run the operations
|
|
3390
|
+
this.busy = true;
|
|
3391
|
+
this.initialStepIndex = index;
|
|
3392
|
+
this._api
|
|
3393
|
+
.runOperations(snapshot.text, snapshot.operations)
|
|
3394
|
+
.subscribe({
|
|
3395
|
+
next: (wrapper) => {
|
|
3396
|
+
// handle operation (non-fatal) error or result
|
|
3397
|
+
if (wrapper.error) {
|
|
3398
|
+
this._snackbar.open(wrapper.error, 'OK');
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
// set the result
|
|
3402
|
+
console.log('result:', wrapper.result);
|
|
3403
|
+
this.result = wrapper.result;
|
|
3404
|
+
// supply the last operation output tag from its result step if not set
|
|
3405
|
+
if (!lastOperation.outputTag) {
|
|
3406
|
+
const step = wrapper.result.steps.find((s) => s.operation.id === lastOperation.id);
|
|
3407
|
+
lastOperation.outputTag = step.outputTag;
|
|
3408
|
+
}
|
|
3409
|
+
// update the snapshot text nodes with those introduced by operations
|
|
3410
|
+
this.supplyOpNodes(snapshot, wrapper.result, lastOperation.outputTag);
|
|
3411
|
+
// update the view data
|
|
3412
|
+
this.setViewData(snapshot, lastOperation.outputTag);
|
|
3413
|
+
// play animation if any
|
|
3414
|
+
const tl = this.timelines.value.find((t) => t.tag === lastOperation.outputTag);
|
|
3415
|
+
if (tl) {
|
|
3416
|
+
// play the timeline
|
|
3417
|
+
setTimeout(() => {
|
|
3418
|
+
this.playTimeline(tl);
|
|
3419
|
+
}, 0);
|
|
3420
|
+
}
|
|
3421
|
+
// get the chain model if requested
|
|
3422
|
+
if (this.showChain.value) {
|
|
3423
|
+
this.getChainAt(index);
|
|
3424
|
+
}
|
|
3425
|
+
},
|
|
3426
|
+
error: (error) => {
|
|
3427
|
+
console.error(error);
|
|
3428
|
+
this._transparentIds = undefined;
|
|
3429
|
+
this._snackbar.open('Error running operations', 'OK');
|
|
3430
|
+
},
|
|
3431
|
+
complete: () => {
|
|
3432
|
+
this.busy = false;
|
|
3433
|
+
},
|
|
3095
3434
|
});
|
|
3096
3435
|
}
|
|
3097
3436
|
/**
|
|
@@ -3099,15 +3438,55 @@ class SnapshotEditorComponent {
|
|
|
3099
3438
|
* execution result. The execution result is always the result from
|
|
3100
3439
|
* all the operations, as users must be able to browse across its steps.
|
|
3101
3440
|
*/
|
|
3102
|
-
|
|
3441
|
+
runToLast() {
|
|
3103
3442
|
if (this.operations.value.length) {
|
|
3104
|
-
this.
|
|
3443
|
+
this.runTo(this.operations.value.length - 1);
|
|
3105
3444
|
}
|
|
3106
3445
|
else {
|
|
3107
3446
|
this.setViewData();
|
|
3108
3447
|
this.result = undefined;
|
|
3109
3448
|
}
|
|
3110
3449
|
}
|
|
3450
|
+
getChainAt(index, lastOperation) {
|
|
3451
|
+
// get the snapshot to run operations on
|
|
3452
|
+
const snapshot = this.getSnapshot();
|
|
3453
|
+
if (!snapshot.text) {
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
// remove from the snapshot the operations past the specified index,
|
|
3457
|
+
// also replacing the last operation when this was received as a parameter
|
|
3458
|
+
if (lastOperation) {
|
|
3459
|
+
snapshot.operations = snapshot.operations.slice(0, index);
|
|
3460
|
+
snapshot.operations.push(lastOperation);
|
|
3461
|
+
}
|
|
3462
|
+
else {
|
|
3463
|
+
lastOperation = snapshot.operations[index];
|
|
3464
|
+
snapshot.operations = snapshot.operations.slice(0, index + 1);
|
|
3465
|
+
}
|
|
3466
|
+
this.busy = true;
|
|
3467
|
+
this._api
|
|
3468
|
+
.getChain(snapshot.text, snapshot.operations)
|
|
3469
|
+
.subscribe({
|
|
3470
|
+
next: (wrapper) => {
|
|
3471
|
+
// handle operation (non-fatal) error or result
|
|
3472
|
+
if (wrapper.error) {
|
|
3473
|
+
this._snackbar.open(wrapper.error, 'OK');
|
|
3474
|
+
this.chain = undefined;
|
|
3475
|
+
}
|
|
3476
|
+
else {
|
|
3477
|
+
this.chain = wrapper.result;
|
|
3478
|
+
}
|
|
3479
|
+
},
|
|
3480
|
+
error: (error) => {
|
|
3481
|
+
console.error(error);
|
|
3482
|
+
this._transparentIds = undefined;
|
|
3483
|
+
this._snackbar.open('Error running operations', 'OK');
|
|
3484
|
+
},
|
|
3485
|
+
complete: () => {
|
|
3486
|
+
this.busy = false;
|
|
3487
|
+
},
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
3111
3490
|
/**
|
|
3112
3491
|
* Update the snapshot view by running the operations up to the
|
|
3113
3492
|
* currently edited operation if any.
|
|
@@ -3126,64 +3505,6 @@ class SnapshotEditorComponent {
|
|
|
3126
3505
|
}, 0);
|
|
3127
3506
|
}
|
|
3128
3507
|
// #endregion
|
|
3129
|
-
/**
|
|
3130
|
-
* Build the snapshot at the specified execution step. This implies adding
|
|
3131
|
-
* the nodes introduced by the operations up to the specified step, and
|
|
3132
|
-
* updating the features of the nodes introduced by the specified step.
|
|
3133
|
-
* Also, the operations after the specified step are removed from the
|
|
3134
|
-
* snapshot.
|
|
3135
|
-
*
|
|
3136
|
-
* @param step The step to build the snapshot at.
|
|
3137
|
-
* @returns The snapshot at the specified step.
|
|
3138
|
-
*/
|
|
3139
|
-
buildSnapshotAtStep(step) {
|
|
3140
|
-
// no result, nothing to do
|
|
3141
|
-
if (!this.result) {
|
|
3142
|
-
return null;
|
|
3143
|
-
}
|
|
3144
|
-
// get the currently edited snapshot
|
|
3145
|
-
const snapshot = this.getSnapshot();
|
|
3146
|
-
// find the max ID in snapshot.text (=original) nodes,
|
|
3147
|
-
// so that any ID greater than it belongs to new nodes
|
|
3148
|
-
const maxOriginalId = snapshot.text.reduce((max, n) => Math.max(max, n.id), 0);
|
|
3149
|
-
// get a copy of the nodes of the snapshot base text
|
|
3150
|
-
const snapNodes = deepCopy(snapshot.text);
|
|
3151
|
-
// get the nodes of the picked step
|
|
3152
|
-
const stepNodes = this.result.taggedNodes[step.outputTag];
|
|
3153
|
-
// update the snapshot text nodes copies with the picked step nodes,
|
|
3154
|
-
// by updating their features if existing, or adding new nodes if not.
|
|
3155
|
-
for (const sn of stepNodes) {
|
|
3156
|
-
// find the node in the original snapshot text
|
|
3157
|
-
const i = snapNodes.findIndex((n) => n.id === sn.id);
|
|
3158
|
-
// if found, update its features, else add it
|
|
3159
|
-
if (i > -1) {
|
|
3160
|
-
snapNodes[i].features = sn.features;
|
|
3161
|
-
}
|
|
3162
|
-
else {
|
|
3163
|
-
// new node: we assume that it has a manually set position (x,y features)
|
|
3164
|
-
snapNodes.push(sn);
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
|
-
// append nodes introduced *before* the picked step, if any
|
|
3168
|
-
// (those introduced by the picked step have already been added)
|
|
3169
|
-
const stepIndex = this.result.steps.indexOf(step);
|
|
3170
|
-
for (let i = 0; i < stepIndex; i++) {
|
|
3171
|
-
const stepNodes = this.result.taggedNodes[this.result.steps[i].outputTag];
|
|
3172
|
-
for (const n of stepNodes) {
|
|
3173
|
-
if (n.id > maxOriginalId && !snapNodes.find((x) => x.id === n.id)) {
|
|
3174
|
-
snapNodes.push(n);
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
// update the snapshot text nodes
|
|
3179
|
-
snapshot.text = snapNodes;
|
|
3180
|
-
// remove from the snapshot the operations after the picked step
|
|
3181
|
-
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
3182
|
-
if (i > -1) {
|
|
3183
|
-
snapshot.operations = snapshot.operations.slice(0, i + 1);
|
|
3184
|
-
}
|
|
3185
|
-
return snapshot;
|
|
3186
|
-
}
|
|
3187
3508
|
playTimeline(tl) {
|
|
3188
3509
|
const shadowRoot = this.snapshotView?.nativeElement?.shadowRoot;
|
|
3189
3510
|
if (tl && shadowRoot) {
|
|
@@ -3204,28 +3525,32 @@ class SnapshotEditorComponent {
|
|
|
3204
3525
|
*
|
|
3205
3526
|
* @param step The step to pick.
|
|
3206
3527
|
*/
|
|
3207
|
-
|
|
3528
|
+
onStepPick(step) {
|
|
3208
3529
|
if (!this.result || this._stepPickFrozen) {
|
|
3209
3530
|
return;
|
|
3210
3531
|
}
|
|
3211
|
-
// get
|
|
3212
|
-
const snapshot = this.
|
|
3532
|
+
// get base text snapshot
|
|
3533
|
+
const snapshot = this.getSnapshot();
|
|
3534
|
+
// get the step index
|
|
3535
|
+
const index = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
3536
|
+
// remove snapshot operations after the picked step to avoid
|
|
3537
|
+
// their visuals creep into the snapshot view
|
|
3538
|
+
snapshot.operations = snapshot.operations.slice(0, index + 1);
|
|
3213
3539
|
// update result operation ID
|
|
3214
|
-
this.resultOperationId =
|
|
3215
|
-
//
|
|
3216
|
-
|
|
3217
|
-
//
|
|
3540
|
+
this.resultOperationId = snapshot.operations[index].id;
|
|
3541
|
+
// extract the IDs from the last operation's diplomatics and filter
|
|
3542
|
+
// them so that only the ones ending with _t are kept. By convention,
|
|
3543
|
+
// all the IDs ending with this suffix are to be made invisible at
|
|
3544
|
+
// their first rendition (opacity=0). An animation will then make
|
|
3545
|
+
// them visible.
|
|
3546
|
+
this._transparentIds = this.getTransparentIds(snapshot.operations[index].diplomatics?.g);
|
|
3547
|
+
// update the snapshot text nodes with those introduced by operations
|
|
3548
|
+
this.supplyOpNodes(snapshot, this.result, step.outputTag);
|
|
3549
|
+
// update the view data
|
|
3218
3550
|
this.setViewData(snapshot, step.outputTag);
|
|
3219
3551
|
// play animation if any
|
|
3220
3552
|
const tl = this.timelines.value.find((t) => t.tag === step.outputTag);
|
|
3221
3553
|
if (tl) {
|
|
3222
|
-
this._stepPickFrozen = true;
|
|
3223
|
-
// find the index of the picked step in the snapshot operations
|
|
3224
|
-
const i = snapshot.operations.findIndex((o) => o.id === step.operation.id);
|
|
3225
|
-
// run the operations up to the picked step as we need to hide
|
|
3226
|
-
// those visuals to be revealed by the animation
|
|
3227
|
-
await this.runTo(i);
|
|
3228
|
-
this._stepPickFrozen = false;
|
|
3229
3554
|
// play the timeline
|
|
3230
3555
|
setTimeout(() => {
|
|
3231
3556
|
this.playTimeline(tl);
|
|
@@ -3278,6 +3603,9 @@ class SnapshotEditorComponent {
|
|
|
3278
3603
|
* @param timelines The timelines.
|
|
3279
3604
|
*/
|
|
3280
3605
|
onTimelinesChange(timelines) {
|
|
3606
|
+
if (!timelines) {
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3281
3609
|
this.timelines.setValue(timelines);
|
|
3282
3610
|
this.timelines.markAsDirty();
|
|
3283
3611
|
this.timelines.updateValueAndValidity();
|
|
@@ -3297,6 +3625,36 @@ class SnapshotEditorComponent {
|
|
|
3297
3625
|
onSnapshotRender(event) {
|
|
3298
3626
|
this._renderer = event.detail.renderer;
|
|
3299
3627
|
}
|
|
3628
|
+
onImageLoad(imageElement) {
|
|
3629
|
+
const size = {
|
|
3630
|
+
width: imageElement.naturalWidth,
|
|
3631
|
+
height: imageElement.naturalHeight,
|
|
3632
|
+
};
|
|
3633
|
+
if (!this.imageWidth.value) {
|
|
3634
|
+
this.imageWidth.setValue(size.width);
|
|
3635
|
+
this.imageWidth.updateValueAndValidity();
|
|
3636
|
+
this.imageWidth.markAsDirty();
|
|
3637
|
+
}
|
|
3638
|
+
if (!this.imageHeight.value) {
|
|
3639
|
+
this.imageHeight.setValue(size.height);
|
|
3640
|
+
this.imageHeight.updateValueAndValidity();
|
|
3641
|
+
this.imageHeight.markAsDirty();
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
onImageOpacityChange(value) {
|
|
3645
|
+
this.imageOpacity.setValue(value);
|
|
3646
|
+
this.imageOpacity.updateValueAndValidity();
|
|
3647
|
+
this.imageOpacity.markAsDirty();
|
|
3648
|
+
if (this._renderer) {
|
|
3649
|
+
this._renderer.setImageOpacity(value);
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
resetImgMetadata() {
|
|
3653
|
+
this.imageX.reset();
|
|
3654
|
+
this.imageY.reset();
|
|
3655
|
+
this.imageWidth.reset();
|
|
3656
|
+
this.imageHeight.reset();
|
|
3657
|
+
}
|
|
3300
3658
|
/**
|
|
3301
3659
|
* Toggle rulers in the snapshot view.
|
|
3302
3660
|
*/
|
|
@@ -3319,7 +3677,9 @@ class SnapshotEditorComponent {
|
|
|
3319
3677
|
},
|
|
3320
3678
|
style: this.style.value || undefined,
|
|
3321
3679
|
defs: this.defs.value || undefined,
|
|
3322
|
-
|
|
3680
|
+
// snapshot nodes might be changed, so we copy them
|
|
3681
|
+
// to avoid changing the original snapshot
|
|
3682
|
+
text: deepCopy(this.baseText.value),
|
|
3323
3683
|
textStyle: this.textStyle.value || undefined,
|
|
3324
3684
|
textOptions: {
|
|
3325
3685
|
lineHeightOffset: this.lineHeightOffset.value,
|
|
@@ -3369,16 +3729,16 @@ class SnapshotEditorComponent {
|
|
|
3369
3729
|
* in the snapshot change event.
|
|
3370
3730
|
*/
|
|
3371
3731
|
save() {
|
|
3372
|
-
if (this.form.invalid || this.noSave) {
|
|
3732
|
+
if (this.form.invalid || this.noSave()) {
|
|
3373
3733
|
return;
|
|
3374
3734
|
}
|
|
3375
|
-
this.
|
|
3376
|
-
this.snapshotChange.emit(this.
|
|
3735
|
+
this.snapshot.set(this.getSnapshot());
|
|
3736
|
+
this.snapshotChange.emit(this.snapshot());
|
|
3377
3737
|
}
|
|
3378
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.
|
|
3379
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", 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", "disabledInteractive"], 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", "mat-align-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"] }] }); }
|
|
3738
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SnapshotEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$1.MatDialog }, { token: i2$1.DialogService }, { token: i4$2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3739
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: SnapshotEditorComponent, isStandalone: true, selector: "gve-snapshot-editor", inputs: { snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, batchOps: { classPropertyName: "batchOps", publicName: "batchOps", isSignal: true, isRequired: false, transformFunction: null }, noSave: { classPropertyName: "noSave", publicName: "noSave", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { snapshot: "snapshotChange", 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>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange) {\r\n <span id=\"text-range\">{{ textRange.at }}\u00D7{{ textRange.run }}</span>\r\n }\r\n </div>\r\n <!-- base text metadata -->\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 || undefined\"\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 <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\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 <!-- chain -->\r\n <mat-checkbox [formControl]=\"showChain\">show chain</mat-checkbox>\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>nr.</th>\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\">{{ index + 1 }}.</td>\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 <!-- 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 <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\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\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\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=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</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 <!-- chain view -->\r\n @if (chain) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain\" />\r\n </div>\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\"\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}div#chain-view{margin-bottom:8px}@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: "pipe", type: i2.DecimalPipe, name: "number" }, { 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: "component", type: i4$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "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: "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", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatSliderModule }, { kind: "component", type: i15.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i15.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i16.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i16.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i16.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-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: "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: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { 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"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }, { kind: "component", type: ChainViewComponent, selector: "gve-chain-view", inputs: ["chain", "direction", "selectedTags"], outputs: ["selectedTagsChange"] }] }); }
|
|
3380
3740
|
}
|
|
3381
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.
|
|
3741
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: SnapshotEditorComponent, decorators: [{
|
|
3382
3742
|
type: Component,
|
|
3383
3743
|
args: [{ selector: 'gve-snapshot-editor', imports: [
|
|
3384
3744
|
CommonModule,
|
|
@@ -3392,31 +3752,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
3392
3752
|
MatIconModule,
|
|
3393
3753
|
MatInputModule,
|
|
3394
3754
|
MatProgressBarModule,
|
|
3755
|
+
MatSliderModule,
|
|
3395
3756
|
MatSnackBarModule,
|
|
3396
3757
|
MatTabsModule,
|
|
3397
3758
|
MatTooltipModule,
|
|
3398
|
-
NgToolsModule,
|
|
3399
|
-
BaseTextEditorComponent,
|
|
3400
3759
|
ChainOperationEditorComponent,
|
|
3401
3760
|
ChainResultViewComponent,
|
|
3761
|
+
BaseTextViewComponent,
|
|
3402
3762
|
LnHeightsEditorComponent,
|
|
3403
3763
|
AnimationTimelineSetComponent,
|
|
3404
|
-
], 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"] }]
|
|
3405
|
-
|
|
3764
|
+
FlatLookupPipe,
|
|
3765
|
+
ChainViewComponent,
|
|
3766
|
+
], 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>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange) {\r\n <span id=\"text-range\">{{ textRange.at }}\u00D7{{ textRange.run }}</span>\r\n }\r\n </div>\r\n <!-- base text metadata -->\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 || undefined\"\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 <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\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 <!-- chain -->\r\n <mat-checkbox [formControl]=\"showChain\">show chain</mat-checkbox>\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>nr.</th>\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\">{{ index + 1 }}.</td>\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 <!-- 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 <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\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\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\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=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</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 <!-- chain view -->\r\n @if (chain) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain\" />\r\n </div>\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\"\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}div#chain-view{margin-bottom:8px}@media only screen and (max-width: 959px){div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
|
|
3767
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$1.MatDialog }, { type: i2$1.DialogService }, { type: i4$2.MatSnackBar }], propDecorators: { snapshotView: [{
|
|
3406
3768
|
type: ViewChild,
|
|
3407
3769
|
args: ['snapshotView', { static: false }]
|
|
3408
|
-
}], snapshot: [{
|
|
3409
|
-
type: Input
|
|
3410
|
-
}], batchOps: [{
|
|
3411
|
-
type: Input
|
|
3412
|
-
}], noSave: [{
|
|
3413
|
-
type: Input
|
|
3414
|
-
}], debug: [{
|
|
3415
|
-
type: Input
|
|
3416
|
-
}], snapshotChange: [{
|
|
3417
|
-
type: Output
|
|
3418
|
-
}], snapshotCancel: [{
|
|
3419
|
-
type: Output
|
|
3420
3770
|
}] } });
|
|
3421
3771
|
|
|
3422
3772
|
/*
|
|
@@ -3427,5 +3777,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImpor
|
|
|
3427
3777
|
* Generated bundle index. Do not edit.
|
|
3428
3778
|
*/
|
|
3429
3779
|
|
|
3430
|
-
export { AnimationTimelineComponent, AnimationTweenComponent, BaseTextCharComponent, BaseTextEditorComponent, BaseTextViewComponent, BatchOperationEditorComponent, ChainOperationEditorComponent, ChainResultViewComponent, FeatureEditorComponent, FeatureSetEditorComponent, FeatureSetViewComponent, GveApiService, LnHeightsEditorComponent, OperationSourceEditorComponent, SettingsService,
|
|
3780
|
+
export { AnimationTimelineComponent, AnimationTweenComponent, BaseTextCharComponent, BaseTextEditorComponent, BaseTextViewComponent, BatchOperationEditorComponent, ChainOperationEditorComponent, ChainResultViewComponent, ChainViewComponent, FeatureEditorComponent, FeatureSetEditorComponent, FeatureSetViewComponent, GveApiService, GveGraphvizService, LnHeightsEditorComponent, OperationSourceEditorComponent, SettingsService, SnapshotEditorComponent, SnapshotTextEditorComponent, StepsMapComponent };
|
|
3431
3781
|
//# sourceMappingURL=myrmidon-gve-core.mjs.map
|