@candy-kingdom/bonnie-cms 0.26.16 → 0.27.1

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.
@@ -1,91 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, forwardRef, Output, Directive, ContentChildren, Component, inject, Input, ViewChildren, ChangeDetectorRef, ViewChild, InjectionToken, Injectable, ViewContainerRef, HostBinding, ComponentFactoryResolver } from '@angular/core';
3
- import * as i2 from '@angular/common';
4
- import { CommonModule, DecimalPipe, JsonPipe } from '@angular/common';
5
- import { Subject, debounceTime, map, last, catchError, Observable, of, combineLatest, mergeMap, merge } from 'rxjs';
2
+ import { input, Component, output, model, signal, computed, effect, HostBinding, forwardRef, Directive, contentChildren, inject, DestroyRef, viewChildren, viewChild, InjectionToken, Injectable, ViewContainerRef, afterNextRender } from '@angular/core';
3
+ import { JsonPipe, DecimalPipe, NgTemplateOutlet } from '@angular/common';
6
4
  import * as i1 from '@angular/forms';
7
5
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import { Subject, debounceTime, map, last, catchError, Observable, of, combineLatest, mergeMap, merge } from 'rxjs';
8
8
  import { CdkTextareaAutosize } from '@angular/cdk/text-field';
9
- import { DomSanitizer } from '@angular/platform-browser';
10
9
  import { HttpClient, HttpRequest, HttpEventType } from '@angular/common/http';
10
+ import { DomSanitizer } from '@angular/platform-browser';
11
11
  import { LottieComponent } from 'ngx-lottie';
12
12
  import { MediaObjectFit, MarcyMediaComponent } from '@candy-kingdom/bonnie';
13
13
 
14
- /**
15
- * This is a TypeGen auto-generated file.
16
- * Any changes made to this file can be lost when this file is regenerated.
17
- */
18
-
19
- /**
20
- * This is a TypeGen auto-generated file.
21
- * Any changes made to this file can be lost when this file is regenerated.
22
- */
23
-
24
- /**
25
- * This is a TypeGen auto-generated file.
26
- * Any changes made to this file can be lost when this file is regenerated.
27
- */
28
-
29
- /**
30
- * This is a TypeGen auto-generated file.
31
- * Any changes made to this file can be lost when this file is regenerated.
32
- */
33
-
34
- /**
35
- * This is a TypeGen auto-generated file.
36
- * Any changes made to this file can be lost when this file is regenerated.
37
- */
38
-
39
- /**
40
- * This is a TypeGen auto-generated file.
41
- * Any changes made to this file can be lost when this file is regenerated.
42
- */
43
-
44
- /**
45
- * This is a TypeGen auto-generated file.
46
- * Any changes made to this file can be lost when this file is regenerated.
47
- */
48
-
49
- /**
50
- * This is a TypeGen auto-generated file.
51
- * Any changes made to this file can be lost when this file is regenerated.
52
- */
53
-
54
- /**
55
- * This is a TypeGen auto-generated file.
56
- * Any changes made to this file can be lost when this file is regenerated.
57
- */
58
-
59
- /**
60
- * This is a TypeGen auto-generated file.
61
- * Any changes made to this file can be lost when this file is regenerated.
62
- */
63
-
64
- /**
65
- * This is a TypeGen auto-generated file.
66
- * Any changes made to this file can be lost when this file is regenerated.
67
- */
68
-
69
- /**
70
- * This is a TypeGen auto-generated file.
71
- * Any changes made to this file can be lost when this file is regenerated.
72
- */
73
-
74
- /**
75
- * This is a TypeGen auto-generated file.
76
- * Any changes made to this file can be lost when this file is regenerated.
77
- */
78
- var TextSettingType;
79
- (function (TextSettingType) {
80
- TextSettingType[TextSettingType["SingleLine"] = 0] = "SingleLine";
81
- TextSettingType[TextSettingType["MultiLine"] = 1] = "MultiLine";
82
- })(TextSettingType || (TextSettingType = {}));
83
-
84
- /**
85
- * This is a TypeGen auto-generated file.
86
- * Any changes made to this file can be lost when this file is regenerated.
87
- */
88
-
89
14
  var DeviceType;
90
15
  (function (DeviceType) {
91
16
  DeviceType[DeviceType["NotSet"] = 0] = "NotSet";
@@ -94,6 +19,15 @@ var DeviceType;
94
19
  DeviceType[DeviceType["Desktop"] = 3] = "Desktop";
95
20
  })(DeviceType || (DeviceType = {}));
96
21
 
22
+ var DeviceVisibility;
23
+ (function (DeviceVisibility) {
24
+ DeviceVisibility[DeviceVisibility["None"] = 0] = "None";
25
+ DeviceVisibility[DeviceVisibility["Desktop"] = 1] = "Desktop";
26
+ DeviceVisibility[DeviceVisibility["Tablet"] = 2] = "Tablet";
27
+ DeviceVisibility[DeviceVisibility["Mobile"] = 4] = "Mobile";
28
+ DeviceVisibility[DeviceVisibility["All"] = 7] = "All";
29
+ })(DeviceVisibility || (DeviceVisibility = {}));
30
+
97
31
  var TextEditorField;
98
32
  (function (TextEditorField) {
99
33
  TextEditorField[TextEditorField["NotSet"] = 0] = "NotSet";
@@ -102,14 +36,17 @@ var TextEditorField;
102
36
  })(TextEditorField || (TextEditorField = {}));
103
37
 
104
38
  // tslint:disable-next-line: max-line-length
105
- const regExpIsMobile = new RegExp(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/, 'i');
39
+ const regExpIsMobile = new RegExp(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/, "i");
106
40
  function isLocalUrlString(url) {
107
- if (url === undefined || url === null || url.trim().length === 0)
41
+ if (url === undefined || url === null || url.trim().length === 0) {
108
42
  return true;
109
- if (url.startsWith('//'))
43
+ }
44
+ if (url.startsWith("//")) {
110
45
  return false;
111
- if (url.startsWith('./') || url.startsWith('/'))
46
+ }
47
+ if (url.startsWith("./") || url.startsWith("/")) {
112
48
  return true;
49
+ }
113
50
  return false;
114
51
  }
115
52
  function hasFlag(value, flag) {
@@ -119,67 +56,231 @@ function setOrRemoveFlag(value, flag, enabled) {
119
56
  return (enabled ? value | flag : value & ~flag);
120
57
  }
121
58
 
122
- var DeviceVisibility;
123
- (function (DeviceVisibility) {
124
- DeviceVisibility[DeviceVisibility["None"] = 0] = "None";
125
- DeviceVisibility[DeviceVisibility["Desktop"] = 1] = "Desktop";
126
- DeviceVisibility[DeviceVisibility["Tablet"] = 2] = "Tablet";
127
- DeviceVisibility[DeviceVisibility["Mobile"] = 4] = "Mobile";
128
- DeviceVisibility[DeviceVisibility["All"] = 7] = "All";
129
- })(DeviceVisibility || (DeviceVisibility = {}));
59
+ class AdminControlsComponent {
60
+ DeviceType = DeviceType;
61
+ editableGroup = input.required(...(ngDevMode ? [{ debugName: "editableGroup" }] : []));
62
+ deviceControls = input(false, ...(ngDevMode ? [{ debugName: "deviceControls" }] : []));
63
+ locale = "en";
64
+ device = this.DeviceType.Desktop;
65
+ changeLocale() {
66
+ this.locale = this.locale === "en" ? "ru" : "en"; // move locales to CMS config
67
+ }
68
+ changeDevice() {
69
+ switch (this.device) {
70
+ case DeviceType.Desktop:
71
+ this.device = DeviceType.Tablet;
72
+ return;
73
+ case DeviceType.Tablet:
74
+ this.device = DeviceType.Mobile;
75
+ return;
76
+ case DeviceType.Mobile:
77
+ this.device = DeviceType.Desktop;
78
+ return;
79
+ default:
80
+ this.device = DeviceType.Desktop;
81
+ return;
82
+ }
83
+ }
84
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
85
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AdminControlsComponent, isStandalone: true, selector: "bonc-admin-controls", inputs: { editableGroup: { classPropertyName: "editableGroup", publicName: "editableGroup", isSignal: true, isRequired: true, transformFunction: null }, deviceControls: { classPropertyName: "deviceControls", publicName: "deviceControls", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"page-controls\">\n <a (click)=\"changeLocale()\" class=\"locale\">Locale: {{locale}}</a>\n\n <!-- todo: return this functionality -->\n <!-- <a *ngIf=\"deviceControls\" (click)=\"changeDevice()\" class=\"device\">\n <span *ngIf=\"device === DeviceType.Desktop\">DESKTOP</span>\n <span *ngIf=\"device === DeviceType.Tablet\">TABLET</span>\n <span *ngIf=\"device === DeviceType.Mobile\">MOBILE</span>\n </a> -->\n\n @if (editableGroup() && editableGroup().inEditMode()) {\n <a (click)=\"editableGroup().saveAll()\"\n class=\"save\">Save all</a>\n\n <a (click)=\"editableGroup().cancelAll()\"\n class=\"cancel\">Cancel all</a>\n }\n</div>\n", styles: [":host{display:block;position:sticky;top:0;width:100%;z-index:1000}.page-controls{background-color:#111;height:60px;display:flex;align-items:center;justify-content:center}.page-controls a{color:silver;padding:4px;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center}.page-controls a:hover{color:#000;background-color:#cf0}.page-controls a.device{width:80px}.page-controls a+a{margin-left:6px}\n"] });
86
+ }
87
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, decorators: [{
88
+ type: Component,
89
+ args: [{ selector: "bonc-admin-controls", template: "<div class=\"page-controls\">\n <a (click)=\"changeLocale()\" class=\"locale\">Locale: {{locale}}</a>\n\n <!-- todo: return this functionality -->\n <!-- <a *ngIf=\"deviceControls\" (click)=\"changeDevice()\" class=\"device\">\n <span *ngIf=\"device === DeviceType.Desktop\">DESKTOP</span>\n <span *ngIf=\"device === DeviceType.Tablet\">TABLET</span>\n <span *ngIf=\"device === DeviceType.Mobile\">MOBILE</span>\n </a> -->\n\n @if (editableGroup() && editableGroup().inEditMode()) {\n <a (click)=\"editableGroup().saveAll()\"\n class=\"save\">Save all</a>\n\n <a (click)=\"editableGroup().cancelAll()\"\n class=\"cancel\">Cancel all</a>\n }\n</div>\n", styles: [":host{display:block;position:sticky;top:0;width:100%;z-index:1000}.page-controls{background-color:#111;height:60px;display:flex;align-items:center;justify-content:center}.page-controls a{color:silver;padding:4px;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center}.page-controls a:hover{color:#000;background-color:#cf0}.page-controls a.device{width:80px}.page-controls a+a{margin-left:6px}\n"] }]
90
+ }], propDecorators: { editableGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableGroup", required: true }] }], deviceControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "deviceControls", required: false }] }] } });
91
+
92
+ class BoneEditorBaseComponent {
93
+ editing = output();
94
+ saved = output();
95
+ removed = output();
96
+ boneEtalon = model.required(...(ngDevMode ? [{ debugName: "boneEtalon" }] : []));
97
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
98
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
99
+ _presets = signal([], ...(ngDevMode ? [{ debugName: "_presets" }] : []));
100
+ presets = this._presets.asReadonly();
101
+ _currentPreset = signal(undefined, ...(ngDevMode ? [{ debugName: "_currentPreset" }] : []));
102
+ currentPreset = this._currentPreset.asReadonly();
103
+ _isDirty = signal(false, ...(ngDevMode ? [{ debugName: "_isDirty" }] : []));
104
+ isDirty = this._isDirty.asReadonly();
105
+ _isEditing = signal(false, ...(ngDevMode ? [{ debugName: "_isEditing" }] : []));
106
+ isEditing = this._isEditing.asReadonly();
107
+ noPresets = computed(() => this.presets().length === 0, ...(ngDevMode ? [{ debugName: "noPresets" }] : []));
108
+ // effect gonna fix this undefined value
109
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
110
+ bone = signal(undefined, ...(ngDevMode ? [{ debugName: "bone" }] : []));
111
+ _storedBoneJson = "";
112
+ constructor() {
113
+ effect(() => {
114
+ const newData = this.boneEtalon();
115
+ this._isDirty.set(false);
116
+ this._storedBoneJson = JSON.stringify(newData);
117
+ this.bone.set(JSON.parse(this._storedBoneJson));
118
+ this.updatePresetByData();
119
+ });
120
+ const presets = this.getPresets();
121
+ if (presets.length === 0) {
122
+ presets.push({
123
+ title: "default",
124
+ isActive: () => true,
125
+ transformer: (bone) => bone,
126
+ clean: (bone) => bone,
127
+ });
128
+ }
129
+ // todo: remove this, use just regular presets
130
+ this._presets.set(presets);
131
+ }
132
+ get isMobile() {
133
+ return this.device() === DeviceType.Mobile;
134
+ }
135
+ get isTablet() {
136
+ return this.device() === DeviceType.Tablet;
137
+ }
138
+ get isDesktop() {
139
+ return this.device() === DeviceType.Desktop;
140
+ }
141
+ resetData() {
142
+ this._isEditing.set(false);
143
+ this.boneEtalon.set(JSON.parse(this._storedBoneJson));
144
+ this.updatePresetByData();
145
+ if (this.onReset !== undefined) {
146
+ this.onReset();
147
+ }
148
+ this.editing.emit(false);
149
+ }
150
+ updateDirty() {
151
+ this._isDirty.set(JSON.stringify(this.bone()) !== this._storedBoneJson);
152
+ }
153
+ markAsDirty() {
154
+ this._isDirty.set(true);
155
+ }
156
+ remove() {
157
+ this.removed.emit();
158
+ }
159
+ save() {
160
+ if (!this.isDirty()) {
161
+ return;
162
+ }
163
+ this._isDirty.set(false);
164
+ this.finishEditing();
165
+ this._storedBoneJson = JSON.stringify(this.bone());
166
+ const clonedData = JSON.parse(this._storedBoneJson);
167
+ this.saved.emit(clonedData);
168
+ }
169
+ startEditing() {
170
+ if (this.isEditing()) {
171
+ this.updateDirty();
172
+ return;
173
+ }
174
+ this._isEditing.set(true);
175
+ this.updateDirty();
176
+ this.editing.emit(true);
177
+ }
178
+ finishEditing() {
179
+ if (this._isDirty()) {
180
+ throw new Error("Нельзя закрывать редактирование когда есть изменения. Надо сохранить либо зарезетить.");
181
+ }
182
+ if (this.onFinishEditing !== undefined) {
183
+ this.onFinishEditing();
184
+ }
185
+ this._isEditing.set(false);
186
+ }
187
+ nextPreset() {
188
+ const currentPreset = this.currentPreset();
189
+ const newIndex = currentPreset === undefined ? 0 : this.presets().indexOf(currentPreset) + 1;
190
+ this.applyPresetAtIndex(newIndex);
191
+ }
192
+ applyPresetAtIndex = (newIndex) => {
193
+ newIndex = newIndex < 0 ? 0 : newIndex % this.presets().length;
194
+ const currentPreset = this.currentPreset();
195
+ const currentIndex = currentPreset === undefined ? -1 : this.presets().indexOf(currentPreset);
196
+ if (currentIndex === newIndex) {
197
+ return;
198
+ }
199
+ const newPreset = this.presets()[newIndex];
200
+ this._currentPreset.set(newPreset);
201
+ newPreset.transformer(this.bone());
202
+ this.updateDirty();
203
+ };
204
+ updatePresetByData = () => {
205
+ const countOfActive = this.presets()
206
+ .map((p) => p.isActive(this.bone()))
207
+ .filter((p) => p).length;
208
+ if (countOfActive !== 1) {
209
+ throw new Error(`active preset count should be equal 1, but it was: ${countOfActive}. ${this.constructor.name}`);
210
+ }
211
+ for (let i = 0; i < this.presets().length; i++) {
212
+ const preset = this.presets()[i];
213
+ if (preset.isActive(this.bone())) {
214
+ this.applyPresetAtIndex(i);
215
+ break;
216
+ }
217
+ }
218
+ };
219
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
220
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: BoneEditorBaseComponent, isStandalone: true, selector: "ng-component", inputs: { boneEtalon: { classPropertyName: "boneEtalon", publicName: "boneEtalon", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, device: { classPropertyName: "device", publicName: "device", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editing: "editing", saved: "saved", removed: "removed", boneEtalon: "boneEtalonChange" }, host: { properties: { "class.mobile": "this.isMobile", "class.tablet": "this.isTablet", "class.desktop": "this.isDesktop" } }, ngImport: i0, template: "", isInline: true });
221
+ }
222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, decorators: [{
223
+ type: Component,
224
+ args: [{ template: "" }]
225
+ }], ctorParameters: () => [], propDecorators: { editing: [{ type: i0.Output, args: ["editing"] }], saved: [{ type: i0.Output, args: ["saved"] }], removed: [{ type: i0.Output, args: ["removed"] }], boneEtalon: [{ type: i0.Input, args: [{ isSignal: true, alias: "boneEtalon", required: true }] }, { type: i0.Output, args: ["boneEtalonChange"] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], device: [{ type: i0.Input, args: [{ isSignal: true, alias: "device", required: false }] }], isMobile: [{
226
+ type: HostBinding,
227
+ args: ["class.mobile"]
228
+ }], isTablet: [{
229
+ type: HostBinding,
230
+ args: ["class.tablet"]
231
+ }], isDesktop: [{
232
+ type: HostBinding,
233
+ args: ["class.desktop"]
234
+ }] } });
235
+
236
+ class UnknownBoneEditorComponent extends BoneEditorBaseComponent {
237
+ onFinishEditing() {
238
+ // no-op for unknown bone type
239
+ }
240
+ onReset() {
241
+ // no-op for unknown bone type
242
+ }
243
+ getPresets() {
244
+ return [];
245
+ }
246
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
247
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: UnknownBoneEditorComponent, isStandalone: true, selector: "bonc-unknown-bone-editor", usesInheritance: true, ngImport: i0, template: "<p>Unknown Bone Editor | bone type: {{bone().type}}</p>\n<pre>{{bone() | json}}</pre>\n", styles: [":host{display:block;background-color:#dc143c}\n"], dependencies: [{ kind: "pipe", type: JsonPipe, name: "json" }] });
248
+ }
249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, decorators: [{
250
+ type: Component,
251
+ args: [{ selector: "bonc-unknown-bone-editor", imports: [JsonPipe], template: "<p>Unknown Bone Editor | bone type: {{bone().type}}</p>\n<pre>{{bone() | json}}</pre>\n", styles: [":host{display:block;background-color:#dc143c}\n"] }]
252
+ }] });
130
253
 
131
254
  class EditableDirective {
132
- saved = new EventEmitter();
133
- editModeChange = new EventEmitter();
134
- externalSaveCall = new EventEmitter();
135
- canceled = new EventEmitter();
136
- valueChange = new EventEmitter();
255
+ saved = output();
256
+ editModeChange = output();
257
+ externalSaveCall = output();
258
+ canceled = output();
259
+ valueChange = output();
260
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
137
261
  propagateChange = () => { };
138
- _inEditMode = false;
139
- _isDirty = false;
262
+ _inEditMode = signal(false, ...(ngDevMode ? [{ debugName: "_inEditMode" }] : []));
263
+ inEditMode = this._inEditMode.asReadonly();
264
+ _isDirty = signal(false, ...(ngDevMode ? [{ debugName: "_isDirty" }] : []));
265
+ isDirty = this._isDirty.asReadonly();
140
266
  _value;
141
267
  _originalValue;
142
268
  _storedData;
143
- get inEditMode() {
144
- return this._inEditMode;
145
- }
146
- subscribe(params) {
147
- const subscriptions = [];
148
- if (params.onValueChange !== undefined && params.onValueChange !== null) {
149
- subscriptions.push(this.valueChange.subscribe((x) => {
150
- if (params.onValueChange)
151
- params.onValueChange(x);
152
- }));
153
- }
154
- if (params.onEditModeChange !== undefined) {
155
- subscriptions.push(this.editModeChange.subscribe((x) => {
156
- if (params.onEditModeChange)
157
- params.onEditModeChange(x);
158
- }));
159
- }
160
- if (params.onSaveRequest !== undefined) {
161
- subscriptions.push(this.externalSaveCall.subscribe(() => {
162
- if (params.onSaveRequest)
163
- params.onSaveRequest();
164
- }));
165
- }
166
- return subscriptions;
167
- }
168
269
  requestSave() {
169
270
  this.externalSaveCall.emit();
170
271
  }
171
272
  startEditing = () => {
172
- if (this._inEditMode) {
273
+ if (this.inEditMode()) {
173
274
  this.updateDirty();
174
275
  return;
175
276
  }
176
- this._inEditMode = true;
277
+ this._inEditMode.set(true);
177
278
  this.updateDirty();
178
279
  this.editModeChange.emit(true);
179
280
  };
180
281
  save(newData) {
181
- if (!this._inEditMode) {
182
- console.warn('save before edit mode'); //todo: fix that
282
+ if (!this.inEditMode()) {
283
+ console.warn("save before edit mode"); //todo: fix that
183
284
  }
184
285
  this.finishEditing();
185
286
  const newUnqieNotEmptyData = this.value === newData || newData === undefined
@@ -191,24 +292,26 @@ class EditableDirective {
191
292
  this.propagateChange(newUnqieNotEmptyData);
192
293
  }
193
294
  patchSave(propName, newDataParts) {
194
- if (this._originalValue === undefined || this._originalValue === null)
295
+ if (this._originalValue === undefined || this._originalValue === null) {
195
296
  return;
297
+ }
196
298
  if (typeof this._originalValue !== "object") {
197
- console.warn('patch save called on not object type');
299
+ console.warn("patch save called on not object type");
198
300
  return;
199
301
  }
200
302
  this._originalValue[propName] = newDataParts;
201
303
  this.setOriginal(this._originalValue);
202
304
  this.updateDirty();
203
- if (!this.isDirty)
305
+ if (!this.isDirty()) {
204
306
  this.close();
307
+ }
205
308
  }
206
309
  // change model without opening or closing the editor
207
310
  silentPatch(dict) {
208
311
  for (const key in dict) {
209
- // eslint-disable-next-line no-prototype-builtins
210
- if (!dict.hasOwnProperty(key))
312
+ if (!Object.hasOwn(dict, key)) {
211
313
  continue;
314
+ }
212
315
  const propVal = dict[key];
213
316
  if (this._value !== undefined && this._value !== null) {
214
317
  this._value[key] = propVal;
@@ -220,8 +323,9 @@ class EditableDirective {
220
323
  this._storedData = JSON.stringify(this._originalValue);
221
324
  }
222
325
  cancel() {
223
- if (!this._inEditMode)
326
+ if (!this.inEditMode()) {
224
327
  return;
328
+ }
225
329
  this.finishEditing();
226
330
  // reset value
227
331
  this.setValue(this._originalValue);
@@ -231,15 +335,17 @@ class EditableDirective {
231
335
  this.finishEditing();
232
336
  }
233
337
  finishEditing() {
234
- if (!this._inEditMode)
338
+ if (!this.inEditMode()) {
235
339
  return;
236
- this._inEditMode = false;
340
+ }
341
+ this._inEditMode.set(false);
237
342
  this.editModeChange.emit(false);
238
343
  }
239
344
  setValue(newValue) {
240
- this._value = (newValue === null || newValue === undefined)
241
- ? undefined
242
- : JSON.parse(JSON.stringify(newValue));
345
+ this._value =
346
+ newValue === null || newValue === undefined
347
+ ? undefined
348
+ : JSON.parse(JSON.stringify(newValue));
243
349
  this.valueChange.emit(this._value);
244
350
  }
245
351
  set value(newValue) {
@@ -249,13 +355,10 @@ class EditableDirective {
249
355
  return this._value;
250
356
  }
251
357
  updateDirty() {
252
- this._isDirty = JSON.stringify(this._value) !== this._storedData;
358
+ this._isDirty.set(JSON.stringify(this._value) !== this._storedData);
253
359
  }
254
360
  markAsDirty() {
255
- this._isDirty = true;
256
- }
257
- get isDirty() {
258
- return this._isDirty;
361
+ this._isDirty.set(true);
259
362
  }
260
363
  setOriginal(newOriginalValue) {
261
364
  this._originalValue = newOriginalValue;
@@ -265,10 +368,11 @@ class EditableDirective {
265
368
  writeValue(newValue) {
266
369
  this.finishEditing();
267
370
  this.setOriginal(newValue);
268
- this._isDirty = false;
371
+ this._isDirty.set(false);
269
372
  this.setValue(newValue);
270
373
  }
271
374
  setDisabledState() {
375
+ // required by ControlValueAccessor
272
376
  }
273
377
  registerOnChange(tellAngularThatSomethingIsChanged) {
274
378
  this.propagateChange = (newValue) => {
@@ -276,139 +380,107 @@ class EditableDirective {
276
380
  };
277
381
  }
278
382
  registerOnTouched() {
383
+ // required by ControlValueAccessor
279
384
  }
280
385
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
281
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: EditableDirective, isStandalone: true, selector: "[boncEditable]", outputs: { saved: "saved", editModeChange: "editModeChange", externalSaveCall: "externalSaveCall", canceled: "canceled", valueChange: "valueChange" }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true }], ngImport: i0 });
386
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: EditableDirective, isStandalone: true, selector: "[boncEditable]", outputs: { saved: "saved", editModeChange: "editModeChange", externalSaveCall: "externalSaveCall", canceled: "canceled", valueChange: "valueChange" }, providers: [
387
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true },
388
+ ], ngImport: i0 });
282
389
  }
283
390
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableDirective, decorators: [{
284
391
  type: Directive,
285
392
  args: [{
286
- standalone: true,
287
- selector: '[boncEditable]',
288
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true }]
393
+ selector: "[boncEditable]",
394
+ providers: [
395
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true },
396
+ ],
289
397
  }]
290
- }], propDecorators: { saved: [{
291
- type: Output
292
- }], editModeChange: [{
293
- type: Output
294
- }], externalSaveCall: [{
295
- type: Output
296
- }], canceled: [{
297
- type: Output
298
- }], valueChange: [{
299
- type: Output
300
- }] } });
398
+ }], propDecorators: { saved: [{ type: i0.Output, args: ["saved"] }], editModeChange: [{ type: i0.Output, args: ["editModeChange"] }], externalSaveCall: [{ type: i0.Output, args: ["externalSaveCall"] }], canceled: [{ type: i0.Output, args: ["canceled"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
301
399
 
302
400
  class EditableGroupComponent {
303
- editModeChange = new EventEmitter();
304
- saved = new EventEmitter();
305
- requestEditorClose = new EventEmitter();
306
- editables;
307
- subscriptions = [];
308
- _inEditMode = false;
309
- saveSubject = new Subject();
310
- ngAfterContentInit() {
311
- this.saveSubject
401
+ editModeChange = output();
402
+ saved = output();
403
+ requestEditorClose = output();
404
+ editables = contentChildren(EditableDirective, { ...(ngDevMode ? { debugName: "editables" } : {}), descendants: true });
405
+ _subscriptions = [];
406
+ inEditMode = signal(false, ...(ngDevMode ? [{ debugName: "inEditMode" }] : []));
407
+ _saveSubject = new Subject(); // todo: use signal
408
+ constructor() {
409
+ const saveSubscription = this._saveSubject
312
410
  .asObservable()
313
- .pipe(debounceTime(100))
314
- .subscribe(() => this.saved.emit());
315
- this.updateSubscriptions();
316
- this.editables.changes.subscribe(() => {
317
- this.clearSubscribtions();
411
+ .pipe(debounceTime(100), takeUntilDestroyed())
412
+ .subscribe(() => this.saved.emit(undefined));
413
+ effect(() => {
414
+ this.clearSubscriptions();
318
415
  this.updateSubscriptions();
319
416
  });
320
- }
321
- get inEditMode() {
322
- return this._inEditMode;
417
+ inject(DestroyRef).onDestroy(() => {
418
+ saveSubscription.unsubscribe();
419
+ this.clearSubscriptions();
420
+ });
323
421
  }
324
422
  saveAll() {
325
- this.editables.forEach(x => x.requestSave());
423
+ this.editables().forEach((x) => x.requestSave());
326
424
  }
327
425
  cancelAll() {
328
- this.editables.forEach(x => x.cancel());
426
+ this.editables().forEach((x) => x.cancel());
329
427
  }
330
428
  closeAll() {
331
429
  this.requestEditorClose.emit(true);
332
- this.editables.forEach(x => x.close());
430
+ this.editables().forEach((x) => x.close());
333
431
  }
334
- clearSubscribtions() {
335
- this.subscriptions.forEach(x => x.unsubscribe());
336
- this.subscriptions.splice(0, this.subscriptions.length);
432
+ clearSubscriptions() {
433
+ this._subscriptions.forEach((x) => x.unsubscribe());
434
+ this._subscriptions.splice(0, this._subscriptions.length);
337
435
  }
338
436
  updateSubscriptions() {
339
- this.editables.forEach(editable => {
340
- this.subscriptions.push(editable.saved.subscribe(() => this.onSave()));
341
- this.subscriptions.push(...editable.subscribe({
342
- onEditModeChange: this.updateEditMode.bind(this)
343
- }));
437
+ this.editables().forEach((editable) => {
438
+ this._subscriptions.push(editable.saved.subscribe(this.onSave.bind(this)), editable.editModeChange.subscribe(this.updateEditMode.bind(this)));
344
439
  });
345
440
  }
346
441
  onSave() {
347
- this.saveSubject.next();
442
+ this._saveSubject.next();
348
443
  }
349
444
  updateEditMode() {
350
- const newEditMode = this.editables
351
- .filter(x => x.inEditMode)
352
- .length > 0;
353
- if (newEditMode === this._inEditMode)
445
+ const newEditMode = this.editables().filter((x) => x.inEditMode()).length > 0;
446
+ if (newEditMode === this.inEditMode()) {
354
447
  return;
355
- this._inEditMode = newEditMode;
356
- this.editModeChange.next(newEditMode);
357
- }
358
- ngOnDestroy() {
359
- this.clearSubscribtions();
448
+ }
449
+ this.inEditMode.set(newEditMode);
450
+ this.editModeChange.emit(newEditMode);
360
451
  }
361
452
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
362
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: EditableGroupComponent, isStandalone: true, selector: "bonc-editable-group", outputs: { editModeChange: "editModeChange", saved: "saved", requestEditorClose: "requestEditorClose" }, queries: [{ propertyName: "editables", predicate: EditableDirective, descendants: true }], ngImport: i0, template: '<ng-content></ng-content>', isInline: true });
453
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: EditableGroupComponent, isStandalone: true, selector: "bonc-editable-group", outputs: { editModeChange: "editModeChange", saved: "saved", requestEditorClose: "requestEditorClose" }, queries: [{ propertyName: "editables", predicate: EditableDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-content></ng-content>", isInline: true });
363
454
  }
364
455
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableGroupComponent, decorators: [{
365
456
  type: Component,
366
457
  args: [{
367
- selector: 'bonc-editable-group',
368
- standalone: true,
369
- template: '<ng-content></ng-content>'
458
+ selector: "bonc-editable-group",
459
+ template: "<ng-content></ng-content>",
370
460
  }]
371
- }], propDecorators: { editModeChange: [{
372
- type: Output
373
- }], saved: [{
374
- type: Output
375
- }], requestEditorClose: [{
376
- type: Output
377
- }], editables: [{
378
- type: ContentChildren,
379
- args: [EditableDirective, { descendants: true }]
380
- }] } });
461
+ }], ctorParameters: () => [], propDecorators: { editModeChange: [{ type: i0.Output, args: ["editModeChange"] }], saved: [{ type: i0.Output, args: ["saved"] }], requestEditorClose: [{ type: i0.Output, args: ["requestEditorClose"] }], editables: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => EditableDirective), { ...{ descendants: true }, isSignal: true }] }] } });
381
462
 
382
463
  class FormBaseComponent {
383
- static inputs = ['locale'];
384
- name = '';
385
- _locale = '';
464
+ name = "";
386
465
  editable = inject((EditableDirective), { host: true });
387
- set locale(value) {
388
- this._locale = value;
389
- }
390
- get locale() {
391
- return this._locale;
392
- }
466
+ locale = input("", ...(ngDevMode ? [{ debugName: "locale" }] : []));
393
467
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
394
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: FormBaseComponent, isStandalone: true, selector: "ng-component", inputs: { locale: "locale" }, ngImport: i0, template: '', isInline: true });
468
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: FormBaseComponent, isStandalone: true, selector: "ng-component", inputs: { locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "", isInline: true });
395
469
  }
396
470
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormBaseComponent, decorators: [{
397
471
  type: Component,
398
472
  args: [{
399
- template: ''
473
+ template: "",
400
474
  }]
401
- }], propDecorators: { locale: [{
402
- type: Input
403
- }] } });
475
+ }], propDecorators: { locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }] } });
404
476
 
405
477
  class TranslationInputComponent {
406
- text;
407
- locale;
408
- device = DeviceType.NotSet;
409
- startEditing = new EventEmitter();
410
- changed = new EventEmitter();
411
- blurred = new EventEmitter();
478
+ text = input.required(...(ngDevMode ? [{ debugName: "text" }] : []));
479
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
480
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
481
+ startEditing = output();
482
+ changed = output();
483
+ blurred = output();
412
484
  onClick() {
413
485
  this.startEditing.emit();
414
486
  }
@@ -419,42 +491,35 @@ class TranslationInputComponent {
419
491
  this.blurred.emit();
420
492
  }
421
493
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
422
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TranslationInputComponent, isStandalone: true, selector: "bonc-translation-input", inputs: { text: "text", locale: "locale", device: "device" }, outputs: { startEditing: "startEditing", changed: "changed", blurred: "blurred" }, ngImport: i0, template: "<input [(ngModel)]=\"text[locale]\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\" />\n", styles: [":host{padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}:host{display:block}:host input{display:block;border:none;background-color:#8080801a;color:var(--text-color)}:host input:hover{background-color:#d3d3d3;cursor:pointer}:host input:focus{outline:none;color:#000;background-color:#cf0}:host input{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}:host input:hover{border-color:transparent;background-color:#333;cursor:pointer}:host input:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}:host input{display:block;box-sizing:border-box;font-family:inherit;width:100%;border:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
494
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: TranslationInputComponent, isStandalone: true, selector: "bonc-translation-input", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, device: { classPropertyName: "device", publicName: "device", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { startEditing: "startEditing", changed: "changed", blurred: "blurred" }, ngImport: i0, template: "<input [(ngModel)]=\"text()[locale()]\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\" />\n", styles: [":host{padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}:host{display:block}:host input{display:block;border:none;background-color:#8080801a;color:var(--text-color)}:host input:hover{background-color:#d3d3d3;cursor:pointer}:host input:focus{outline:none;color:#000;background-color:#cf0}:host input{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}:host input:hover{border-color:transparent;background-color:#333;cursor:pointer}:host input:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}:host input{display:block;box-sizing:border-box;font-family:inherit;width:100%;border:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
423
495
  }
424
496
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationInputComponent, decorators: [{
425
497
  type: Component,
426
- args: [{ selector: 'bonc-translation-input', standalone: true, imports: [FormsModule], template: "<input [(ngModel)]=\"text[locale]\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\" />\n", styles: [":host{padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}:host{display:block}:host input{display:block;border:none;background-color:#8080801a;color:var(--text-color)}:host input:hover{background-color:#d3d3d3;cursor:pointer}:host input:focus{outline:none;color:#000;background-color:#cf0}:host input{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}:host input:hover{border-color:transparent;background-color:#333;cursor:pointer}:host input:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}:host input{display:block;box-sizing:border-box;font-family:inherit;width:100%;border:none}\n"] }]
427
- }], propDecorators: { text: [{
428
- type: Input,
429
- args: [{ required: true }]
430
- }], locale: [{
431
- type: Input,
432
- args: [{ required: true }]
433
- }], device: [{
434
- type: Input
435
- }], startEditing: [{
436
- type: Output
437
- }], changed: [{
438
- type: Output
439
- }], blurred: [{
440
- type: Output
441
- }] } });
498
+ args: [{ selector: "bonc-translation-input", imports: [FormsModule], template: "<input [(ngModel)]=\"text()[locale()]\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\" />\n", styles: [":host{padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}:host{display:block}:host input{display:block;border:none;background-color:#8080801a;color:var(--text-color)}:host input:hover{background-color:#d3d3d3;cursor:pointer}:host input:focus{outline:none;color:#000;background-color:#cf0}:host input{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}:host input:hover{border-color:transparent;background-color:#333;cursor:pointer}:host input:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}:host input{display:block;box-sizing:border-box;font-family:inherit;width:100%;border:none}\n"] }]
499
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], device: [{ type: i0.Input, args: [{ isSignal: true, alias: "device", required: false }] }], startEditing: [{ type: i0.Output, args: ["startEditing"] }], changed: [{ type: i0.Output, args: ["changed"] }], blurred: [{ type: i0.Output, args: ["blurred"] }] } });
442
500
 
443
501
  class TranslationTextareaComponent {
444
- autosizeList;
445
- minRows;
446
- maxRows;
447
- text;
448
- locale;
449
- device = DeviceType.NotSet;
450
- startEditing = new EventEmitter();
451
- changed = new EventEmitter();
452
- blurred = new EventEmitter();
453
- ngAfterViewInit() {
454
- setTimeout(this.triggerResize.bind(this));
455
- }
456
- ngOnChanges() {
457
- setTimeout(this.triggerResize.bind(this), 500);
502
+ autosizeList = viewChildren(CdkTextareaAutosize, ...(ngDevMode ? [{ debugName: "autosizeList" }] : []));
503
+ text = input.required(...(ngDevMode ? [{ debugName: "text" }] : []));
504
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
505
+ minRows = input(...(ngDevMode ? [undefined, { debugName: "minRows" }] : []));
506
+ maxRows = input(...(ngDevMode ? [undefined, { debugName: "maxRows" }] : []));
507
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
508
+ startEditing = output();
509
+ changed = output();
510
+ blurred = output();
511
+ constructor() {
512
+ // todo: check if it still necessary
513
+ effect((onCleanup) => {
514
+ this.text();
515
+ this.locale();
516
+ this.minRows();
517
+ this.maxRows();
518
+ const timer = setTimeout(() => this.triggerResize(), 500);
519
+ onCleanup(() => {
520
+ clearTimeout(timer);
521
+ });
522
+ });
458
523
  }
459
524
  onClick() {
460
525
  this.startEditing.emit();
@@ -466,63 +531,36 @@ class TranslationTextareaComponent {
466
531
  this.blurred.emit();
467
532
  }
468
533
  triggerResize() {
469
- // console.log('trigger resize!');
470
- // todo: investigate is it working or not
471
- // this.ngZone.onStable.pipe(take(1))
472
- // .subscribe(() => {
473
- // this.autosizeList.forEach(x => x.resizeToFitContent(true));
474
- // });
534
+ this.autosizeList().forEach((x) => x.resizeToFitContent(true));
475
535
  }
476
536
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
477
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TranslationTextareaComponent, isStandalone: true, selector: "bonc-translation-textarea", inputs: { minRows: "minRows", maxRows: "maxRows", text: "text", locale: "locale", device: "device" }, outputs: { startEditing: "startEditing", changed: "changed", blurred: "blurred" }, viewQueries: [{ propertyName: "autosizeList", predicate: CdkTextareaAutosize, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<textarea [(ngModel)]=\"text[locale]\"\n [cdkAutosizeMinRows]=\"minRows\"\n [cdkAutosizeMaxRows]=\"maxRows\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\"\n matInput\n cdkTextareaAutosize>\n</textarea>\n", styles: [":host{display:block;padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}textarea{display:block;width:100%;line-height:140%;padding:2px 0;margin:0;box-sizing:content-box;border:none;resize:none}textarea:focus{outline:none}textarea[readonly]{cursor:pointer}textarea[readonly]::selection{-webkit-user-select:none;user-select:none}textarea[readonly]:hover{background:repeating-linear-gradient(-45deg,#cf00,#cf00 10px,#000 10px 20px) fixed bottom;color:#000}textarea{color:silver;background-color:transparent}textarea:hover{cursor:pointer}textarea:focus{outline:none;color:#fff;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }] });
537
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: TranslationTextareaComponent, isStandalone: true, selector: "bonc-translation-textarea", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, minRows: { classPropertyName: "minRows", publicName: "minRows", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, device: { classPropertyName: "device", publicName: "device", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { startEditing: "startEditing", changed: "changed", blurred: "blurred" }, viewQueries: [{ propertyName: "autosizeList", predicate: CdkTextareaAutosize, descendants: true, isSignal: true }], ngImport: i0, template: "<textarea [(ngModel)]=\"text()[locale()]\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\"\n matInput\n cdkTextareaAutosize>\n</textarea>\n", styles: [":host{display:block;padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}textarea{display:block;width:100%;line-height:140%;padding:2px 0;margin:0;box-sizing:content-box;border:none;resize:none}textarea:focus{outline:none}textarea[readonly]{cursor:pointer}textarea[readonly]::selection{-webkit-user-select:none;user-select:none}textarea[readonly]:hover{background:repeating-linear-gradient(-45deg,#cf00,#cf00 10px,#000 10px 20px) fixed bottom;color:#000}textarea{color:silver;background-color:transparent}textarea:hover{cursor:pointer}textarea:focus{outline:none;color:#fff;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }] });
478
538
  }
479
539
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationTextareaComponent, decorators: [{
480
540
  type: Component,
481
- args: [{ selector: 'bonc-translation-textarea', standalone: true, imports: [FormsModule, CdkTextareaAutosize], template: "<textarea [(ngModel)]=\"text[locale]\"\n [cdkAutosizeMinRows]=\"minRows\"\n [cdkAutosizeMaxRows]=\"maxRows\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\"\n matInput\n cdkTextareaAutosize>\n</textarea>\n", styles: [":host{display:block;padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}textarea{display:block;width:100%;line-height:140%;padding:2px 0;margin:0;box-sizing:content-box;border:none;resize:none}textarea:focus{outline:none}textarea[readonly]{cursor:pointer}textarea[readonly]::selection{-webkit-user-select:none;user-select:none}textarea[readonly]:hover{background:repeating-linear-gradient(-45deg,#cf00,#cf00 10px,#000 10px 20px) fixed bottom;color:#000}textarea{color:silver;background-color:transparent}textarea:hover{cursor:pointer}textarea:focus{outline:none;color:#fff;cursor:default}\n"] }]
482
- }], propDecorators: { autosizeList: [{
483
- type: ViewChildren,
484
- args: [CdkTextareaAutosize]
485
- }], minRows: [{
486
- type: Input
487
- }], maxRows: [{
488
- type: Input
489
- }], text: [{
490
- type: Input,
491
- args: [{ required: true }]
492
- }], locale: [{
493
- type: Input,
494
- args: [{ required: true }]
495
- }], device: [{
496
- type: Input
497
- }], startEditing: [{
498
- type: Output
499
- }], changed: [{
500
- type: Output
501
- }], blurred: [{
502
- type: Output
503
- }] } });
541
+ args: [{ selector: "bonc-translation-textarea", imports: [FormsModule, CdkTextareaAutosize], template: "<textarea [(ngModel)]=\"text()[locale()]\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (click)=\"onClick()\"\n (keyup)=\"onKeyPress()\"\n (blur)=\"onBlur()\"\n matInput\n cdkTextareaAutosize>\n</textarea>\n", styles: [":host{display:block;padding:6px 12px;border:1px solid silver}:host:hover{background-color:#333}:host:focus-within{background-color:#555}textarea{display:block;width:100%;line-height:140%;padding:2px 0;margin:0;box-sizing:content-box;border:none;resize:none}textarea:focus{outline:none}textarea[readonly]{cursor:pointer}textarea[readonly]::selection{-webkit-user-select:none;user-select:none}textarea[readonly]:hover{background:repeating-linear-gradient(-45deg,#cf00,#cf00 10px,#000 10px 20px) fixed bottom;color:#000}textarea{color:silver;background-color:transparent}textarea:hover{cursor:pointer}textarea:focus{outline:none;color:#fff;cursor:default}\n"] }]
542
+ }], ctorParameters: () => [], propDecorators: { autosizeList: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CdkTextareaAutosize), { isSignal: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], device: [{ type: i0.Input, args: [{ isSignal: true, alias: "device", required: false }] }], startEditing: [{ type: i0.Output, args: ["startEditing"] }], changed: [{ type: i0.Output, args: ["changed"] }], blurred: [{ type: i0.Output, args: ["blurred"] }] } });
504
543
 
505
544
  class LinkPopupComponent {
506
545
  TextEditorField = TextEditorField;
507
- linkChange = new EventEmitter();
508
- startEditing = new EventEmitter();
509
- changed = new EventEmitter();
510
- blurred = new EventEmitter();
511
- open = new EventEmitter();
512
- closed = new EventEmitter();
513
- field;
514
546
  LinkPopupField = TextEditorField;
515
- maxRows;
516
- minRows;
517
- linkTitle; // todo: allready in weblink
518
- locale;
519
- link;
547
+ linkChange = output();
548
+ startEditing = output();
549
+ changed = output();
550
+ blurred = output();
551
+ open = output();
552
+ closed = output();
553
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
554
+ maxRows = input(...(ngDevMode ? [undefined, { debugName: "maxRows" }] : []));
555
+ minRows = input(...(ngDevMode ? [undefined, { debugName: "minRows" }] : []));
556
+ linkTitle = input.required(...(ngDevMode ? [{ debugName: "linkTitle" }] : []));
557
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
558
+ link = input.required(...(ngDevMode ? [{ debugName: "link" }] : []));
520
559
  popupIsShown = false;
521
560
  onClick() {
522
561
  this.startEditing.emit();
523
562
  }
524
563
  onChange() {
525
- console.log('change link pop up');
526
564
  this.changed.emit();
527
565
  }
528
566
  onBlur() {
@@ -537,126 +575,73 @@ class LinkPopupComponent {
537
575
  this.closed.emit();
538
576
  }
539
577
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LinkPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
540
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: LinkPopupComponent, isStandalone: true, selector: "bonc-link-popup", inputs: { field: "field", maxRows: "maxRows", minRows: "minRows", linkTitle: "linkTitle", locale: "locale", link: "link" }, outputs: { linkChange: "linkChange", startEditing: "startEditing", changed: "changed", blurred: "blurred", open: "open", closed: "closed" }, ngImport: i0, template: "<bonc-translation-input *ngIf=\"field === TextEditorField.Input\"\n (click)=\"showPopup()\"\n [text]=\"linkTitle\"\n [locale]=\"locale\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n</bonc-translation-input>\n\n<bonc-translation-textarea *ngIf=\"field === TextEditorField.Textarea\"\n (click)=\"showPopup()\"\n [text]=\"linkTitle\"\n [locale]=\"locale\"\n [minRows]=\"minRows\"\n [maxRows]=\"maxRows\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n</bonc-translation-textarea>\n\n<div *ngIf=\"popupIsShown\">\n <div class=\"popup\">\n <a (click)=\"hidePopup()\" class=\"close\">close</a>\n\n <label>Link\n <input [(ngModel)]=\"link[locale]\"\n (click)=\"onClick()\"\n (keyup)=\"onChange()\"\n (blur)=\"onBlur()\">\n </label>\n\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;position:relative}:host translation-input{z-index:1}.popup{position:absolute;display:flex;flex-direction:column;justify-content:center;width:350px;padding:20px;box-sizing:border-box;color:#000;background-color:#fff;z-index:5;border:2px solid black}.popup a{cursor:pointer}.popup a:hover{background-color:#cf0}.popup a.close{position:absolute;top:10px;right:10px}.popup label+label{margin-top:20px}.popup input{width:100%;margin-top:5px;padding:6px;font-size:12px;box-sizing:border-box;border:1px solid #222}.popup input:hover,.popup input:focus{background-color:silver;color:#333;border-color:silver;outline:none}button{display:block;color:#fff;background-color:transparent;box-shadow:none;border:none;outline:none;text-decoration:none;cursor:pointer;padding:3pt 10pt;text-align:center;-webkit-user-select:none;user-select:none}button:hover{border-color:#333;background-color:#333}button:disabled{color:gray}button{color:#000;cursor:pointer}button:hover{background-color:#cf0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["minRows", "maxRows", "text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }] });
578
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: LinkPopupComponent, isStandalone: true, selector: "bonc-link-popup", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, minRows: { classPropertyName: "minRows", publicName: "minRows", isSignal: true, isRequired: false, transformFunction: null }, linkTitle: { classPropertyName: "linkTitle", publicName: "linkTitle", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { linkChange: "linkChange", startEditing: "startEditing", changed: "changed", blurred: "blurred", open: "open", closed: "closed" }, ngImport: i0, template: "@switch (field()) {\n @case (TextEditorField.Input) {\n <bonc-translation-input (click)=\"showPopup()\"\n [text]=\"linkTitle()\"\n [locale]=\"locale()\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n </bonc-translation-input>\n }\n @case (TextEditorField.Textarea) {\n <bonc-translation-textarea (click)=\"showPopup()\"\n [text]=\"linkTitle()\"\n [locale]=\"locale()\"\n [minRows]=\"minRows()\"\n [maxRows]=\"maxRows()\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n </bonc-translation-textarea>\n }\n}\n\n@if (popupIsShown) {\n <div>\n <div class=\"popup\">\n <a (click)=\"hidePopup()\" class=\"close\">close</a>\n\n <label>Link\n <input [(ngModel)]=\"link()[locale()]\"\n (click)=\"onClick()\"\n (keyup)=\"onChange()\"\n (blur)=\"onBlur()\">\n </label>\n\n </div>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;position:relative}:host translation-input{z-index:1}.popup{position:absolute;display:flex;flex-direction:column;justify-content:center;width:350px;padding:20px;box-sizing:border-box;color:#000;background-color:#fff;z-index:5;border:2px solid black}.popup a{cursor:pointer}.popup a:hover{background-color:#cf0}.popup a.close{position:absolute;top:10px;right:10px}.popup label+label{margin-top:20px}.popup input{width:100%;margin-top:5px;padding:6px;font-size:12px;box-sizing:border-box;border:1px solid #222}.popup input:hover,.popup input:focus{background-color:silver;color:#333;border-color:silver;outline:none}button{display:block;color:#fff;background-color:transparent;box-shadow:none;border:none;outline:none;text-decoration:none;cursor:pointer;padding:3pt 10pt;text-align:center;-webkit-user-select:none;user-select:none}button:hover{border-color:#333;background-color:#333}button:disabled{color:gray}button{color:#000;cursor:pointer}button:hover{background-color:#cf0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["text", "locale", "minRows", "maxRows", "device"], outputs: ["startEditing", "changed", "blurred"] }] });
541
579
  }
542
580
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LinkPopupComponent, decorators: [{
543
581
  type: Component,
544
- args: [{ selector: 'bonc-link-popup', standalone: true, imports: [CommonModule, FormsModule, TranslationInputComponent, TranslationTextareaComponent], template: "<bonc-translation-input *ngIf=\"field === TextEditorField.Input\"\n (click)=\"showPopup()\"\n [text]=\"linkTitle\"\n [locale]=\"locale\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n</bonc-translation-input>\n\n<bonc-translation-textarea *ngIf=\"field === TextEditorField.Textarea\"\n (click)=\"showPopup()\"\n [text]=\"linkTitle\"\n [locale]=\"locale\"\n [minRows]=\"minRows\"\n [maxRows]=\"maxRows\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n</bonc-translation-textarea>\n\n<div *ngIf=\"popupIsShown\">\n <div class=\"popup\">\n <a (click)=\"hidePopup()\" class=\"close\">close</a>\n\n <label>Link\n <input [(ngModel)]=\"link[locale]\"\n (click)=\"onClick()\"\n (keyup)=\"onChange()\"\n (blur)=\"onBlur()\">\n </label>\n\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;position:relative}:host translation-input{z-index:1}.popup{position:absolute;display:flex;flex-direction:column;justify-content:center;width:350px;padding:20px;box-sizing:border-box;color:#000;background-color:#fff;z-index:5;border:2px solid black}.popup a{cursor:pointer}.popup a:hover{background-color:#cf0}.popup a.close{position:absolute;top:10px;right:10px}.popup label+label{margin-top:20px}.popup input{width:100%;margin-top:5px;padding:6px;font-size:12px;box-sizing:border-box;border:1px solid #222}.popup input:hover,.popup input:focus{background-color:silver;color:#333;border-color:silver;outline:none}button{display:block;color:#fff;background-color:transparent;box-shadow:none;border:none;outline:none;text-decoration:none;cursor:pointer;padding:3pt 10pt;text-align:center;-webkit-user-select:none;user-select:none}button:hover{border-color:#333;background-color:#333}button:disabled{color:gray}button{color:#000;cursor:pointer}button:hover{background-color:#cf0}\n"] }]
545
- }], propDecorators: { linkChange: [{
546
- type: Output
547
- }], startEditing: [{
548
- type: Output
549
- }], changed: [{
550
- type: Output
551
- }], blurred: [{
552
- type: Output
553
- }], open: [{
554
- type: Output
555
- }], closed: [{
556
- type: Output
557
- }], field: [{
558
- type: Input,
559
- args: [{ required: true }]
560
- }], maxRows: [{
561
- type: Input
562
- }], minRows: [{
563
- type: Input
564
- }], linkTitle: [{
565
- type: Input,
566
- args: [{ required: true }]
567
- }], locale: [{
568
- type: Input,
569
- args: [{ required: true }]
570
- }], link: [{
571
- type: Input,
572
- args: [{ required: true }]
573
- }] } });
574
-
575
- class FormControlsComponent {
576
- editable;
577
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
578
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: FormControlsComponent, isStandalone: true, selector: "bonc-form-controls", inputs: { editable: "editable" }, ngImport: i0, template: "<div [class.edit]=\"editable.inEditMode\" class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div [class.hidden]=\"!editable.inEditMode\" class=\"controls\">\n <a *ngIf=\"!editable.isDirty\" (click)=\"editable.cancel()\" class=\"close\">close</a>\n <ng-container *ngIf=\"editable.isDirty\">\n <a (click)=\"editable.cancel()\">reset</a>\n <a (click)=\"editable.requestSave()\" class=\"apply\">apply</a>\n </ng-container>\n</div>\n", styles: [":host{display:flex;background-color:#222;color:silver}.content{flex:1;padding:10px}.content.edit{background-color:#111}.controls{display:flex;flex-direction:column;width:100px;background-color:#111}.controls.hidden{opacity:0;pointer-events:none}.controls a{display:block}.controls a.apply{flex:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
579
- }
580
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, decorators: [{
581
- type: Component,
582
- args: [{ selector: 'bonc-form-controls', standalone: true, imports: [CommonModule], template: "<div [class.edit]=\"editable.inEditMode\" class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div [class.hidden]=\"!editable.inEditMode\" class=\"controls\">\n <a *ngIf=\"!editable.isDirty\" (click)=\"editable.cancel()\" class=\"close\">close</a>\n <ng-container *ngIf=\"editable.isDirty\">\n <a (click)=\"editable.cancel()\">reset</a>\n <a (click)=\"editable.requestSave()\" class=\"apply\">apply</a>\n </ng-container>\n</div>\n", styles: [":host{display:flex;background-color:#222;color:silver}.content{flex:1;padding:10px}.content.edit{background-color:#111}.controls{display:flex;flex-direction:column;width:100px;background-color:#111}.controls.hidden{opacity:0;pointer-events:none}.controls a{display:block}.controls a.apply{flex:1}\n"] }]
583
- }], propDecorators: { editable: [{
584
- type: Input,
585
- args: [{ required: true }]
586
- }] } });
582
+ args: [{ selector: "bonc-link-popup", imports: [FormsModule, TranslationInputComponent, TranslationTextareaComponent], template: "@switch (field()) {\n @case (TextEditorField.Input) {\n <bonc-translation-input (click)=\"showPopup()\"\n [text]=\"linkTitle()\"\n [locale]=\"locale()\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n </bonc-translation-input>\n }\n @case (TextEditorField.Textarea) {\n <bonc-translation-textarea (click)=\"showPopup()\"\n [text]=\"linkTitle()\"\n [locale]=\"locale()\"\n [minRows]=\"minRows()\"\n [maxRows]=\"maxRows()\"\n (startEditing)=\"onClick()\"\n (changed)=\"onChange()\"\n (blurred)=\"onBlur()\">\n </bonc-translation-textarea>\n }\n}\n\n@if (popupIsShown) {\n <div>\n <div class=\"popup\">\n <a (click)=\"hidePopup()\" class=\"close\">close</a>\n\n <label>Link\n <input [(ngModel)]=\"link()[locale()]\"\n (click)=\"onClick()\"\n (keyup)=\"onChange()\"\n (blur)=\"onBlur()\">\n </label>\n\n </div>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;position:relative}:host translation-input{z-index:1}.popup{position:absolute;display:flex;flex-direction:column;justify-content:center;width:350px;padding:20px;box-sizing:border-box;color:#000;background-color:#fff;z-index:5;border:2px solid black}.popup a{cursor:pointer}.popup a:hover{background-color:#cf0}.popup a.close{position:absolute;top:10px;right:10px}.popup label+label{margin-top:20px}.popup input{width:100%;margin-top:5px;padding:6px;font-size:12px;box-sizing:border-box;border:1px solid #222}.popup input:hover,.popup input:focus{background-color:silver;color:#333;border-color:silver;outline:none}button{display:block;color:#fff;background-color:transparent;box-shadow:none;border:none;outline:none;text-decoration:none;cursor:pointer;padding:3pt 10pt;text-align:center;-webkit-user-select:none;user-select:none}button:hover{border-color:#333;background-color:#333}button:disabled{color:gray}button{color:#000;cursor:pointer}button:hover{background-color:#cf0}\n"] }]
583
+ }], propDecorators: { linkChange: [{ type: i0.Output, args: ["linkChange"] }], startEditing: [{ type: i0.Output, args: ["startEditing"] }], changed: [{ type: i0.Output, args: ["changed"] }], blurred: [{ type: i0.Output, args: ["blurred"] }], open: [{ type: i0.Output, args: ["open"] }], closed: [{ type: i0.Output, args: ["closed"] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], linkTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkTitle", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: true }] }] } });
587
584
 
588
585
  class FileUploaderComponent {
589
- fileInput;
590
- srcChange = new EventEmitter();
591
- uploadUrlMap;
592
- progress = 0;
593
- isUploading = false;
594
- autoplay = true;
595
- clipStyle;
596
- fileTypeMask = undefined;
597
- _uploadTypes = [];
598
- _src;
586
+ fileInput = viewChild.required("fileInput");
587
+ srcChange = output();
588
+ uploadUrlMap = input.required(...(ngDevMode ? [{ debugName: "uploadUrlMap" }] : []));
589
+ src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
590
+ uploadTypes = input([], ...(ngDevMode ? [{ debugName: "uploadTypes" }] : []));
591
+ fileTypeMask = computed(() => {
592
+ const types = this.uploadTypes();
593
+ return types.length === 0 ? undefined : types.join(",");
594
+ }, ...(ngDevMode ? [{ debugName: "fileTypeMask" }] : []));
595
+ progress = signal(0, ...(ngDevMode ? [{ debugName: "progress" }] : []));
596
+ isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
597
+ clipStyle = signal(undefined, ...(ngDevMode ? [{ debugName: "clipStyle" }] : []));
599
598
  sanitizer = inject(DomSanitizer);
600
599
  http = inject(HttpClient);
601
- cd = inject(ChangeDetectorRef);
602
- set src(newSrc) {
603
- if (this._src === newSrc)
604
- return;
605
- this._src = newSrc;
606
- }
607
- get src() {
608
- return this._src;
609
- }
610
- set uploadTypes(newUploadType) {
611
- this._uploadTypes.splice(0, this._uploadTypes.length);
612
- this._uploadTypes.push(...newUploadType);
613
- this.fileTypeMask = this._uploadTypes.length === 0 ? undefined : this._uploadTypes.join(',');
614
- this.cd.detectChanges();
615
- }
616
- get uploadTypes() {
617
- return this._uploadTypes;
618
- }
619
600
  onFileSelect(fileInput) {
620
- if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1)
601
+ if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1) {
621
602
  return;
603
+ }
622
604
  const file = fileInput.files[0];
623
- const uploadUrl = this.uploadUrlMap.get(file.type) ?? this.uploadUrlMap.get("");
605
+ const uploadUrl = this.uploadUrlMap().get(file.type) ?? this.uploadUrlMap().get("");
624
606
  if (uploadUrl === undefined || uploadUrl === null) {
625
607
  console.error(`upload map doesn't have url for type '${file.type}'`);
626
608
  return;
627
609
  }
628
610
  const formData = new FormData();
629
- formData.append('file', file);
630
- this.progress = 0;
631
- this.isUploading = true;
611
+ formData.append("file", file);
612
+ this.progress.set(0);
613
+ this.isUploading.set(true);
632
614
  this.updateClip();
633
- const request = new HttpRequest('POST', uploadUrl, formData, {
634
- reportProgress: true
615
+ const request = new HttpRequest("POST", uploadUrl, formData, {
616
+ reportProgress: true,
635
617
  });
636
618
  this.http
637
619
  .request(request)
638
- .pipe(map(event => this.getEventMessage(event)),
620
+ .pipe(map((event) => this.getEventMessage(event)),
639
621
  // tap(message => this.showProgress(message)),
640
622
  last(), // return last (completed) message to caller
641
- catchError(this.handleError(file))).subscribe(() => { this.isUploading = false; });
623
+ catchError(this.handleError(file)))
624
+ .subscribe(() => {
625
+ this.isUploading.set(false);
626
+ });
642
627
  }
643
628
  selectFile(event) {
644
- // ignore buble click on file picker
645
- if (event.target === this.fileInput.nativeElement)
629
+ if (event.target === this.fileInput().nativeElement) {
646
630
  return;
647
- this.fileInput.nativeElement.click();
631
+ }
632
+ this.fileInput().nativeElement.click();
648
633
  }
649
634
  getEventMessage(event) {
650
635
  switch (event.type) {
651
636
  case HttpEventType.Sent:
652
637
  break;
653
638
  case HttpEventType.UploadProgress:
654
- this.progress = event.total === undefined ? 0.5 : event.loaded / event.total;
639
+ this.progress.set(event.total === undefined ? 0.5 : event.loaded / event.total);
655
640
  this.updateClip();
656
641
  break;
657
642
  case HttpEventType.Response:
658
643
  if (event.body === null || event.body === undefined) {
659
- console.warn(`empty body from uploader`);
644
+ console.warn("empty body from uploader");
660
645
  break;
661
646
  }
662
647
  this.srcChange.emit(event.body);
@@ -664,10 +649,9 @@ class FileUploaderComponent {
664
649
  default:
665
650
  break;
666
651
  }
667
- this.cd.detectChanges();
668
652
  }
669
653
  updateClip() {
670
- this.clipStyle = this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`);
654
+ this.clipStyle.set(this.sanitizer.bypassSecurityTrustStyle("inset(0px 100% 0px 0%)"));
671
655
  }
672
656
  handleError(file) {
673
657
  const func = (error, p2) => {
@@ -679,85 +663,62 @@ class FileUploaderComponent {
679
663
  return func;
680
664
  }
681
665
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
682
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: FileUploaderComponent, isStandalone: true, selector: "bonc-file-uploader", inputs: { uploadUrlMap: "uploadUrlMap", src: "src", uploadTypes: "uploadTypes" }, outputs: { srcChange: "srcChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, static: true }], ngImport: i0, template: "<div *ngIf=\"src === undefined || src.url === undefined || src?.url?.length === 0\">Upload file</div>\n\n<ng-content></ng-content>\n\n<div (click)=\"selectFile($event)\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n <span *ngIf=\"isUploading\"\n [style.clip-path]=\"clipStyle\"\n [style.-webkit-clip-path]=\"clipStyle\"\n class=\"loader\"></span>\n\n <span *ngIf=\"isUploading\" class=\"loader-text\">\n <span *ngIf=\"progress < 1\">{{100 * progress | number: '1.0-0'}}%</span>\n <span *ngIf=\"progress >= 1\">converting</span>\n </span>\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }] });
666
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FileUploaderComponent, isStandalone: true, selector: "bonc-file-uploader", inputs: { uploadUrlMap: { classPropertyName: "uploadUrlMap", publicName: "uploadUrlMap", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, uploadTypes: { classPropertyName: "uploadTypes", publicName: "uploadTypes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { srcChange: "srcChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (src() === undefined || src()?.url === undefined || src()?.url?.length === 0) {\n <div>Upload file</div>\n}\n\n<ng-content></ng-content>\n\n<div (click)=\"selectFile($event)\" (keydown.enter)=\"selectFile($event)\" role=\"button\" tabindex=\"0\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask()\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n @if (isUploading()) {\n <span [style.clip-path]=\"clipStyle()\"\n [style.-webkit-clip-path]=\"clipStyle()\"\n class=\"loader\"></span>\n <span class=\"loader-text\">\n @if (progress() < 1) {\n <span>{{100 * progress() | number: '1.0-0'}}%</span>\n } @else {\n <span>converting</span>\n }\n </span>\n }\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"], dependencies: [{ kind: "pipe", type: DecimalPipe, name: "number" }] });
683
667
  }
684
668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileUploaderComponent, decorators: [{
685
669
  type: Component,
686
- args: [{ selector: 'bonc-file-uploader', standalone: true, imports: [CommonModule, DecimalPipe], template: "<div *ngIf=\"src === undefined || src.url === undefined || src?.url?.length === 0\">Upload file</div>\n\n<ng-content></ng-content>\n\n<div (click)=\"selectFile($event)\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n <span *ngIf=\"isUploading\"\n [style.clip-path]=\"clipStyle\"\n [style.-webkit-clip-path]=\"clipStyle\"\n class=\"loader\"></span>\n\n <span *ngIf=\"isUploading\" class=\"loader-text\">\n <span *ngIf=\"progress < 1\">{{100 * progress | number: '1.0-0'}}%</span>\n <span *ngIf=\"progress >= 1\">converting</span>\n </span>\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"] }]
687
- }], propDecorators: { fileInput: [{
688
- type: ViewChild,
689
- args: ['fileInput', { static: true }]
690
- }], srcChange: [{
691
- type: Output
692
- }], uploadUrlMap: [{
693
- type: Input,
694
- args: [{ required: true }]
695
- }], src: [{
696
- type: Input
697
- }], uploadTypes: [{
698
- type: Input
699
- }] } });
670
+ args: [{ selector: "bonc-file-uploader", imports: [DecimalPipe], template: "@if (src() === undefined || src()?.url === undefined || src()?.url?.length === 0) {\n <div>Upload file</div>\n}\n\n<ng-content></ng-content>\n\n<div (click)=\"selectFile($event)\" (keydown.enter)=\"selectFile($event)\" role=\"button\" tabindex=\"0\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask()\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n @if (isUploading()) {\n <span [style.clip-path]=\"clipStyle()\"\n [style.-webkit-clip-path]=\"clipStyle()\"\n class=\"loader\"></span>\n <span class=\"loader-text\">\n @if (progress() < 1) {\n <span>{{100 * progress() | number: '1.0-0'}}%</span>\n } @else {\n <span>converting</span>\n }\n </span>\n }\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"] }]
671
+ }], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ["fileInput", { isSignal: true }] }], srcChange: [{ type: i0.Output, args: ["srcChange"] }], uploadUrlMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadUrlMap", required: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], uploadTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadTypes", required: false }] }] } });
700
672
 
701
- const uploadMap = new Map();
702
- uploadMap.set('', `/api/admin/upload/image?width=${1200}&height=${630}&format=image/jpeg`);
703
- class SeoFormComponent extends FormBaseComponent {
704
- uploadMap = uploadMap;
705
- _pageId = '';
706
- ogImageUploadUrl = '';
707
- label = '';
708
- ngOnInit() {
673
+ class FormControlsComponent {
674
+ editable = input.required(...(ngDevMode ? [{ debugName: "editable" }] : []));
675
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
676
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FormControlsComponent, isStandalone: true, selector: "bonc-form-controls", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [class.edit]=\"editable().inEditMode()\" class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div [class.hidden]=\"!editable().inEditMode()\" class=\"controls\">\n @if (!editable().isDirty()) {\n <a (click)=\"editable().cancel()\" class=\"close\">close</a>\n } @else {\n <a (click)=\"editable().cancel()\">reset</a>\n <a (click)=\"editable().requestSave()\" class=\"apply\">apply</a>\n }\n</div>\n", styles: [":host{display:flex;background-color:#222;color:silver}.content{flex:1;padding:10px}.content.edit{background-color:#111}.controls{display:flex;flex-direction:column;width:100px;background-color:#111}.controls.hidden{opacity:0;pointer-events:none}.controls a{display:block}.controls a.apply{flex:1}\n"] });
677
+ }
678
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, decorators: [{
679
+ type: Component,
680
+ args: [{ selector: "bonc-form-controls", template: "<div [class.edit]=\"editable().inEditMode()\" class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div [class.hidden]=\"!editable().inEditMode()\" class=\"controls\">\n @if (!editable().isDirty()) {\n <a (click)=\"editable().cancel()\" class=\"close\">close</a>\n } @else {\n <a (click)=\"editable().cancel()\">reset</a>\n <a (click)=\"editable().requestSave()\" class=\"apply\">apply</a>\n }\n</div>\n", styles: [":host{display:flex;background-color:#222;color:silver}.content{flex:1;padding:10px}.content.edit{background-color:#111}.controls{display:flex;flex-direction:column;width:100px;background-color:#111}.controls.hidden{opacity:0;pointer-events:none}.controls a{display:block}.controls a.apply{flex:1}\n"] }]
681
+ }], propDecorators: { editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: true }] }] } });
682
+
683
+ class FileFormComponent extends FormBaseComponent {
684
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
685
+ uploadTypes = input.required(...(ngDevMode ? [{ debugName: "uploadTypes" }] : []));
686
+ uploadMap = input.required(...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
687
+ constructor() {
688
+ super();
709
689
  this.editable.externalSaveCall.subscribe(() => {
710
690
  this.editable.save();
711
691
  });
712
692
  }
713
- set pageId(value) {
714
- this._pageId = value;
715
- this.ogImageUploadUrl = `/api/admin/page/Og-Image?pageId=${this.pageId}`; // todo: replace with link to single image api
716
- }
717
- get pageId() {
718
- return this._pageId;
719
- }
720
- ResToSrc(res) {
721
- return res.url;
722
- }
723
- replaceImage($event) {
693
+ onFileUploaded(fileSrc) {
724
694
  this.editable.startEditing();
725
- if (this.editable.value !== undefined) {
726
- this.editable.value.image[this.locale] = $event;
727
- }
695
+ const svgSrc = fileSrc;
696
+ this.editable.value = svgSrc;
728
697
  this.editable.updateDirty();
729
698
  }
730
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
731
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: SeoFormComponent, isStandalone: true, selector: "bonc-seo-form", inputs: { label: "label", pageId: "pageId" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls *ngIf=\"editable.value\" [editable]=\"editable\">\n <h2 *ngIf=\"label\">{{label}}</h2>\n\n <div class=\"form-group\">\n <label>Title</label>\n <bonc-translation-input [text]=\"editable.value.title\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n </div>\n\n <div class=\"form-group\">\n <label>Description</label>\n <bonc-translation-textarea [text]=\"editable.value.description\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n </div>\n\n <div class=\"form-group\">\n <label>Share image | 1200x630</label>\n\n <bonc-file-uploader [src]=\"editable.value.image[locale]\"\n [uploadTypes]=\"['image/png', 'image/jpeg']\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"replaceImage($event)\">\n <img *ngIf=\"editable.value.image[locale]?.url?.length ?? 0 > 0\" [src]=\"editable.value.image[locale].url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value.image[locale]?.url?.length ?? 0 > 0\" [href]=\"editable.value.image[locale].url\" download target=\"_blank\">Download</a>\n </div>\n\n</bonc-form-controls>\n\n<div *ngIf=\"!editable.value\">Editable Value canot be null or undefined</div>\n", styles: [":host{display:block}bonc-one-image-form{max-width:600px}img{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["minRows", "maxRows", "text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
699
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
700
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FileFormComponent, isStandalone: true, selector: "bonc-file-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, uploadTypes: { classPropertyName: "uploadTypes", publicName: "uploadTypes", isSignal: true, isRequired: true, transformFunction: null }, uploadMap: { classPropertyName: "uploadMap", publicName: "uploadMap", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"uploadTypes()\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @let url = editable.value?.url;\n @if (url) {\n <div>{{ url }}</div>\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block}a{color:silver}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
732
701
  }
733
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, decorators: [{
702
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, decorators: [{
734
703
  type: Component,
735
- args: [{ selector: 'bonc-seo-form', standalone: true, imports: [CommonModule, FormControlsComponent, TranslationInputComponent, TranslationTextareaComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls *ngIf=\"editable.value\" [editable]=\"editable\">\n <h2 *ngIf=\"label\">{{label}}</h2>\n\n <div class=\"form-group\">\n <label>Title</label>\n <bonc-translation-input [text]=\"editable.value.title\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n </div>\n\n <div class=\"form-group\">\n <label>Description</label>\n <bonc-translation-textarea [text]=\"editable.value.description\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n </div>\n\n <div class=\"form-group\">\n <label>Share image | 1200x630</label>\n\n <bonc-file-uploader [src]=\"editable.value.image[locale]\"\n [uploadTypes]=\"['image/png', 'image/jpeg']\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"replaceImage($event)\">\n <img *ngIf=\"editable.value.image[locale]?.url?.length ?? 0 > 0\" [src]=\"editable.value.image[locale].url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value.image[locale]?.url?.length ?? 0 > 0\" [href]=\"editable.value.image[locale].url\" download target=\"_blank\">Download</a>\n </div>\n\n</bonc-form-controls>\n\n<div *ngIf=\"!editable.value\">Editable Value canot be null or undefined</div>\n", styles: [":host{display:block}bonc-one-image-form{max-width:600px}img{display:block;width:100%}\n"] }]
736
- }], propDecorators: { label: [{
737
- type: Input
738
- }], pageId: [{
739
- type: Input
740
- }] } });
704
+ args: [{ selector: "bonc-file-form", imports: [FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"uploadTypes()\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @let url = editable.value?.url;\n @if (url) {\n <div>{{ url }}</div>\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block}a{color:silver}\n"] }]
705
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], uploadTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadTypes", required: true }] }], uploadMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadMap", required: true }] }] } });
741
706
 
742
- class SvgFormComponent extends FormBaseComponent {
743
- cd = inject(ChangeDetectorRef);
744
- SvgMime = 'image/svg+xml';
745
- label = '';
746
- uploadMap = new Map();
747
- _uploadUrl = '/api/admin/upload/image/svg';
707
+ class LottieFormComponent extends FormBaseComponent {
708
+ LottieMimeType = "application/json";
709
+ // todo: convert to signal
710
+ animOptions;
711
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
712
+ uploadMap = input.required(...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
748
713
  constructor() {
749
714
  super();
750
- this.uploadMap.set(this.SvgMime, this._uploadUrl);
751
- }
752
- ngOnInit() {
753
715
  this.editable.externalSaveCall.subscribe(() => {
754
716
  this.editable.save();
755
717
  });
756
- }
757
- set uploadUrl(newValue) {
758
- this._uploadUrl = newValue;
759
- this.uploadMap.set(this.SvgMime, this._uploadUrl);
760
- this.cd.detectChanges();
718
+ this.editable.valueChange.subscribe((x) => {
719
+ const url = x?.url ?? "";
720
+ this.animOptions = url.length === 0 ? undefined : { path: url };
721
+ });
761
722
  }
762
723
  onFileUploaded(fileSrc) {
763
724
  this.editable.startEditing();
@@ -765,51 +726,31 @@ class SvgFormComponent extends FormBaseComponent {
765
726
  this.editable.value = svgSrc;
766
727
  this.editable.updateDirty();
767
728
  }
768
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
769
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: SvgFormComponent, isStandalone: true, selector: "bonc-svg-form", inputs: { label: "label", uploadUrl: "uploadUrl" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[SvgMime]\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <img *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [src]=\"editable.value?.url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
729
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
730
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: LottieFormComponent, isStandalone: true, selector: "bonc-lottie-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, uploadMap: { classPropertyName: "uploadMap", publicName: "uploadMap", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"[LottieMimeType]\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @if (animOptions) {\n <ng-lottie [options]=\"animOptions\"></ng-lottie>\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block;max-width:600px}a{color:silver}ng-lottie{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }, { kind: "component", type: LottieComponent, selector: "ng-lottie", inputs: ["width", "height"] }] });
770
731
  }
771
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, decorators: [{
732
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, decorators: [{
772
733
  type: Component,
773
- args: [{ selector: 'bonc-svg-form', standalone: true, imports: [CommonModule, FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[SvgMime]\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <img *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [src]=\"editable.value?.url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"] }]
774
- }], ctorParameters: () => [], propDecorators: { label: [{
775
- type: Input
776
- }], uploadUrl: [{
777
- type: Input
778
- }] } });
734
+ args: [{ selector: "bonc-lottie-form", imports: [FormControlsComponent, FileUploaderComponent, LottieComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"[LottieMimeType]\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @if (animOptions) {\n <ng-lottie [options]=\"animOptions\"></ng-lottie>\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block;max-width:600px}a{color:silver}ng-lottie{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block}\n"] }]
735
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], uploadMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadMap", required: true }] }] } });
779
736
 
780
- const DefaultImageMimeTypes = ['image/png', 'image/jpeg'];
737
+ const DefaultImageMimeTypes = ["image/png", "image/jpeg"];
781
738
  class OneImageFormComponent extends FormBaseComponent {
782
- cd = inject(ChangeDetectorRef);
783
- uploadMap = new Map();
784
- _mimeTypes = DefaultImageMimeTypes;
785
- _uploadUrl = '/api/admin/upload/image';
786
- _label = '';
739
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
740
+ uploadUrl = input("/api/admin/upload/image", ...(ngDevMode ? [{ debugName: "uploadUrl" }] : []));
741
+ mimeTypes = input(DefaultImageMimeTypes, ...(ngDevMode ? [{ debugName: "mimeTypes" }] : []));
742
+ uploadMap = computed(() => {
743
+ const m = new Map();
744
+ m.set("", this.uploadUrl());
745
+ return m;
746
+ }, ...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
747
+ destroyRef = inject(DestroyRef);
787
748
  constructor() {
788
749
  super();
789
- this.uploadMap.set('', this._uploadUrl);
790
- }
791
- ngOnInit() {
792
- this.editable.externalSaveCall.subscribe(() => {
750
+ const sub = this.editable.externalSaveCall.subscribe(() => {
793
751
  this.editable.save();
794
752
  });
795
- }
796
- set label(newValue) {
797
- this._label = newValue;
798
- }
799
- get label() {
800
- return this._label;
801
- }
802
- set uploadUrl(newValue) {
803
- this._uploadUrl = newValue;
804
- this.uploadMap.set('', this._uploadUrl);
805
- this.cd.detectChanges();
806
- }
807
- set mimeTypes(newValue) {
808
- this._mimeTypes = newValue.length === 0 ? DefaultImageMimeTypes : newValue;
809
- this.cd.detectChanges();
810
- }
811
- get mimeTypes() {
812
- return this._mimeTypes;
753
+ this.destroyRef.onDestroy(() => sub.unsubscribe());
813
754
  }
814
755
  onFileUploaded(fileSrc) {
815
756
  this.editable.startEditing();
@@ -818,50 +759,77 @@ class OneImageFormComponent extends FormBaseComponent {
818
759
  this.editable.updateDirty();
819
760
  }
820
761
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OneImageFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
821
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: OneImageFormComponent, isStandalone: true, selector: "bonc-one-image-form", inputs: { label: "label", uploadUrl: "uploadUrl", mimeTypes: "mimeTypes" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"mimeTypes\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <img *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [src]=\"editable.value?.url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
762
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OneImageFormComponent, isStandalone: true, selector: "bonc-one-image-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, uploadUrl: { classPropertyName: "uploadUrl", publicName: "uploadUrl", isSignal: true, isRequired: false, transformFunction: null }, mimeTypes: { classPropertyName: "mimeTypes", publicName: "mimeTypes", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"mimeTypes()\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @let url = editable.value?.url;\n @if (url) {\n <img [src]=\"url\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
822
763
  }
823
764
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OneImageFormComponent, decorators: [{
824
765
  type: Component,
825
- args: [{ selector: 'bonc-one-image-form', standalone: true, imports: [CommonModule, FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"mimeTypes\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <img *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [src]=\"editable.value?.url\" />\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"] }]
826
- }], ctorParameters: () => [], propDecorators: { label: [{
827
- type: Input
828
- }], uploadUrl: [{
829
- type: Input
830
- }], mimeTypes: [{
831
- type: Input
832
- }] } });
766
+ args: [{ selector: "bonc-one-image-form", imports: [FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{ label() }}</label>\n }\n <bonc-file-uploader\n [src]=\"editable.value\"\n [uploadTypes]=\"mimeTypes()\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\"\n >\n @let url = editable.value?.url;\n @if (url) {\n <img [src]=\"url\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"] }]
767
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], uploadUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadUrl", required: false }] }], mimeTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "mimeTypes", required: false }] }] } });
833
768
 
834
- class FileFormComponent extends FormBaseComponent {
835
- label = '';
836
- ngOnInit() {
769
+ const defaultUploadMap = new Map();
770
+ defaultUploadMap.set("", `/api/admin/upload/image?width=${1200}&height=${630}&format=image/jpeg`);
771
+ class SeoFormComponent extends FormBaseComponent {
772
+ uploadMap = defaultUploadMap;
773
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
774
+ pageId = input("", ...(ngDevMode ? [{ debugName: "pageId" }] : []));
775
+ ogImageUploadUrl = computed(() => `/api/admin/page/Og-Image?pageId=${this.pageId()}`, ...(ngDevMode ? [{ debugName: "ogImageUploadUrl" }] : []));
776
+ constructor() {
777
+ super();
778
+ this.editable.externalSaveCall.subscribe(() => {
779
+ this.editable.save();
780
+ });
781
+ }
782
+ ResToSrc(res) {
783
+ return res.url;
784
+ }
785
+ replaceImage($event) {
786
+ this.editable.startEditing();
787
+ if (this.editable.value !== undefined) {
788
+ this.editable.value.image[this.locale()] = $event;
789
+ }
790
+ this.editable.updateDirty();
791
+ }
792
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SeoFormComponent, isStandalone: true, selector: "bonc-seo-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, pageId: { classPropertyName: "pageId", publicName: "pageId", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "@if (editable.value !== undefined && editable.value !== null) {\n <bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <h2>{{label()}}</h2>\n }\n\n <div class=\"form-group\">\n <label>Title</label>\n <bonc-translation-input [text]=\"editable.value.title\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n </div>\n\n <div class=\"form-group\">\n <label>Description</label>\n <bonc-translation-textarea [text]=\"editable.value.description\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n </div>\n\n <div class=\"form-group\">\n <label>Share image | 1200x630</label>\n\n <bonc-file-uploader [src]=\"editable.value.image[locale()]\"\n [uploadTypes]=\"['image/png', 'image/jpeg']\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"replaceImage($event)\">\n @let imageUrl = editable.value.image[locale()]?.url;\n @if (imageUrl) {\n <img [src]=\"imageUrl\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value.image[locale()]?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n </div>\n\n </bonc-form-controls>\n} @else {\n <div>Editable Value canot be null or undefined</div>\n}\n", styles: [":host{display:block}bonc-one-image-form{max-width:600px}img{display:block;width:100%}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["text", "locale", "minRows", "maxRows", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
794
+ }
795
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, decorators: [{
796
+ type: Component,
797
+ args: [{ selector: "bonc-seo-form", imports: [
798
+ FormControlsComponent,
799
+ TranslationInputComponent,
800
+ TranslationTextareaComponent,
801
+ FileUploaderComponent,
802
+ ], hostDirectives: [EditableDirective], template: "@if (editable.value !== undefined && editable.value !== null) {\n <bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <h2>{{label()}}</h2>\n }\n\n <div class=\"form-group\">\n <label>Title</label>\n <bonc-translation-input [text]=\"editable.value.title\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n </div>\n\n <div class=\"form-group\">\n <label>Description</label>\n <bonc-translation-textarea [text]=\"editable.value.description\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n </div>\n\n <div class=\"form-group\">\n <label>Share image | 1200x630</label>\n\n <bonc-file-uploader [src]=\"editable.value.image[locale()]\"\n [uploadTypes]=\"['image/png', 'image/jpeg']\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"replaceImage($event)\">\n @let imageUrl = editable.value.image[locale()]?.url;\n @if (imageUrl) {\n <img [src]=\"imageUrl\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value.image[locale()]?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n </div>\n\n </bonc-form-controls>\n} @else {\n <div>Editable Value canot be null or undefined</div>\n}\n", styles: [":host{display:block}bonc-one-image-form{max-width:600px}img{display:block;width:100%}\n"] }]
803
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], pageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageId", required: false }] }] } });
804
+
805
+ class SvgFormComponent extends FormBaseComponent {
806
+ SvgMime = "image/svg+xml";
807
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
808
+ uploadUrl = input("/api/admin/upload/image/svg", ...(ngDevMode ? [{ debugName: "uploadUrl" }] : []));
809
+ uploadMap = computed(() => {
810
+ const m = new Map();
811
+ m.set(this.SvgMime, this.uploadUrl());
812
+ return m;
813
+ }, ...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
814
+ constructor() {
815
+ super();
837
816
  this.editable.externalSaveCall.subscribe(() => {
838
817
  this.editable.save();
839
818
  });
840
819
  }
841
- uploadTypes = [];
842
- uploadMap = new Map();
843
- ;
844
820
  onFileUploaded(fileSrc) {
845
821
  this.editable.startEditing();
846
822
  const svgSrc = fileSrc;
847
823
  this.editable.value = svgSrc;
848
824
  this.editable.updateDirty();
849
825
  }
850
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
851
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: FileFormComponent, isStandalone: true, selector: "bonc-file-form", inputs: { label: "label", uploadTypes: "uploadTypes", uploadMap: "uploadMap" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"uploadTypes\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <div *ngIf=\"editable.value?.url?.length ?? 0 > 0\">{{editable.value?.url}}</div>\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n</bonc-form-controls>\n", styles: [":host{display:block}a{color:silver}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
826
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
827
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SvgFormComponent, isStandalone: true, selector: "bonc-svg-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, uploadUrl: { classPropertyName: "uploadUrl", publicName: "uploadUrl", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[SvgMime]\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\">\n @let url = editable.value?.url;\n @if (url) {\n <img [src]=\"url\" alt=\"\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }] });
852
828
  }
853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, decorators: [{
829
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, decorators: [{
854
830
  type: Component,
855
- args: [{ selector: 'bonc-file-form', standalone: true, imports: [CommonModule, FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"uploadTypes\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n <div *ngIf=\"editable.value?.url?.length ?? 0 > 0\">{{editable.value?.url}}</div>\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n</bonc-form-controls>\n", styles: [":host{display:block}a{color:silver}\n"] }]
856
- }], propDecorators: { label: [{
857
- type: Input
858
- }], uploadTypes: [{
859
- type: Input,
860
- args: [{ required: true }]
861
- }], uploadMap: [{
862
- type: Input,
863
- args: [{ required: true }]
864
- }] } });
831
+ args: [{ selector: "bonc-svg-form", imports: [FormControlsComponent, FileUploaderComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[SvgMime]\"\n [uploadUrlMap]=\"uploadMap()\"\n (srcChange)=\"onFileUploaded($event)\">\n @let url = editable.value?.url;\n @if (url) {\n <img [src]=\"url\" alt=\"\" />\n }\n </bonc-file-uploader>\n\n @let downloadUrl = editable.value?.url;\n @if (downloadUrl) {\n <a [href]=\"downloadUrl\" download target=\"_blank\">Download</a>\n }\n\n</bonc-form-controls>\n", styles: [":host{display:block}img{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block;width:100%}a{color:silver}\n"] }]
832
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], uploadUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadUrl", required: false }] }] } });
865
833
 
866
834
  var TextInputStyle;
867
835
  (function (TextInputStyle) {
@@ -872,336 +840,220 @@ var TextInputStyle;
872
840
 
873
841
  class TextFormComponent extends FormBaseComponent {
874
842
  TextInputStyle = TextInputStyle;
875
- label = '';
876
- type = TextInputStyle.SingleLine;
877
- ngOnInit() {
843
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
844
+ type = input(TextInputStyle.SingleLine, ...(ngDevMode ? [{ debugName: "type" }] : []));
845
+ constructor() {
846
+ super();
878
847
  this.editable.externalSaveCall.subscribe(() => {
879
848
  this.editable.save();
880
849
  });
881
850
  }
882
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TextFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
883
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TextFormComponent, isStandalone: true, selector: "bonc-text-form", inputs: { label: "label", type: "type" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n\n <input *ngIf=\"editable && type === TextInputStyle.SingleLine\"\n [(ngModel)]=\"editable.value\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\" />\n\n <textarea *ngIf=\"editable && type === TextInputStyle.MultiLine\"\n [(ngModel)]=\"editable.value\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\">\n </textarea>\n</bonc-form-controls>\n", styles: [":host{display:block}input,textarea{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}input:hover,textarea:hover{border-color:transparent;background-color:#333;cursor:pointer}input:focus,textarea:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}input,textarea{font-size:inherit}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }] });
851
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TextFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
852
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TextFormComponent, isStandalone: true, selector: "bonc-text-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n @if (editable) {\n @switch (type()) {\n @case (TextInputStyle.SingleLine) {\n <input [(ngModel)]=\"editable.value\"\n [attr.aria-label]=\"label()\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\" />\n }\n @case (TextInputStyle.MultiLine) {\n <textarea [(ngModel)]=\"editable.value\"\n [attr.aria-label]=\"label()\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\">\n </textarea>\n }\n }\n }\n</bonc-form-controls>\n", styles: [":host{display:block}input,textarea{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}input:hover,textarea:hover{border-color:transparent;background-color:#333;cursor:pointer}input:focus,textarea:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}input,textarea{font-size:inherit}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }] });
884
853
  }
885
854
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TextFormComponent, decorators: [{
886
855
  type: Component,
887
- args: [{ selector: 'bonc-text-form', standalone: true, imports: [CommonModule, FormsModule, FormControlsComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n\n <input *ngIf=\"editable && type === TextInputStyle.SingleLine\"\n [(ngModel)]=\"editable.value\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\" />\n\n <textarea *ngIf=\"editable && type === TextInputStyle.MultiLine\"\n [(ngModel)]=\"editable.value\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\">\n </textarea>\n</bonc-form-controls>\n", styles: [":host{display:block}input,textarea{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}input:hover,textarea:hover{border-color:transparent;background-color:#333;cursor:pointer}input:focus,textarea:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}input,textarea{font-size:inherit}\n"] }]
888
- }], propDecorators: { label: [{
889
- type: Input
890
- }], type: [{
891
- type: Input
892
- }] } });
893
-
894
- class LottieFormComponent extends FormBaseComponent {
895
- LottieMimeType = 'application/json';
896
- animOptions;
897
- label = '';
898
- ngOnInit() {
899
- this.editable.externalSaveCall.subscribe(() => {
900
- this.editable.save();
901
- });
902
- this.editable.valueChange.subscribe(x => {
903
- const url = x?.url ?? '';
904
- this.animOptions = url.length === 0 ? undefined : { path: url };
905
- });
906
- }
907
- uploadMap = new Map();
908
- ;
909
- onFileUploaded(fileSrc) {
910
- this.editable.startEditing();
911
- const svgSrc = fileSrc;
912
- this.editable.value = svgSrc;
913
- this.editable.updateDirty();
914
- }
915
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
916
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: LottieFormComponent, isStandalone: true, selector: "bonc-lottie-form", inputs: { label: "label", uploadMap: "uploadMap" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[LottieMimeType]\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n\n <ng-lottie *ngIf=\"animOptions\"\n [options]=\"animOptions\"></ng-lottie>\n\n\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n</bonc-form-controls>\n", styles: [":host{display:block;max-width:600px}a{color:silver}ng-lottie{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: FileUploaderComponent, selector: "bonc-file-uploader", inputs: ["uploadUrlMap", "src", "uploadTypes"], outputs: ["srcChange"] }, { kind: "component", type: LottieComponent, selector: "ng-lottie", inputs: ["width", "height"] }] });
917
- }
918
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, decorators: [{
919
- type: Component,
920
- args: [{ selector: 'bonc-lottie-form', standalone: true, imports: [CommonModule, FormControlsComponent, FileUploaderComponent, LottieComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n <bonc-file-uploader [src]=\"editable.value\"\n [uploadTypes]=\"[LottieMimeType]\"\n [uploadUrlMap]=\"uploadMap\"\n (srcChange)=\"onFileUploaded($event)\">\n\n <ng-lottie *ngIf=\"animOptions\"\n [options]=\"animOptions\"></ng-lottie>\n\n\n </bonc-file-uploader>\n\n <a *ngIf=\"editable.value?.url?.length ?? 0 > 0\" [href]=\"editable.value!.url\" download target=\"_blank\">Download</a>\n</bonc-form-controls>\n", styles: [":host{display:block;max-width:600px}a{color:silver}ng-lottie{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px;display:block}\n"] }]
921
- }], propDecorators: { label: [{
922
- type: Input
923
- }], uploadMap: [{
924
- type: Input,
925
- args: [{ required: true }]
926
- }] } });
856
+ args: [{ selector: "bonc-text-form", imports: [FormsModule, FormControlsComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n @if (editable) {\n @switch (type()) {\n @case (TextInputStyle.SingleLine) {\n <input [(ngModel)]=\"editable.value\"\n [attr.aria-label]=\"label()\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\" />\n }\n @case (TextInputStyle.MultiLine) {\n <textarea [(ngModel)]=\"editable.value\"\n [attr.aria-label]=\"label()\"\n (click)=\"editable.startEditing()\"\n (keyup)=\"editable.updateDirty()\"\n (blur)=\"editable.updateDirty()\">\n </textarea>\n }\n }\n }\n</bonc-form-controls>\n", styles: [":host{display:block}input,textarea{display:block;width:100%;padding:6px;color:silver;border:1px solid silver;background-color:transparent;box-sizing:border-box}input:hover,textarea:hover{border-color:transparent;background-color:#333;cursor:pointer}input:focus,textarea:focus{outline:none;color:#fff;border-color:transparent;background-color:#555}input,textarea{font-size:inherit}\n"] }]
857
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
927
858
 
928
859
  class TranslationFormComponent extends FormBaseComponent {
929
860
  TextEditorField = TextEditorField;
930
- field;
931
- label;
932
- ngOnInit() {
861
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
862
+ label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
863
+ constructor() {
864
+ super();
933
865
  this.editable.externalSaveCall.subscribe(() => {
934
866
  this.editable.save();
935
867
  });
936
868
  }
937
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
938
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TranslationFormComponent, isStandalone: true, selector: "bonc-translation-form", inputs: { field: "field", label: "label" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<bonc-form-controls *ngIf=\"editable.value\" [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n\n <bonc-translation-input *ngIf=\"field === TextEditorField.Input\"\n [text]=\"editable.value\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n\n\n <bonc-translation-textarea *ngIf=\"field === TextEditorField.Textarea\"\n [text]=\"editable.value\"\n [minRows]=\"2\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n</bonc-form-controls>\n\n<div *ngIf=\"editable.value===undefined\">Editable value canot be undefined</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["minRows", "maxRows", "text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }] });
869
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TranslationFormComponent, isStandalone: true, selector: "bonc-translation-form", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "@if (editable.value) {\n <bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n\n @switch (field()) {\n @case (TextEditorField.Input) {\n <bonc-translation-input [text]=\"editable.value\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n }\n @case (TextEditorField.Textarea) {\n <bonc-translation-textarea [text]=\"editable.value\"\n [minRows]=\"2\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n }\n }\n </bonc-form-controls>\n} @else {\n <div>Editable value canot be undefined</div>\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: FormControlsComponent, selector: "bonc-form-controls", inputs: ["editable"] }, { kind: "component", type: TranslationInputComponent, selector: "bonc-translation-input", inputs: ["text", "locale", "device"], outputs: ["startEditing", "changed", "blurred"] }, { kind: "component", type: TranslationTextareaComponent, selector: "bonc-translation-textarea", inputs: ["text", "locale", "minRows", "maxRows", "device"], outputs: ["startEditing", "changed", "blurred"] }] });
939
871
  }
940
872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationFormComponent, decorators: [{
941
873
  type: Component,
942
- args: [{ selector: 'bonc-translation-form', standalone: true, imports: [CommonModule, FormControlsComponent, TranslationInputComponent, TranslationTextareaComponent], hostDirectives: [EditableDirective], template: "<bonc-form-controls *ngIf=\"editable.value\" [editable]=\"editable\">\n <label *ngIf=\"label\">{{label}}</label>\n\n <bonc-translation-input *ngIf=\"field === TextEditorField.Input\"\n [text]=\"editable.value\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n\n\n <bonc-translation-textarea *ngIf=\"field === TextEditorField.Textarea\"\n [text]=\"editable.value\"\n [minRows]=\"2\"\n [locale]=\"locale\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n</bonc-form-controls>\n\n<div *ngIf=\"editable.value===undefined\">Editable value canot be undefined</div>\n", styles: [":host{display:block}\n"] }]
943
- }], propDecorators: { field: [{
944
- type: Input,
945
- args: [{ required: true }]
946
- }], label: [{
947
- type: Input
948
- }] } });
874
+ args: [{ selector: "bonc-translation-form", imports: [FormControlsComponent, TranslationInputComponent, TranslationTextareaComponent], hostDirectives: [EditableDirective], template: "@if (editable.value) {\n <bonc-form-controls [editable]=\"editable\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n\n @switch (field()) {\n @case (TextEditorField.Input) {\n <bonc-translation-input [text]=\"editable.value\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-input>\n }\n @case (TextEditorField.Textarea) {\n <bonc-translation-textarea [text]=\"editable.value\"\n [minRows]=\"2\"\n [locale]=\"locale()\"\n (startEditing)=\"editable.startEditing()\"\n (changed)=\"editable.updateDirty()\"\n (blurred)=\"editable.updateDirty()\">\n </bonc-translation-textarea>\n }\n }\n </bonc-form-controls>\n} @else {\n <div>Editable value canot be undefined</div>\n}\n", styles: [":host{display:block}\n"] }]
875
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
949
876
 
950
877
  class UnknownFormComponent extends FormBaseComponent {
951
- label = '';
952
- ngOnInit() {
878
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
879
+ constructor() {
880
+ super();
953
881
  this.editable.externalSaveCall.subscribe(() => {
954
882
  this.editable.save();
955
883
  });
956
884
  }
957
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
958
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: UnknownFormComponent, isStandalone: true, selector: "bonc-unknown-form", inputs: { label: "label" }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<label>Unknown form:<b>{{label}}</b></label>\n<pre><ng-content></ng-content></pre>\n", styles: [":host{display:block;color:#000;padding:20px;background-color:#dc143c;margin-bottom:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
885
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
886
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: UnknownFormComponent, isStandalone: true, selector: "bonc-unknown-form", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<label>Unknown form:<b>{{label()}}</b></label>\n<pre><ng-content></ng-content></pre>\n", styles: [":host{display:block;color:#000;padding:20px;background-color:#dc143c;margin-bottom:10px}\n"] });
959
887
  }
960
888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownFormComponent, decorators: [{
961
889
  type: Component,
962
- args: [{ selector: 'bonc-unknown-form', standalone: true, imports: [CommonModule], hostDirectives: [EditableDirective], template: "<label>Unknown form:<b>{{label}}</b></label>\n<pre><ng-content></ng-content></pre>\n", styles: [":host{display:block;color:#000;padding:20px;background-color:#dc143c;margin-bottom:10px}\n"] }]
963
- }], propDecorators: { label: [{
964
- type: Input
965
- }] } });
890
+ args: [{ selector: "bonc-unknown-form", hostDirectives: [EditableDirective], template: "<label>Unknown form:<b>{{label()}}</b></label>\n<pre><ng-content></ng-content></pre>\n", styles: [":host{display:block;color:#000;padding:20px;background-color:#dc143c;margin-bottom:10px}\n"] }]
891
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
966
892
 
967
- const API_BASE_URL = new InjectionToken('ApiBaseUrl');
893
+ /**
894
+ * This is a TypeGen auto-generated file.
895
+ * Any changes made to this file can be lost when this file is regenerated.
896
+ */
968
897
 
969
- class DataService {
970
- http = inject(HttpClient);
971
- baseHref = inject(API_BASE_URL);
972
- getView(viewCode) {
973
- const pageOb = this.getSkeleton(`${this.baseHref}api/views/${viewCode}`);
974
- return pageOb;
975
- }
976
- getPage(pageRoute) {
977
- const pageUrl = `/${pageRoute}`;
978
- const pageOb = this.getSkeleton(`${this.baseHref}api/pages/?url=${encodeURIComponent(pageUrl)}`);
979
- return pageOb;
980
- }
981
- getSettings(ids) {
982
- const pageOb = this.http.get(`${this.baseHref}api/settings`, {
983
- params: { ids: ids }
984
- });
985
- return pageOb;
986
- }
987
- getSkeleton(url) {
988
- const skeletonOb = this.http.get(url);
989
- const routeDataObs = skeletonOb
990
- .pipe(map(x => {
991
- const notEmptyDataRoutes = x.bones
992
- .map(b => ('dataRoute' in b) && typeof (b.dataRoute) === 'string' ? b.dataRoute : '')
993
- .filter(x => x.length > 0);
994
- if (notEmptyDataRoutes.length === 0) {
995
- const emptyData = {};
996
- return { page: of(x), data: of(emptyData) };
997
- }
998
- return {
999
- page: of(x),
1000
- data: combineLatest(notEmptyDataRoutes
1001
- .map(dataRoute => {
1002
- const url = `${this.baseHref}api/Pages/Children/?url=${dataRoute}`;
1003
- return {
1004
- route: dataRoute,
1005
- json: this.http.get(url)
1006
- };
1007
- })
1008
- .reduce((prev, curr) => {
1009
- prev[curr.route] = curr.json;
1010
- return prev;
1011
- }, {}))
1012
- };
1013
- }), map(x => combineLatest(x)), mergeMap(res => merge(res)), map(x => {
1014
- for (const bone of x.page.bones) {
1015
- if (!('dataRoute' in bone) || typeof bone.dataRoute !== 'string' || bone.dataRoute.length === 0)
1016
- continue;
1017
- const data = x.data[bone.dataRoute];
1018
- if (data === undefined || data === null)
1019
- throw new Error(`Data ${bone.dataRoute} have not been preloaded`);
1020
- bone.data = data; // todo: fix
1021
- }
1022
- return x.page;
1023
- }));
1024
- return routeDataObs;
1025
- }
1026
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1027
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService });
1028
- }
1029
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, decorators: [{
1030
- type: Injectable
1031
- }] });
898
+ /**
899
+ * This is a TypeGen auto-generated file.
900
+ * Any changes made to this file can be lost when this file is regenerated.
901
+ */
1032
902
 
1033
- class AdminDataService {
1034
- http = inject(HttpClient);
1035
- baseHref = inject(API_BASE_URL);
1036
- constructor() {
1037
- console.log('baseHref: ' + this.baseHref);
1038
- }
1039
- getSettingGroups() {
1040
- const pageOb = this.http.get(`${this.baseHref}api/admin/settings`);
1041
- return pageOb;
1042
- }
1043
- getPage(url) {
1044
- const pageOb = this.http.get(`${this.baseHref}api/admin/pages/${url}`);
1045
- return pageOb;
1046
- }
1047
- storePage(page) {
1048
- const ob = this.http.post(`${this.baseHref}api/admin/pages`, page);
1049
- return ob;
1050
- }
1051
- updateSettings(settings) {
1052
- const ob = this.http.post(`${this.baseHref}api/admin/settings`, settings);
1053
- return ob;
1054
- }
1055
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1056
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService });
1057
- }
1058
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, decorators: [{
1059
- type: Injectable
1060
- }], ctorParameters: () => [] });
903
+ /**
904
+ * This is a TypeGen auto-generated file.
905
+ * Any changes made to this file can be lost when this file is regenerated.
906
+ */
1061
907
 
1062
- class AdminControlsComponent {
1063
- DeviceType = DeviceType;
1064
- editableGroup;
1065
- deviceControls = false;
1066
- locale = 'en';
1067
- device = this.DeviceType.Desktop;
1068
- changeLocale() {
1069
- this.locale = this.locale === 'en' ? 'ru' : 'en';
1070
- }
1071
- changeDevice() {
1072
- switch (this.device) {
1073
- case DeviceType.Desktop:
1074
- this.device = DeviceType.Tablet;
1075
- return;
1076
- case DeviceType.Tablet:
1077
- this.device = DeviceType.Mobile;
1078
- return;
1079
- case DeviceType.Mobile:
1080
- this.device = DeviceType.Desktop;
1081
- return;
1082
- default:
1083
- this.device = DeviceType.Desktop;
1084
- return;
1085
- }
1086
- }
1087
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1088
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: AdminControlsComponent, isStandalone: true, selector: "bonc-admin-controls", inputs: { editableGroup: "editableGroup", deviceControls: "deviceControls" }, ngImport: i0, template: "<div class=\"page-controls\">\n <a (click)=\"changeLocale()\" class=\"locale\">Locale: {{locale}}</a>\n\n <!-- todo: return this functionality -->\n <!-- <a *ngIf=\"deviceControls\" (click)=\"changeDevice()\" class=\"device\">\n <span *ngIf=\"device === DeviceType.Desktop\">DESKTOP</span>\n <span *ngIf=\"device === DeviceType.Tablet\">TABLET</span>\n <span *ngIf=\"device === DeviceType.Mobile\">MOBILE</span>\n </a> -->\n\n <a *ngIf=\"editableGroup && editableGroup.inEditMode\"\n (click)=\"editableGroup.saveAll()\"\n class=\"save\">Save all</a>\n\n <a *ngIf=\"editableGroup && editableGroup.inEditMode\"\n (click)=\"editableGroup.cancelAll()\"\n class=\"cancel\">Cancel all</a>\n</div>\n", styles: [":host{display:block;position:sticky;top:0;width:100%;z-index:1000}.page-controls{background-color:#111;height:60px;display:flex;align-items:center;justify-content:center}.page-controls a{color:silver;padding:4px;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center}.page-controls a:hover{color:#000;background-color:#cf0}.page-controls a.device{width:80px}.page-controls a+a{margin-left:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1089
- }
1090
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, decorators: [{
1091
- type: Component,
1092
- args: [{ selector: 'bonc-admin-controls', standalone: true, imports: [CommonModule], template: "<div class=\"page-controls\">\n <a (click)=\"changeLocale()\" class=\"locale\">Locale: {{locale}}</a>\n\n <!-- todo: return this functionality -->\n <!-- <a *ngIf=\"deviceControls\" (click)=\"changeDevice()\" class=\"device\">\n <span *ngIf=\"device === DeviceType.Desktop\">DESKTOP</span>\n <span *ngIf=\"device === DeviceType.Tablet\">TABLET</span>\n <span *ngIf=\"device === DeviceType.Mobile\">MOBILE</span>\n </a> -->\n\n <a *ngIf=\"editableGroup && editableGroup.inEditMode\"\n (click)=\"editableGroup.saveAll()\"\n class=\"save\">Save all</a>\n\n <a *ngIf=\"editableGroup && editableGroup.inEditMode\"\n (click)=\"editableGroup.cancelAll()\"\n class=\"cancel\">Cancel all</a>\n</div>\n", styles: [":host{display:block;position:sticky;top:0;width:100%;z-index:1000}.page-controls{background-color:#111;height:60px;display:flex;align-items:center;justify-content:center}.page-controls a{color:silver;padding:4px;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center}.page-controls a:hover{color:#000;background-color:#cf0}.page-controls a.device{width:80px}.page-controls a+a{margin-left:6px}\n"] }]
1093
- }], propDecorators: { editableGroup: [{
1094
- type: Input,
1095
- args: [{ required: true }]
1096
- }], deviceControls: [{
1097
- type: Input
1098
- }] } });
908
+ /**
909
+ * This is a TypeGen auto-generated file.
910
+ * Any changes made to this file can be lost when this file is regenerated.
911
+ */
912
+
913
+ /**
914
+ * This is a TypeGen auto-generated file.
915
+ * Any changes made to this file can be lost when this file is regenerated.
916
+ */
1099
917
 
1100
- const imageMimeTypes = ['image/png', 'image/jpeg'];
1101
- const videoMimeTypes = ['video/mp4'];
1102
- const imageFileTypes = imageMimeTypes.join(',');
1103
- const videoFileTypes = videoMimeTypes.join(',');
918
+ /**
919
+ * This is a TypeGen auto-generated file.
920
+ * Any changes made to this file can be lost when this file is regenerated.
921
+ */
922
+
923
+ /**
924
+ * This is a TypeGen auto-generated file.
925
+ * Any changes made to this file can be lost when this file is regenerated.
926
+ */
927
+
928
+ /**
929
+ * This is a TypeGen auto-generated file.
930
+ * Any changes made to this file can be lost when this file is regenerated.
931
+ */
932
+
933
+ /**
934
+ * This is a TypeGen auto-generated file.
935
+ * Any changes made to this file can be lost when this file is regenerated.
936
+ */
937
+
938
+ /**
939
+ * This is a TypeGen auto-generated file.
940
+ * Any changes made to this file can be lost when this file is regenerated.
941
+ */
942
+
943
+ /**
944
+ * This is a TypeGen auto-generated file.
945
+ * Any changes made to this file can be lost when this file is regenerated.
946
+ */
947
+
948
+ /**
949
+ * This is a TypeGen auto-generated file.
950
+ * Any changes made to this file can be lost when this file is regenerated.
951
+ */
952
+
953
+ /**
954
+ * This is a TypeGen auto-generated file.
955
+ * Any changes made to this file can be lost when this file is regenerated.
956
+ */
957
+ var TextSettingType;
958
+ (function (TextSettingType) {
959
+ TextSettingType[TextSettingType["SingleLine"] = 0] = "SingleLine";
960
+ TextSettingType[TextSettingType["MultiLine"] = 1] = "MultiLine";
961
+ })(TextSettingType || (TextSettingType = {}));
962
+
963
+ /**
964
+ * This is a TypeGen auto-generated file.
965
+ * Any changes made to this file can be lost when this file is regenerated.
966
+ */
967
+
968
+ const imageMimeTypes = ["image/png", "image/jpeg"];
969
+ const videoMimeTypes = ["video/mp4"];
970
+ const imageFileTypes = imageMimeTypes.join(",");
971
+ const videoFileTypes = videoMimeTypes.join(",");
1104
972
  const allMediaFileTypes = `${imageFileTypes},${videoFileTypes}`;
1105
973
  class MediaUploaderComponent {
1106
974
  MediaObjectFit = MediaObjectFit;
1107
- fileInput;
1108
- srcChange = new EventEmitter();
1109
- uploadUrlMap;
1110
- forceRatio;
1111
- progress = 0;
1112
- isUploading = false;
1113
- autoplay = true;
1114
- clipStyle;
1115
- fileTypeMask = allMediaFileTypes;
1116
- _uploadType;
1117
- _src;
1118
- sanitizer = inject(DomSanitizer);
1119
- http = inject(HttpClient);
1120
- cd = inject(ChangeDetectorRef);
1121
- set src(newSrc) {
1122
- if (this._src === newSrc)
1123
- return;
1124
- this._src = newSrc;
1125
- }
1126
- get src() {
1127
- return this._src;
1128
- }
1129
- set uploadType(newUploadType) {
1130
- switch (newUploadType) {
975
+ fileInput = viewChild.required("fileInput");
976
+ srcChange = output();
977
+ uploadUrlMap = input.required(...(ngDevMode ? [{ debugName: "uploadUrlMap" }] : []));
978
+ forceRatio = input(...(ngDevMode ? [undefined, { debugName: "forceRatio" }] : []));
979
+ src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
980
+ uploadType = input(...(ngDevMode ? [undefined, { debugName: "uploadType" }] : []));
981
+ fileTypeMask = computed(() => {
982
+ switch (this.uploadType()) {
1131
983
  case "image":
1132
- this._uploadType = newUploadType;
1133
- this.fileTypeMask = imageFileTypes;
1134
- break;
984
+ return imageFileTypes;
1135
985
  case "video":
1136
- this._uploadType = newUploadType;
1137
- this.fileTypeMask = videoFileTypes;
1138
- break;
1139
- case undefined:
986
+ return videoFileTypes;
1140
987
  default:
1141
- this._uploadType = newUploadType;
1142
- this.fileTypeMask = allMediaFileTypes;
1143
- break;
988
+ return allMediaFileTypes;
1144
989
  }
1145
- }
1146
- get uploadType() {
1147
- return this._uploadType;
1148
- }
990
+ }, ...(ngDevMode ? [{ debugName: "fileTypeMask" }] : []));
991
+ progress = signal(0, ...(ngDevMode ? [{ debugName: "progress" }] : []));
992
+ isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
993
+ clipStyle = signal(undefined, ...(ngDevMode ? [{ debugName: "clipStyle" }] : []));
994
+ sanitizer = inject(DomSanitizer);
995
+ http = inject(HttpClient);
1149
996
  onFileSelect(fileInput) {
1150
- if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1)
997
+ if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1) {
1151
998
  return;
999
+ }
1152
1000
  const file = fileInput.files[0];
1153
1001
  let uploadingMediaType;
1154
- if (this._uploadType !== undefined && this._uploadType !== null) {
1155
- uploadingMediaType = this._uploadType;
1002
+ const uploadType = this.uploadType();
1003
+ if (uploadType !== undefined && uploadType !== null) {
1004
+ uploadingMediaType = uploadType;
1156
1005
  }
1157
1006
  else if (imageMimeTypes.includes(file.type)) {
1158
- uploadingMediaType = 'image';
1007
+ uploadingMediaType = "image";
1159
1008
  }
1160
1009
  else if (videoMimeTypes.includes(file.type)) {
1161
- uploadingMediaType = 'video';
1010
+ uploadingMediaType = "video";
1162
1011
  }
1163
1012
  else {
1164
1013
  console.error(`unknown media type ${file.type} (${file.name})`);
1165
1014
  return;
1166
1015
  }
1167
- const uploadUrl = this.uploadUrlMap.get(uploadingMediaType);
1016
+ const uploadUrl = this.uploadUrlMap().get(uploadingMediaType);
1168
1017
  if (uploadUrl === undefined || uploadUrl === null) {
1169
1018
  console.error(`upload map doesn't have url for type ${uploadingMediaType}`);
1170
1019
  return;
1171
1020
  }
1172
1021
  const formData = new FormData();
1173
- formData.append('file', file);
1174
- formData.append('ratio', `${this.forceRatio ?? 0}`);
1175
- this.progress = 0;
1176
- this.isUploading = true;
1022
+ formData.append("file", file);
1023
+ formData.append("ratio", `${this.forceRatio() ?? 0}`);
1024
+ this.progress.set(0);
1025
+ this.isUploading.set(true);
1177
1026
  this.updateClip();
1178
- const request = new HttpRequest('POST', uploadUrl, formData, {
1179
- reportProgress: true
1027
+ const request = new HttpRequest("POST", uploadUrl, formData, {
1028
+ reportProgress: true,
1180
1029
  });
1181
1030
  this.http
1182
1031
  .request(request)
1183
- .pipe(map(event => this.getEventMessage(event)),
1032
+ .pipe(map((event) => this.getEventMessage(event)),
1184
1033
  // tap(message => this.showProgress(message)),
1185
1034
  last(), // return last (completed) message to caller
1186
- catchError(this.handleError(file))).subscribe(() => { this.isUploading = false; });
1035
+ catchError(this.handleError(file)))
1036
+ .subscribe(() => {
1037
+ this.isUploading.set(false);
1038
+ });
1187
1039
  }
1188
1040
  selectFile(event) {
1189
- // ignore buble click on file picker
1190
- if (event.target === this.fileInput.nativeElement)
1041
+ if (event.target === this.fileInput().nativeElement) {
1191
1042
  return;
1192
- this.fileInput.nativeElement.click();
1043
+ }
1044
+ this.fileInput().nativeElement.click();
1193
1045
  }
1194
1046
  getEventMessage(event) {
1195
1047
  switch (event.type) {
1196
1048
  case HttpEventType.Sent:
1197
1049
  break;
1198
1050
  case HttpEventType.UploadProgress:
1199
- this.progress = event.total === undefined ? 0.5 : event.loaded / event.total;
1051
+ this.progress.set(event.total === undefined ? 0.5 : event.loaded / event.total);
1200
1052
  this.updateClip();
1201
1053
  break;
1202
1054
  case HttpEventType.Response:
1203
1055
  if (event.body === undefined || event.body === null) {
1204
- console.error('media deserialization error. Response body in undefined');
1056
+ console.error("media deserialization error. Response body in undefined");
1205
1057
  }
1206
1058
  else {
1207
1059
  // remove this
@@ -1216,10 +1068,9 @@ class MediaUploaderComponent {
1216
1068
  default:
1217
1069
  break;
1218
1070
  }
1219
- this.cd.detectChanges();
1220
1071
  }
1221
1072
  updateClip() {
1222
- this.clipStyle = this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`);
1073
+ this.clipStyle.set(this.sanitizer.bypassSecurityTrustStyle("inset(0px 100% 0px 0%)"));
1223
1074
  }
1224
1075
  handleError(file) {
1225
1076
  const func = (error, p2) => {
@@ -1231,260 +1082,187 @@ class MediaUploaderComponent {
1231
1082
  return func;
1232
1083
  }
1233
1084
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MediaUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1234
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: MediaUploaderComponent, isStandalone: true, selector: "bonc-media-uploader", inputs: { uploadUrlMap: "uploadUrlMap", forceRatio: "forceRatio", src: "src", uploadType: "uploadType" }, outputs: { srcChange: "srcChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, static: true }], ngImport: i0, template: "<bon-media [src]=\"src\" [objectFit]=\"MediaObjectFit.Cover\"></bon-media>\n\n<div *ngIf=\"src?.sources?.length===0\">Upload media</div>\n\n<div (click)=\"selectFile($event)\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n <span *ngIf=\"isUploading\"\n [style.clip-path]=\"clipStyle\"\n [style.-webkit-clip-path]=\"clipStyle\"\n class=\"loader\"></span>\n\n <span *ngIf=\"isUploading\" class=\"loader-text\">\n <span *ngIf=\"progress < 1\">{{100 * progress | number: '1.0-0'}}%</span>\n <span *ngIf=\"progress >= 1\">converting</span>\n </span>\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: MarcyMediaComponent, selector: "bon-media", inputs: ["src", "objectFit"], outputs: ["isLoaded"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }] });
1085
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: MediaUploaderComponent, isStandalone: true, selector: "bonc-media-uploader", inputs: { uploadUrlMap: { classPropertyName: "uploadUrlMap", publicName: "uploadUrlMap", isSignal: true, isRequired: true, transformFunction: null }, forceRatio: { classPropertyName: "forceRatio", publicName: "forceRatio", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, uploadType: { classPropertyName: "uploadType", publicName: "uploadType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { srcChange: "srcChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<bon-media [src]=\"src()\" [objectFit]=\"MediaObjectFit.Cover\"></bon-media>\n\n@if (src()?.sources?.length===0) {\n <div>Upload media</div>\n}\n\n<div (click)=\"selectFile($event)\" (keydown.enter)=\"selectFile($event)\" role=\"button\" tabindex=\"0\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask()\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n @if (isUploading()) {\n <span [style.clip-path]=\"clipStyle()\"\n [style.-webkit-clip-path]=\"clipStyle()\"\n class=\"loader\"></span>\n <span class=\"loader-text\">\n @if (progress() < 1) {\n <span>{{100 * progress() | number: '1.0-0'}}%</span>\n } @else {\n <span>converting</span>\n }\n </span>\n }\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"], dependencies: [{ kind: "component", type: MarcyMediaComponent, selector: "bon-media", inputs: ["src", "objectFit"], outputs: ["isLoaded"] }, { kind: "pipe", type: DecimalPipe, name: "number" }] });
1235
1086
  }
1236
1087
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MediaUploaderComponent, decorators: [{
1237
1088
  type: Component,
1238
- args: [{ selector: 'bonc-media-uploader', standalone: true, imports: [CommonModule, DecimalPipe, MarcyMediaComponent], template: "<bon-media [src]=\"src\" [objectFit]=\"MediaObjectFit.Cover\"></bon-media>\n\n<div *ngIf=\"src?.sources?.length===0\">Upload media</div>\n\n<div (click)=\"selectFile($event)\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n <span *ngIf=\"isUploading\"\n [style.clip-path]=\"clipStyle\"\n [style.-webkit-clip-path]=\"clipStyle\"\n class=\"loader\"></span>\n\n <span *ngIf=\"isUploading\" class=\"loader-text\">\n <span *ngIf=\"progress < 1\">{{100 * progress | number: '1.0-0'}}%</span>\n <span *ngIf=\"progress >= 1\">converting</span>\n </span>\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"] }]
1239
- }], propDecorators: { fileInput: [{
1240
- type: ViewChild,
1241
- args: ['fileInput', { static: true }]
1242
- }], srcChange: [{
1243
- type: Output
1244
- }], uploadUrlMap: [{
1245
- type: Input,
1246
- args: [{ required: true }]
1247
- }], forceRatio: [{
1248
- type: Input
1249
- }], src: [{
1250
- type: Input
1251
- }], uploadType: [{
1252
- type: Input
1253
- }] } });
1089
+ args: [{ selector: "bonc-media-uploader", imports: [DecimalPipe, MarcyMediaComponent], template: "<bon-media [src]=\"src()\" [objectFit]=\"MediaObjectFit.Cover\"></bon-media>\n\n@if (src()?.sources?.length===0) {\n <div>Upload media</div>\n}\n\n<div (click)=\"selectFile($event)\" (keydown.enter)=\"selectFile($event)\" role=\"button\" tabindex=\"0\" class=\"media-container\">\n <input #fileInput [accept]=\"fileTypeMask()\" (change)=\"onFileSelect(fileInput)\" name=\"media\" type=\"file\" />\n @if (isUploading()) {\n <span [style.clip-path]=\"clipStyle()\"\n [style.-webkit-clip-path]=\"clipStyle()\"\n class=\"loader\"></span>\n <span class=\"loader-text\">\n @if (progress() < 1) {\n <span>{{100 * progress() | number: '1.0-0'}}%</span>\n } @else {\n <span>converting</span>\n }\n </span>\n }\n</div>\n", styles: [":host{display:block;background-color:#000;position:relative}.media-container{display:block;position:absolute;inset:0;cursor:pointer}.media-container:hover:hover:after{display:flex;justify-content:center;align-items:center;content:\"\";position:absolute;color:silver;inset:0;font-weight:700;font-size:50pt;pointer-events:none;background-color:#ccff00b3}.media-container img,.media-container video{display:block;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}input[type=file]{display:none}.loader{position:absolute;inset:0;background-color:#cf0}.loader-text{background-color:#000;position:absolute;top:50%;left:0;right:0;text-align:center;font-size:20pt;margin-top:-10pt;color:#fff}\n"] }]
1090
+ }], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ["fileInput", { isSignal: true }] }], srcChange: [{ type: i0.Output, args: ["srcChange"] }], uploadUrlMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadUrlMap", required: true }] }], forceRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceRatio", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], uploadType: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadType", required: false }] }] } });
1254
1091
 
1255
- class SkeletonEditorAnchorDirective {
1256
- viewContainerRef = inject(ViewContainerRef);
1257
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1258
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: SkeletonEditorAnchorDirective, isStandalone: true, selector: "[boncSkeletonEditorAnchor]", ngImport: i0 });
1259
- }
1260
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, decorators: [{
1261
- type: Directive,
1262
- args: [{
1263
- selector: '[boncSkeletonEditorAnchor]',
1264
- standalone: true
1265
- }]
1266
- }] });
1092
+ const API_BASE_URL = new InjectionToken("ApiBaseUrl");
1267
1093
 
1268
- class BoneEditorBaseComponent {
1269
- editing = new EventEmitter();
1270
- saved = new EventEmitter();
1271
- removed = new EventEmitter();
1272
- noPresets;
1273
- locale;
1274
- device = DeviceType.NotSet;
1275
- _bone;
1276
- _storedData = '';
1277
- _currentPreset;
1278
- presets;
1279
- _isDirty = false;
1280
- _isEditing = false;
1094
+ class AdminDataService {
1095
+ _http = inject(HttpClient);
1096
+ _baseHref = inject(API_BASE_URL);
1281
1097
  constructor() {
1282
- this.presets = this.getPresets();
1283
- if (this.presets === undefined || this.presets === null)
1284
- throw new Error('presets cannot be undefined in ContentPreset constructor');
1285
- if (this.presets.length === 0) {
1286
- this.presets = [
1287
- {
1288
- title: 'default',
1289
- isActive: () => true,
1290
- transformer: bone => bone,
1291
- clean: bone => bone,
1292
- }
1293
- ];
1294
- }
1295
- this.noPresets = this.presets.length === 1;
1098
+ console.log(`baseHref: ${this._baseHref}`);
1296
1099
  }
1297
- get isMobile() {
1298
- return this.device !== undefined && this.device === DeviceType.Mobile;
1299
- }
1300
- get isTablet() {
1301
- return this.device !== undefined && this.device === DeviceType.Tablet;
1302
- }
1303
- get isDesktop() {
1304
- return this.device !== undefined && this.device === DeviceType.Desktop;
1305
- }
1306
- get currentPreset() {
1307
- return this._currentPreset;
1308
- }
1309
- get bone() {
1310
- return this._bone;
1311
- }
1312
- set bone(newData) {
1313
- this._isDirty = false;
1314
- this._storedData = JSON.stringify(newData);
1315
- this._bone = JSON.parse(this._storedData);
1316
- this.updatePresetByData();
1317
- }
1318
- get isDirty() {
1319
- return this._isDirty;
1320
- }
1321
- get isEditing() {
1322
- return this._isEditing;
1323
- }
1324
- resetData() {
1325
- this._isEditing = false;
1326
- this.bone = JSON.parse(this._storedData);
1327
- if (this.onReset !== undefined)
1328
- this.onReset();
1329
- this.editing.emit(false);
1330
- }
1331
- updateDirty() {
1332
- this._isDirty = JSON.stringify(this._bone) !== this._storedData;
1100
+ getSettingGroups() {
1101
+ const pageOb = this._http.get(`${this._baseHref}api/admin/settings`);
1102
+ return pageOb;
1333
1103
  }
1334
- markAsDirty() {
1335
- this._isDirty = true;
1104
+ getPage(url) {
1105
+ const pageOb = this._http.get(`${this._baseHref}api/admin/pages/${url}`);
1106
+ return pageOb;
1336
1107
  }
1337
- remove() {
1338
- this.removed.next();
1108
+ storePage(page) {
1109
+ const ob = this._http.post(`${this._baseHref}api/admin/pages`, page);
1110
+ return ob;
1339
1111
  }
1340
- save() {
1341
- if (!this.isDirty)
1342
- return;
1343
- this._isDirty = false;
1344
- this.finishEditing();
1345
- this._storedData = JSON.stringify(this._bone);
1346
- const clonedData = JSON.parse(this._storedData);
1347
- this.saved.emit(clonedData);
1112
+ updateSettings(settings) {
1113
+ const ob = this._http.post(`${this._baseHref}api/admin/settings`, settings);
1114
+ return ob;
1348
1115
  }
1349
- startEditing() {
1350
- if (this._isEditing) {
1351
- this.updateDirty();
1352
- return;
1353
- }
1354
- this._isEditing = true;
1355
- this.updateDirty();
1356
- this.editing.emit(true);
1116
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1117
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService });
1118
+ }
1119
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, decorators: [{
1120
+ type: Injectable
1121
+ }], ctorParameters: () => [] });
1122
+
1123
+ class DataService {
1124
+ http = inject(HttpClient);
1125
+ baseHref = inject(API_BASE_URL);
1126
+ getView(viewCode) {
1127
+ const pageOb = this.getSkeleton(`${this.baseHref}api/views/${viewCode}`);
1128
+ return pageOb;
1357
1129
  }
1358
- finishEditing() {
1359
- if (this._isDirty)
1360
- throw new Error('Нельзя закрывать редактирование когда есть изменения. Надо сохранить либо зарезетить.');
1361
- if (this.onFinishEditing !== undefined)
1362
- this.onFinishEditing();
1363
- this._isEditing = false;
1130
+ getPage(pageRoute) {
1131
+ const pageUrl = `/${pageRoute}`;
1132
+ const pageOb = this.getSkeleton(`${this.baseHref}api/pages/?url=${encodeURIComponent(pageUrl)}`);
1133
+ return pageOb;
1364
1134
  }
1365
- nextPreset() {
1366
- const newIndex = this._currentPreset === undefined ? 0 : this.presets.indexOf(this._currentPreset) + 1;
1367
- this.applyPresetAtIndex(newIndex);
1135
+ getSettings(ids) {
1136
+ const pageOb = this.http.get(`${this.baseHref}api/settings`, {
1137
+ params: { ids },
1138
+ });
1139
+ return pageOb;
1368
1140
  }
1369
- applyPresetAtIndex = (newIndex) => {
1370
- newIndex = newIndex < 0 ? 0 : newIndex % this.presets.length;
1371
- const currentIndex = this._currentPreset === undefined ? -1 : this.presets.indexOf(this._currentPreset);
1372
- if (currentIndex === newIndex)
1373
- return;
1374
- this._currentPreset = this.presets[newIndex];
1375
- this._currentPreset.transformer(this._bone);
1376
- this.updateDirty();
1377
- };
1378
- updatePresetByData = () => {
1379
- const countOfActive = this.presets.map(p => p.isActive(this._bone)).filter(p => p).length;
1380
- if (countOfActive !== 1)
1381
- throw new Error(`active preset count should be equal 1, but it was: ${countOfActive}. ${this.constructor.name}`);
1382
- for (let i = 0; this.presets.length; i++) {
1383
- const preset = this.presets[i];
1384
- if (preset.isActive(this._bone)) {
1385
- this.applyPresetAtIndex(i);
1386
- break;
1141
+ getSkeleton(url) {
1142
+ const skeletonOb = this.http.get(url);
1143
+ const routeDataObs = skeletonOb.pipe(map((x) => {
1144
+ const notEmptyDataRoutes = x.bones
1145
+ .map((b) => ("dataRoute" in b && typeof b.dataRoute === "string" ? b.dataRoute : ""))
1146
+ .filter((x) => x.length > 0);
1147
+ if (notEmptyDataRoutes.length === 0) {
1148
+ const emptyData = {};
1149
+ return { page: of(x), data: of(emptyData) };
1387
1150
  }
1388
- }
1389
- };
1390
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1391
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: BoneEditorBaseComponent, isStandalone: true, selector: "ng-component", inputs: { locale: "locale", bone: "bone" }, host: { properties: { "class.mobile": "this.isMobile", "class.tablet": "this.isTablet", "class.desktop": "this.isDesktop" } }, ngImport: i0, template: '', isInline: true });
1151
+ return {
1152
+ page: of(x),
1153
+ data: combineLatest(notEmptyDataRoutes
1154
+ .map((dataRoute) => {
1155
+ const url = `${this.baseHref}api/Pages/Children/?url=${dataRoute}`;
1156
+ return {
1157
+ route: dataRoute,
1158
+ json: this.http.get(url),
1159
+ };
1160
+ })
1161
+ .reduce((prev, curr) => {
1162
+ prev[curr.route] = curr.json;
1163
+ return prev;
1164
+ }, {})),
1165
+ };
1166
+ }), map((x) => combineLatest(x)), mergeMap((res) => merge(res)), map((x) => {
1167
+ for (const bone of x.page.bones) {
1168
+ if (!("dataRoute" in bone) ||
1169
+ typeof bone.dataRoute !== "string" ||
1170
+ bone.dataRoute.length === 0) {
1171
+ continue;
1172
+ }
1173
+ const data = x.data[bone.dataRoute];
1174
+ if (data === undefined || data === null) {
1175
+ throw new Error(`Data ${bone.dataRoute} have not been preloaded`);
1176
+ }
1177
+ bone.data = data; // todo: fix
1178
+ }
1179
+ return x.page;
1180
+ }));
1181
+ return routeDataObs;
1182
+ }
1183
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1184
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService });
1392
1185
  }
1393
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, decorators: [{
1394
- type: Component,
1395
- args: [{ template: '' }]
1396
- }], ctorParameters: () => [], propDecorators: { locale: [{
1397
- type: Input,
1398
- args: [{ required: true }]
1399
- }], isMobile: [{
1400
- type: HostBinding,
1401
- args: ['class.mobile']
1402
- }], isTablet: [{
1403
- type: HostBinding,
1404
- args: ['class.tablet']
1405
- }], isDesktop: [{
1406
- type: HostBinding,
1407
- args: ['class.desktop']
1408
- }], bone: [{
1409
- type: Input,
1410
- args: [{ required: true }]
1411
- }] } });
1186
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, decorators: [{
1187
+ type: Injectable
1188
+ }] });
1412
1189
 
1413
- class UnknownBoneEditorComponent extends BoneEditorBaseComponent {
1414
- onFinishEditing() { }
1415
- onReset() { }
1416
- getPresets() {
1417
- return [];
1418
- }
1419
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1420
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: UnknownBoneEditorComponent, isStandalone: true, selector: "bonc-unknown-bone-editor", usesInheritance: true, ngImport: i0, template: "<p>Unknown Bone Editor | bone type: {{bone.type}}</p>\n<pre>{{bone | json}}</pre>\n", styles: [":host{display:block;background-color:#dc143c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2.JsonPipe, name: "json" }] });
1190
+ class SkeletonEditorAnchorDirective {
1191
+ viewContainerRef = inject(ViewContainerRef);
1192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1193
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: SkeletonEditorAnchorDirective, isStandalone: true, selector: "[boncSkeletonEditorAnchor]", ngImport: i0 });
1421
1194
  }
1422
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, decorators: [{
1423
- type: Component,
1424
- args: [{ selector: 'bonc-unknown-bone-editor', standalone: true, imports: [CommonModule, JsonPipe], template: "<p>Unknown Bone Editor | bone type: {{bone.type}}</p>\n<pre>{{bone | json}}</pre>\n", styles: [":host{display:block;background-color:#dc143c}\n"] }]
1195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, decorators: [{
1196
+ type: Directive,
1197
+ args: [{
1198
+ selector: "[boncSkeletonEditorAnchor]",
1199
+ }]
1425
1200
  }] });
1426
1201
 
1427
- // todo: rename class
1428
1202
  class BoneEditorContainerComponent {
1429
- anchor;
1430
- removed = new EventEmitter();
1431
- saved = new EventEmitter();
1432
- editing = new EventEmitter();
1203
+ anchor = viewChild.required(SkeletonEditorAnchorDirective);
1204
+ removed = output();
1205
+ saved = output();
1206
+ editing = output();
1433
1207
  DeviceType = DeviceType;
1434
1208
  editor;
1435
1209
  themePopupIsShown = false;
1436
- _bone;
1210
+ bone = input.required(...(ngDevMode ? [{ debugName: "bone" }] : []));
1211
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
1212
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
1213
+ map = input.required(...(ngDevMode ? [{ debugName: "map" }] : []));
1214
+ boneEditorRef;
1437
1215
  removeSubscription;
1438
1216
  saveSubscription;
1439
1217
  changedSubscription;
1440
- locale;
1441
- device = DeviceType.NotSet;
1442
- componentFactoryResolver = inject(ComponentFactoryResolver);
1443
- ngOnChanges() {
1444
- if (this.editor === undefined || this.editor === null)
1445
- return;
1446
- this.editor.locale = this.locale;
1447
- this.editor.device = this.device;
1448
- }
1449
- map;
1450
- get bone() {
1451
- return this._bone;
1452
- }
1453
- set bone(newBone) {
1454
- this._bone = newBone;
1455
- if (this.removeSubscription) {
1456
- this.removeSubscription.unsubscribe();
1457
- this.removeSubscription = undefined;
1458
- }
1459
- if (this.saveSubscription) {
1460
- this.saveSubscription.unsubscribe();
1461
- this.saveSubscription = undefined;
1462
- }
1463
- if (this.changedSubscription) {
1464
- this.changedSubscription.unsubscribe();
1465
- this.changedSubscription = undefined;
1466
- }
1467
- const viewContainerRef = this.anchor.viewContainerRef;
1468
- viewContainerRef.clear();
1469
- const componentType = this.map.get(newBone.type) ?? UnknownBoneEditorComponent;
1470
- const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);
1471
- const boneEditorRef = viewContainerRef.createComponent(componentFactory);
1472
- this.editor = boneEditorRef.instance;
1473
- this.editor.bone = newBone;
1474
- this.removeSubscription = this.editor.removed.subscribe(() => {
1475
- this.removed.next();
1476
- });
1477
- this.changedSubscription = this.editor.editing.subscribe((isEditing) => {
1478
- this.editing.next(isEditing);
1218
+ viewReady = signal(false, ...(ngDevMode ? [{ debugName: "viewReady" }] : []));
1219
+ constructor() {
1220
+ afterNextRender(() => this.viewReady.set(true));
1221
+ effect(() => {
1222
+ if (!this.viewReady()) {
1223
+ return;
1224
+ }
1225
+ const newBone = this.bone();
1226
+ const editorMap = this.map();
1227
+ if (this.removeSubscription) {
1228
+ this.removeSubscription.unsubscribe();
1229
+ this.removeSubscription = undefined;
1230
+ }
1231
+ if (this.saveSubscription) {
1232
+ this.saveSubscription.unsubscribe();
1233
+ this.saveSubscription = undefined;
1234
+ }
1235
+ if (this.changedSubscription) {
1236
+ this.changedSubscription.unsubscribe();
1237
+ this.changedSubscription = undefined;
1238
+ }
1239
+ const viewContainerRef = this.anchor().viewContainerRef;
1240
+ viewContainerRef.clear();
1241
+ const componentType = editorMap.get(newBone.type) ?? UnknownBoneEditorComponent;
1242
+ this.boneEditorRef = viewContainerRef.createComponent(componentType);
1243
+ this.editor = this.boneEditorRef.instance;
1244
+ this.boneEditorRef.setInput("boneEtalon", newBone);
1245
+ this.boneEditorRef.setInput("locale", this.locale());
1246
+ this.boneEditorRef.setInput("device", this.device());
1247
+ this.removeSubscription = this.editor.removed.subscribe(() => {
1248
+ this.removed.emit();
1249
+ });
1250
+ this.changedSubscription = this.editor.editing.subscribe((isEditing) => this.editing.emit(isEditing));
1251
+ this.saveSubscription = this.editor.saved.subscribe((newBoneValue) => this.saved.emit(newBoneValue));
1479
1252
  });
1480
- this.saveSubscription = this.editor.saved.subscribe((newBoneValue) => {
1481
- this.saved.next(newBoneValue);
1253
+ effect(() => {
1254
+ const locale = this.locale();
1255
+ const device = this.device();
1256
+ if (this.boneEditorRef) {
1257
+ this.boneEditorRef.setInput("locale", locale);
1258
+ this.boneEditorRef.setInput("device", device);
1259
+ }
1482
1260
  });
1483
- this.ngOnChanges();
1484
1261
  }
1485
1262
  nextPreset = () => {
1486
- if (this.editor === undefined || this.editor === null)
1263
+ if (this.editor === undefined || this.editor === null) {
1487
1264
  return;
1265
+ }
1488
1266
  this.editor.nextPreset();
1489
1267
  };
1490
1268
  // todo: add or remove visibility feature
@@ -1500,9 +1278,13 @@ class BoneEditorContainerComponent {
1500
1278
  // this.editor.bone.visibility = setOrRemoveFlag(this.editor.bone.visibility, DeviceVisibility.Mobile, !disabled);
1501
1279
  // this.editor.updateDirty();
1502
1280
  // }
1281
+ // todo: use this feature
1282
+ // its not working now
1503
1283
  get disabled() {
1504
- if (this.editor === undefined || this.editor === null)
1505
- throw new Error('editor should have been set');
1284
+ if (this.editor === undefined || this.editor === null) {
1285
+ console.warn("editor should have been set before disabled is called");
1286
+ return false;
1287
+ }
1506
1288
  return false;
1507
1289
  // todo: add or remove visibility feature
1508
1290
  // const visibility = this.editor.bone.visibility;
@@ -1515,72 +1297,64 @@ class BoneEditorContainerComponent {
1515
1297
  // return true;
1516
1298
  }
1517
1299
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1518
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: BoneEditorContainerComponent, isStandalone: true, selector: "bonc-bone-editor-container", inputs: { locale: "locale", device: "device", map: "map", bone: "bone" }, outputs: { removed: "removed", saved: "saved", editing: "editing" }, viewQueries: [{ propertyName: "anchor", first: true, predicate: SkeletonEditorAnchorDirective, descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"editor\" class=\"left bar\" [class.inactive]=\"editor.noPresets\">\n <!-- <a *ngFor=\"let control of editor.controls\" (click)=\"control.activate()\">{{control.label}}</a> -->\n <!-- <a (click)=\"setDisabled(!disabled)\" class=\"disabled\">block {{disabled ? 'disabled' : 'enabled'}} ({{editor.bone.visibility}})</a> -->\n <a *ngIf=\"editor.currentPreset && !editor.noPresets\" (click)=\"nextPreset()\" class=\"preset\">style:<br />{{editor.currentPreset.title}}</a>\n</div>\n\n<div class=\"editor-container\"\n [class.disabled]=\"disabled\"\n [style.width]=\"\n device === DeviceType.Desktop ? '75vw'\n : device === DeviceType.Tablet ? '55vw'\n : device === DeviceType.Mobile ? '35vw'\n : '75vw' \">\n <ng-template boncSkeletonEditorAnchor>\n </ng-template>\n</div>\n\n<div *ngIf=\"editor\" [class.active]=\"editor.isEditing || editor.isDirty\" class=\"right bar\">\n <!-- <a *ngIf=\"!editor.isEditing\" (click)=\"editor.remove()\" class=\"delete\">!!! remove</a> -->\n <a *ngIf=\"editor.isDirty\" (click)=\"editor.resetData()\" class=\"reset\">reset</a>\n <a *ngIf=\"editor.isEditing && !editor.isDirty\" (click)=\"editor.finishEditing()\" class=\"close\">close</a>\n <a *ngIf=\"editor.isDirty\" (click)=\"editor.save()\" class=\"save\">apply</a>\n</div>\n", styles: [":host{display:flex}.editor-container{background-color:var(--bg-color)}.editor-container.disabled{opacity:.1;pointer-events:none}.editor-container.mobile{width:350px}.preset{white-space:pre-line}.bar{width:120px;display:flex;flex-direction:column;pointer-events:all}.bar.left:hover,.bar.active{background-color:#111}.bar.hidden{opacity:.2}.bar.left{position:relative;border-right:none}.bar.right{border-left:none;text-align:right}.bar.right .delete{display:none}.bar.right .delete:hover{color:#ff355e}.bar.right.active .delete,.bar.right:hover .delete{display:block}.left.bar.inactive a:hover{cursor:initial;color:#444}.popup{position:absolute;top:0;right:-350px;display:flex;flex-direction:column;width:350px;padding:20px;color:#222;background-color:#fff;border:2px solid black;box-sizing:border-box;z-index:3}.popup a{padding:0!important;cursor:pointer!important}.popup a.close{position:absolute;top:10px;right:10px;color:#666}.popup a.close:hover{background-color:#cf0!important}.popup a.reset{align-self:flex-start;margin-top:30px}.popup a.reset:hover{background-color:#cf0!important}.color-group{display:flex}.color-group:first-of-type{margin-top:20px}.color-group label{flex:1}.color-group input{border:1px solid #222;padding:6px;cursor:pointer}.color-group input:hover,.color-group input:focus{outline:none}.color-group+.color-group{margin-top:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: SkeletonEditorAnchorDirective, selector: "[boncSkeletonEditorAnchor]" }] });
1300
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: BoneEditorContainerComponent, isStandalone: true, selector: "bonc-bone-editor-container", inputs: { bone: { classPropertyName: "bone", publicName: "bone", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, device: { classPropertyName: "device", publicName: "device", isSignal: true, isRequired: false, transformFunction: null }, map: { classPropertyName: "map", publicName: "map", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { removed: "removed", saved: "saved", editing: "editing" }, viewQueries: [{ propertyName: "anchor", first: true, predicate: SkeletonEditorAnchorDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@if (editor) {\n <div class=\"left bar\" [class.inactive]=\"editor.noPresets()\">\n <!-- <a *ngFor=\"let control of editor.controls\" (click)=\"control.activate()\">{{control.label}}</a> -->\n <!-- <a (click)=\"setDisabled(!disabled)\" class=\"disabled\">block {{disabled ? 'disabled' : 'enabled'}} ({{editor.bone.visibility}})</a> -->\n @if (editor.currentPreset() && !editor.noPresets()) {\n <a (click)=\"nextPreset()\" class=\"preset\">style:<br />{{editor.currentPreset()?.title}}</a>\n }\n </div>\n}@else {\n EDITOR COMPONENTS IS NOT DEFINED\n}\n\n<div class=\"editor-container\"\n [class.disabled]=\"disabled\"\n [style.width]=\"\n device() === DeviceType.Desktop ? '75vw'\n : device() === DeviceType.Tablet ? '55vw'\n : device() === DeviceType.Mobile ? '35vw'\n : '75vw' \">\n <ng-template boncSkeletonEditorAnchor>\n </ng-template>\n</div>\n\n@if (editor) {\n <div [class.active]=\"editor.isEditing() || editor.isDirty()\" class=\"right bar\">\n <!-- <a *ngIf=\"!editor.isEditing\" (click)=\"editor.remove()\" class=\"delete\">!!! remove</a> -->\n @if (editor.isDirty()) {\n <a (click)=\"editor.resetData()\" class=\"reset\">reset</a>\n <a (click)=\"editor.save()\" class=\"save\">apply</a>\n } @else if (editor.isEditing()) {\n <a (click)=\"editor.finishEditing()\" class=\"close\">close</a>\n }\n </div>\n}\n", styles: [":host{display:flex}.editor-container{background-color:var(--bg-color)}.editor-container.disabled{opacity:.1;pointer-events:none}.editor-container.mobile{width:350px}.preset{white-space:pre-line}.bar{width:120px;display:flex;flex-direction:column;pointer-events:all}.bar.left:hover,.bar.active{background-color:#111}.bar.hidden{opacity:.2}.bar.left{position:relative;border-right:none}.bar.right{border-left:none;text-align:right}.bar.right .delete{display:none}.bar.right .delete:hover{color:#ff355e}.bar.right.active .delete,.bar.right:hover .delete{display:block}.left.bar.inactive a:hover{cursor:initial;color:#444}.popup{position:absolute;top:0;right:-350px;display:flex;flex-direction:column;width:350px;padding:20px;color:#222;background-color:#fff;border:2px solid black;box-sizing:border-box;z-index:3}.popup a{padding:0!important;cursor:pointer!important}.popup a.close{position:absolute;top:10px;right:10px;color:#666}.popup a.close:hover{background-color:#cf0!important}.popup a.reset{align-self:flex-start;margin-top:30px}.popup a.reset:hover{background-color:#cf0!important}.color-group{display:flex}.color-group:first-of-type{margin-top:20px}.color-group label{flex:1}.color-group input{border:1px solid #222;padding:6px;cursor:pointer}.color-group input:hover,.color-group input:focus{outline:none}.color-group+.color-group{margin-top:10px}\n"], dependencies: [{ kind: "directive", type: SkeletonEditorAnchorDirective, selector: "[boncSkeletonEditorAnchor]" }] });
1519
1301
  }
1520
1302
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorContainerComponent, decorators: [{
1521
1303
  type: Component,
1522
- args: [{ selector: 'bonc-bone-editor-container', standalone: true, imports: [CommonModule, SkeletonEditorAnchorDirective], template: "<div *ngIf=\"editor\" class=\"left bar\" [class.inactive]=\"editor.noPresets\">\n <!-- <a *ngFor=\"let control of editor.controls\" (click)=\"control.activate()\">{{control.label}}</a> -->\n <!-- <a (click)=\"setDisabled(!disabled)\" class=\"disabled\">block {{disabled ? 'disabled' : 'enabled'}} ({{editor.bone.visibility}})</a> -->\n <a *ngIf=\"editor.currentPreset && !editor.noPresets\" (click)=\"nextPreset()\" class=\"preset\">style:<br />{{editor.currentPreset.title}}</a>\n</div>\n\n<div class=\"editor-container\"\n [class.disabled]=\"disabled\"\n [style.width]=\"\n device === DeviceType.Desktop ? '75vw'\n : device === DeviceType.Tablet ? '55vw'\n : device === DeviceType.Mobile ? '35vw'\n : '75vw' \">\n <ng-template boncSkeletonEditorAnchor>\n </ng-template>\n</div>\n\n<div *ngIf=\"editor\" [class.active]=\"editor.isEditing || editor.isDirty\" class=\"right bar\">\n <!-- <a *ngIf=\"!editor.isEditing\" (click)=\"editor.remove()\" class=\"delete\">!!! remove</a> -->\n <a *ngIf=\"editor.isDirty\" (click)=\"editor.resetData()\" class=\"reset\">reset</a>\n <a *ngIf=\"editor.isEditing && !editor.isDirty\" (click)=\"editor.finishEditing()\" class=\"close\">close</a>\n <a *ngIf=\"editor.isDirty\" (click)=\"editor.save()\" class=\"save\">apply</a>\n</div>\n", styles: [":host{display:flex}.editor-container{background-color:var(--bg-color)}.editor-container.disabled{opacity:.1;pointer-events:none}.editor-container.mobile{width:350px}.preset{white-space:pre-line}.bar{width:120px;display:flex;flex-direction:column;pointer-events:all}.bar.left:hover,.bar.active{background-color:#111}.bar.hidden{opacity:.2}.bar.left{position:relative;border-right:none}.bar.right{border-left:none;text-align:right}.bar.right .delete{display:none}.bar.right .delete:hover{color:#ff355e}.bar.right.active .delete,.bar.right:hover .delete{display:block}.left.bar.inactive a:hover{cursor:initial;color:#444}.popup{position:absolute;top:0;right:-350px;display:flex;flex-direction:column;width:350px;padding:20px;color:#222;background-color:#fff;border:2px solid black;box-sizing:border-box;z-index:3}.popup a{padding:0!important;cursor:pointer!important}.popup a.close{position:absolute;top:10px;right:10px;color:#666}.popup a.close:hover{background-color:#cf0!important}.popup a.reset{align-self:flex-start;margin-top:30px}.popup a.reset:hover{background-color:#cf0!important}.color-group{display:flex}.color-group:first-of-type{margin-top:20px}.color-group label{flex:1}.color-group input{border:1px solid #222;padding:6px;cursor:pointer}.color-group input:hover,.color-group input:focus{outline:none}.color-group+.color-group{margin-top:10px}\n"] }]
1523
- }], propDecorators: { anchor: [{
1524
- type: ViewChild,
1525
- args: [SkeletonEditorAnchorDirective, { static: true }]
1526
- }], removed: [{
1527
- type: Output
1528
- }], saved: [{
1529
- type: Output
1530
- }], editing: [{
1531
- type: Output
1532
- }], locale: [{
1533
- type: Input,
1534
- args: [{ required: true }]
1535
- }], device: [{
1536
- type: Input
1537
- }], map: [{
1538
- type: Input,
1539
- args: [{ required: true }]
1540
- }], bone: [{
1541
- type: Input,
1542
- args: [{ required: true }]
1543
- }] } });
1304
+ args: [{ selector: "bonc-bone-editor-container", imports: [SkeletonEditorAnchorDirective], template: "@if (editor) {\n <div class=\"left bar\" [class.inactive]=\"editor.noPresets()\">\n <!-- <a *ngFor=\"let control of editor.controls\" (click)=\"control.activate()\">{{control.label}}</a> -->\n <!-- <a (click)=\"setDisabled(!disabled)\" class=\"disabled\">block {{disabled ? 'disabled' : 'enabled'}} ({{editor.bone.visibility}})</a> -->\n @if (editor.currentPreset() && !editor.noPresets()) {\n <a (click)=\"nextPreset()\" class=\"preset\">style:<br />{{editor.currentPreset()?.title}}</a>\n }\n </div>\n}@else {\n EDITOR COMPONENTS IS NOT DEFINED\n}\n\n<div class=\"editor-container\"\n [class.disabled]=\"disabled\"\n [style.width]=\"\n device() === DeviceType.Desktop ? '75vw'\n : device() === DeviceType.Tablet ? '55vw'\n : device() === DeviceType.Mobile ? '35vw'\n : '75vw' \">\n <ng-template boncSkeletonEditorAnchor>\n </ng-template>\n</div>\n\n@if (editor) {\n <div [class.active]=\"editor.isEditing() || editor.isDirty()\" class=\"right bar\">\n <!-- <a *ngIf=\"!editor.isEditing\" (click)=\"editor.remove()\" class=\"delete\">!!! remove</a> -->\n @if (editor.isDirty()) {\n <a (click)=\"editor.resetData()\" class=\"reset\">reset</a>\n <a (click)=\"editor.save()\" class=\"save\">apply</a>\n } @else if (editor.isEditing()) {\n <a (click)=\"editor.finishEditing()\" class=\"close\">close</a>\n }\n </div>\n}\n", styles: [":host{display:flex}.editor-container{background-color:var(--bg-color)}.editor-container.disabled{opacity:.1;pointer-events:none}.editor-container.mobile{width:350px}.preset{white-space:pre-line}.bar{width:120px;display:flex;flex-direction:column;pointer-events:all}.bar.left:hover,.bar.active{background-color:#111}.bar.hidden{opacity:.2}.bar.left{position:relative;border-right:none}.bar.right{border-left:none;text-align:right}.bar.right .delete{display:none}.bar.right .delete:hover{color:#ff355e}.bar.right.active .delete,.bar.right:hover .delete{display:block}.left.bar.inactive a:hover{cursor:initial;color:#444}.popup{position:absolute;top:0;right:-350px;display:flex;flex-direction:column;width:350px;padding:20px;color:#222;background-color:#fff;border:2px solid black;box-sizing:border-box;z-index:3}.popup a{padding:0!important;cursor:pointer!important}.popup a.close{position:absolute;top:10px;right:10px;color:#666}.popup a.close:hover{background-color:#cf0!important}.popup a.reset{align-self:flex-start;margin-top:30px}.popup a.reset:hover{background-color:#cf0!important}.color-group{display:flex}.color-group:first-of-type{margin-top:20px}.color-group label{flex:1}.color-group input{border:1px solid #222;padding:6px;cursor:pointer}.color-group input:hover,.color-group input:focus{outline:none}.color-group+.color-group{margin-top:10px}\n"] }]
1305
+ }], ctorParameters: () => [], propDecorators: { anchor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SkeletonEditorAnchorDirective), { isSignal: true }] }], removed: [{ type: i0.Output, args: ["removed"] }], saved: [{ type: i0.Output, args: ["saved"] }], editing: [{ type: i0.Output, args: ["editing"] }], bone: [{ type: i0.Input, args: [{ isSignal: true, alias: "bone", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], device: [{ type: i0.Input, args: [{ isSignal: true, alias: "device", required: false }] }], map: [{ type: i0.Input, args: [{ isSignal: true, alias: "map", required: true }] }] } });
1306
+
1307
+ function createPreset(params) {
1308
+ return {
1309
+ title: params.title,
1310
+ isActive: (x) => x.style === params.style,
1311
+ transformer: (bone) => (bone.style = params.style),
1312
+ clean: (bone) => (bone.style = params.style),
1313
+ };
1314
+ }
1544
1315
 
1545
- // todo: rename class
1546
1316
  class SkeletonEditorComponent {
1547
- boneEditorContainerList;
1548
- locale;
1549
- device = DeviceType.NotSet;
1317
+ boneEditorContainerList = viewChildren("boneEditorContainer", ...(ngDevMode ? [{ debugName: "boneEditorContainerList" }] : []));
1318
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
1319
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
1320
+ map = input.required(...(ngDevMode ? [{ debugName: "map" }] : []));
1321
+ templates = input.required(...(ngDevMode ? [{ debugName: "templates" }] : []));
1550
1322
  templatesAreShown = [];
1551
1323
  editable = inject((EditableDirective), { host: true });
1552
- ngOnInit() {
1324
+ constructor() {
1325
+ effect(() => {
1326
+ this.map();
1327
+ this.templates();
1328
+ this.templatesAreShown.splice(0, this.templatesAreShown.length);
1329
+ const bones = this.editable.value ?? [];
1330
+ if (bones === undefined) {
1331
+ return;
1332
+ }
1333
+ bones.forEach(() => this.templatesAreShown.push(false));
1334
+ this.templatesAreShown.push(false);
1335
+ });
1553
1336
  this.editable.externalSaveCall.subscribe(() => {
1554
- this.boneEditorContainerList.forEach(editorContainer => editorContainer.editor?.save());
1337
+ this.boneEditorContainerList().forEach((editorContainer) => editorContainer.editor?.save());
1555
1338
  this.editable.save();
1556
1339
  });
1557
1340
  this.editable.canceled.subscribe(() => {
1558
- this.boneEditorContainerList.forEach(editorContainer => editorContainer.editor?.resetData());
1341
+ this.boneEditorContainerList().forEach((editorContainer) => editorContainer.editor?.resetData());
1559
1342
  this.editable.save();
1560
1343
  });
1561
1344
  }
1562
- map;
1563
- ngOnChanges() {
1564
- this.templatesAreShown.splice(0, this.templatesAreShown.length);
1565
- if (this.bones === undefined)
1566
- return;
1567
- this.bones.forEach(() => this.templatesAreShown.push(false));
1568
- this.templatesAreShown.push(false);
1569
- }
1570
- templates;
1571
1345
  get bones() {
1572
1346
  return this.editable?.value ?? [];
1573
1347
  }
1574
1348
  boneEditHandler(isEditing) {
1575
- if (isEditing)
1349
+ if (isEditing) {
1576
1350
  this.editable.startEditing();
1351
+ }
1577
1352
  else {
1578
- const allClosed = this.boneEditorContainerList
1579
- .filter(x => x.editor !== undefined && x.editor.isEditing)
1580
- .length === 0;
1353
+ const allClosed = this.boneEditorContainerList().filter((x) => x.editor?.isEditing).length === 0;
1581
1354
  this.editable.updateDirty();
1582
- if (!this.editable.isDirty && allClosed)
1355
+ if (!this.editable.isDirty() && allClosed) {
1583
1356
  this.editable.close();
1357
+ }
1584
1358
  }
1585
1359
  }
1586
1360
  boneChangeHandler(index, newBoneValue) {
@@ -1590,23 +1364,28 @@ class SkeletonEditorComponent {
1590
1364
  this.editable.save();
1591
1365
  }
1592
1366
  removeBone(boneIndex) {
1593
- if (this.bones === undefined || this.bones === null)
1367
+ if (this.bones === undefined || this.bones === null) {
1594
1368
  return;
1595
- if (!confirm('Вы уверены, что хотите удалить компонент?'))
1369
+ }
1370
+ if (!confirm("Вы уверены, что хотите удалить компонент?")) {
1596
1371
  return;
1372
+ }
1597
1373
  this.bones.splice(boneIndex, 1);
1598
- if (!this.editable.inEditMode)
1374
+ if (!this.editable.inEditMode()) {
1599
1375
  this.editable.startEditing();
1376
+ }
1600
1377
  this.editable.save();
1601
1378
  }
1602
1379
  moveDown(boneIndex) {
1603
- if (boneIndex >= this.bones.length - 1)
1380
+ if (boneIndex >= this.bones.length - 1) {
1604
1381
  return;
1382
+ }
1605
1383
  this.swapBones(boneIndex, boneIndex + 1);
1606
1384
  }
1607
1385
  moveUp(boneIndex) {
1608
- if (boneIndex <= 0)
1386
+ if (boneIndex <= 0) {
1609
1387
  return;
1388
+ }
1610
1389
  this.swapBones(boneIndex - 1, boneIndex);
1611
1390
  }
1612
1391
  createBone(index, selectedTemplate) {
@@ -1614,42 +1393,20 @@ class SkeletonEditorComponent {
1614
1393
  this.bones.splice(index, 0, newBone);
1615
1394
  }
1616
1395
  swapBones(index1, index2) {
1617
- if (index1 < 0 || index2 < 0 || index1 >= this.bones.length || index2 >= this.bones.length)
1396
+ if (index1 < 0 || index2 < 0 || index1 >= this.bones.length || index2 >= this.bones.length) {
1618
1397
  throw new Error(`ошибка swapBones. неправильные индексы ${index1} и ${index2}. bone count: ${this.bones.length}`);
1398
+ }
1619
1399
  const tempBone = this.bones[index1];
1620
1400
  this.bones[index1] = this.bones[index2];
1621
1401
  this.bones[index2] = tempBone;
1622
1402
  }
1623
1403
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1624
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: SkeletonEditorComponent, isStandalone: true, selector: "bonc-skeleton-editor", inputs: { locale: "locale", device: "device", map: "map", templates: "templates" }, viewQueries: [{ propertyName: "boneEditorContainerList", predicate: ["boneEditorContainer"], descendants: true }], usesOnChanges: true, hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<ng-template #controls let-index='index'>\n <div [class.appearable]=\"index > 0 && index < bones.length\" class=\"controls\">\n <div *ngIf=\"index < bones.length\" class=\"up-down\">\n <a *ngIf=\"index > 0\" (click)=\"moveDown(index - 1)\" class=\"down\">move down</a>\n <a *ngIf=\"index > 0\" (click)=\"moveUp(index)\" class=\"up\">move up</a>\n </div>\n <a (click)=\"templatesAreShown[index] = true\" class=\"add\">create</a>\n <a *ngIf=\"index < bones.length\" (click)=\"removeBone(index)\" class=\"remove\">remove</a>\n </div>\n\n <div *ngIf=\"templatesAreShown[index]\" class=\"templates\">\n <a *ngFor=\"let template of templates\" (click)=\"templatesAreShown[index] = false; createBone(index,template);\">{{template.title}}</a>\n <a (click)=\"templatesAreShown[index] = false\">Close</a>\n </div>\n</ng-template>\n\n<!-- todo: add trackby -->\n<ng-container *ngFor=\"let bone of bones; let i = index\">\n <ng-container>\n <ng-container *ngTemplateOutlet=\"controls; context: {index: i}\"></ng-container>\n </ng-container>\n\n <bonc-bone-editor-container #boneEditorContainer\n [map]=\"map\"\n [bone]=\"bone\"\n [locale]=\"locale\"\n [device]=\"device\"\n (removed)=\"removeBone(i)\"\n (editing)=\"boneEditHandler($event)\"\n (saved)=\"boneChangeHandler(i, $event)\">\n </bonc-bone-editor-container>\n\n</ng-container>\n\n<ng-container *ngTemplateOutlet=\"controls; context: {index: bones.length}\"></ng-container>\n", styles: [":host{display:block}.controls{height:62px;display:flex;align-items:center;justify-content:space-between;margin:0 120px}.controls.appearable{background-color:var(--bg-color)}.controls.appearable *{opacity:0}.controls.appearable:hover *{opacity:1}.controls.appearable:hover{background-color:#222}.controls .up-down{width:100px;align-self:stretch;display:flex;flex-direction:column;justify-content:space-between}.controls a{display:flex;align-items:center;justify-content:center;color:#000;background-color:#cf0;-webkit-user-select:none;user-select:none;cursor:pointer}.controls a.add{padding:10px 30px}.controls a.up,.controls a.down,.controls a.remove{padding:4px 8px}.controls a.remove{align-self:flex-end}.controls a:hover{color:#cf0;background-color:#333}.controls a.remove:hover{color:#fff;background-color:#ff355e}.templates{width:220px;height:380px;position:absolute;left:50%;display:flex;flex-direction:column;margin-left:-110px;margin-top:-190px;z-index:10;padding:6px;background-color:#cf0;box-shadow:0 0 78px #000000bf}.templates a{display:flex;flex:1;align-items:center;justify-content:center;background-color:#cf0;text-align:center;cursor:pointer;text-transform:lowercase}.templates a:hover{text-decoration:underline;font-weight:700}\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: "component", type: BoneEditorContainerComponent, selector: "bonc-bone-editor-container", inputs: ["locale", "device", "map", "bone"], outputs: ["removed", "saved", "editing"] }] });
1404
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SkeletonEditorComponent, isStandalone: true, selector: "bonc-skeleton-editor", inputs: { locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: true, transformFunction: null }, device: { classPropertyName: "device", publicName: "device", isSignal: true, isRequired: false, transformFunction: null }, map: { classPropertyName: "map", publicName: "map", isSignal: true, isRequired: true, transformFunction: null }, templates: { classPropertyName: "templates", publicName: "templates", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "boneEditorContainerList", predicate: ["boneEditorContainer"], descendants: true, isSignal: true }], hostDirectives: [{ directive: EditableDirective }], ngImport: i0, template: "<ng-template #controls let-index='index'>\n <div [class.appearable]=\"index > 0 && index < bones.length\" class=\"controls\">\n @if (index < bones.length) {\n <div class=\"up-down\">\n @if (index > 0) {\n <a (click)=\"moveDown(index - 1)\" class=\"down\">move down</a>\n <a (click)=\"moveUp(index)\" class=\"up\">move up</a>\n }\n </div>\n }\n <a (click)=\"templatesAreShown[index] = true\" class=\"add\">create</a>\n @if (index < bones.length) {\n <a (click)=\"removeBone(index)\" class=\"remove\">remove</a>\n }\n </div>\n\n @if (templatesAreShown[index]) {\n <div class=\"templates\">\n @for (template of templates(); track template.title) {\n <a (click)=\"templatesAreShown[index] = false; createBone(index,template);\">{{template.title}}</a>\n }\n <a (click)=\"templatesAreShown[index] = false\">Close</a>\n </div>\n }\n</ng-template>\n\n<!-- todo: add trackby -->\n@for (bone of bones; track $index) {\n <ng-container>\n <ng-container *ngTemplateOutlet=\"controls; context: {index: $index}\"></ng-container>\n </ng-container>\n\n <bonc-bone-editor-container #boneEditorContainer\n [map]=\"map()\"\n [bone]=\"bone\"\n [locale]=\"locale()\"\n [device]=\"device()\"\n (removed)=\"removeBone($index)\"\n (editing)=\"boneEditHandler($event)\"\n (saved)=\"boneChangeHandler($index, $event)\">\n </bonc-bone-editor-container>\n}\n\n<ng-container *ngTemplateOutlet=\"controls; context: {index: bones.length}\"></ng-container>\n", styles: [":host{display:block}.controls{height:62px;display:flex;align-items:center;justify-content:space-between;margin:0 120px}.controls.appearable{background-color:var(--bg-color)}.controls.appearable *{opacity:0}.controls.appearable:hover *{opacity:1}.controls.appearable:hover{background-color:#222}.controls .up-down{width:100px;align-self:stretch;display:flex;flex-direction:column;justify-content:space-between}.controls a{display:flex;align-items:center;justify-content:center;color:#000;background-color:#cf0;-webkit-user-select:none;user-select:none;cursor:pointer}.controls a.add{padding:10px 30px}.controls a.up,.controls a.down,.controls a.remove{padding:4px 8px}.controls a.remove{align-self:flex-end}.controls a:hover{color:#cf0;background-color:#333}.controls a.remove:hover{color:#fff;background-color:#ff355e}.templates{width:220px;height:380px;position:absolute;left:50%;display:flex;flex-direction:column;margin-left:-110px;margin-top:-190px;z-index:10;padding:6px;background-color:#cf0;box-shadow:0 0 78px #000000bf}.templates a{display:flex;flex:1;align-items:center;justify-content:center;background-color:#cf0;text-align:center;cursor:pointer;text-transform:lowercase}.templates a:hover{text-decoration:underline;font-weight:700}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: BoneEditorContainerComponent, selector: "bonc-bone-editor-container", inputs: ["bone", "locale", "device", "map"], outputs: ["removed", "saved", "editing"] }] });
1625
1405
  }
1626
1406
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorComponent, decorators: [{
1627
1407
  type: Component,
1628
- args: [{ selector: 'bonc-skeleton-editor', standalone: true, imports: [CommonModule, BoneEditorContainerComponent], hostDirectives: [EditableDirective], template: "<ng-template #controls let-index='index'>\n <div [class.appearable]=\"index > 0 && index < bones.length\" class=\"controls\">\n <div *ngIf=\"index < bones.length\" class=\"up-down\">\n <a *ngIf=\"index > 0\" (click)=\"moveDown(index - 1)\" class=\"down\">move down</a>\n <a *ngIf=\"index > 0\" (click)=\"moveUp(index)\" class=\"up\">move up</a>\n </div>\n <a (click)=\"templatesAreShown[index] = true\" class=\"add\">create</a>\n <a *ngIf=\"index < bones.length\" (click)=\"removeBone(index)\" class=\"remove\">remove</a>\n </div>\n\n <div *ngIf=\"templatesAreShown[index]\" class=\"templates\">\n <a *ngFor=\"let template of templates\" (click)=\"templatesAreShown[index] = false; createBone(index,template);\">{{template.title}}</a>\n <a (click)=\"templatesAreShown[index] = false\">Close</a>\n </div>\n</ng-template>\n\n<!-- todo: add trackby -->\n<ng-container *ngFor=\"let bone of bones; let i = index\">\n <ng-container>\n <ng-container *ngTemplateOutlet=\"controls; context: {index: i}\"></ng-container>\n </ng-container>\n\n <bonc-bone-editor-container #boneEditorContainer\n [map]=\"map\"\n [bone]=\"bone\"\n [locale]=\"locale\"\n [device]=\"device\"\n (removed)=\"removeBone(i)\"\n (editing)=\"boneEditHandler($event)\"\n (saved)=\"boneChangeHandler(i, $event)\">\n </bonc-bone-editor-container>\n\n</ng-container>\n\n<ng-container *ngTemplateOutlet=\"controls; context: {index: bones.length}\"></ng-container>\n", styles: [":host{display:block}.controls{height:62px;display:flex;align-items:center;justify-content:space-between;margin:0 120px}.controls.appearable{background-color:var(--bg-color)}.controls.appearable *{opacity:0}.controls.appearable:hover *{opacity:1}.controls.appearable:hover{background-color:#222}.controls .up-down{width:100px;align-self:stretch;display:flex;flex-direction:column;justify-content:space-between}.controls a{display:flex;align-items:center;justify-content:center;color:#000;background-color:#cf0;-webkit-user-select:none;user-select:none;cursor:pointer}.controls a.add{padding:10px 30px}.controls a.up,.controls a.down,.controls a.remove{padding:4px 8px}.controls a.remove{align-self:flex-end}.controls a:hover{color:#cf0;background-color:#333}.controls a.remove:hover{color:#fff;background-color:#ff355e}.templates{width:220px;height:380px;position:absolute;left:50%;display:flex;flex-direction:column;margin-left:-110px;margin-top:-190px;z-index:10;padding:6px;background-color:#cf0;box-shadow:0 0 78px #000000bf}.templates a{display:flex;flex:1;align-items:center;justify-content:center;background-color:#cf0;text-align:center;cursor:pointer;text-transform:lowercase}.templates a:hover{text-decoration:underline;font-weight:700}\n"] }]
1629
- }], propDecorators: { boneEditorContainerList: [{
1630
- type: ViewChildren,
1631
- args: ['boneEditorContainer']
1632
- }], locale: [{
1633
- type: Input,
1634
- args: [{ required: true }]
1635
- }], device: [{
1636
- type: Input
1637
- }], map: [{
1638
- type: Input,
1639
- args: [{ required: true }]
1640
- }], templates: [{
1641
- type: Input,
1642
- args: [{ required: true }]
1643
- }] } });
1644
-
1645
- function createPreset(params) {
1646
- return {
1647
- title: params.title,
1648
- isActive: x => x.style === params.style,
1649
- transformer: bone => bone.style = params.style,
1650
- clean: bone => bone.style = params.style,
1651
- };
1652
- }
1408
+ args: [{ selector: "bonc-skeleton-editor", imports: [NgTemplateOutlet, BoneEditorContainerComponent], hostDirectives: [EditableDirective], template: "<ng-template #controls let-index='index'>\n <div [class.appearable]=\"index > 0 && index < bones.length\" class=\"controls\">\n @if (index < bones.length) {\n <div class=\"up-down\">\n @if (index > 0) {\n <a (click)=\"moveDown(index - 1)\" class=\"down\">move down</a>\n <a (click)=\"moveUp(index)\" class=\"up\">move up</a>\n }\n </div>\n }\n <a (click)=\"templatesAreShown[index] = true\" class=\"add\">create</a>\n @if (index < bones.length) {\n <a (click)=\"removeBone(index)\" class=\"remove\">remove</a>\n }\n </div>\n\n @if (templatesAreShown[index]) {\n <div class=\"templates\">\n @for (template of templates(); track template.title) {\n <a (click)=\"templatesAreShown[index] = false; createBone(index,template);\">{{template.title}}</a>\n }\n <a (click)=\"templatesAreShown[index] = false\">Close</a>\n </div>\n }\n</ng-template>\n\n<!-- todo: add trackby -->\n@for (bone of bones; track $index) {\n <ng-container>\n <ng-container *ngTemplateOutlet=\"controls; context: {index: $index}\"></ng-container>\n </ng-container>\n\n <bonc-bone-editor-container #boneEditorContainer\n [map]=\"map()\"\n [bone]=\"bone\"\n [locale]=\"locale()\"\n [device]=\"device()\"\n (removed)=\"removeBone($index)\"\n (editing)=\"boneEditHandler($event)\"\n (saved)=\"boneChangeHandler($index, $event)\">\n </bonc-bone-editor-container>\n}\n\n<ng-container *ngTemplateOutlet=\"controls; context: {index: bones.length}\"></ng-container>\n", styles: [":host{display:block}.controls{height:62px;display:flex;align-items:center;justify-content:space-between;margin:0 120px}.controls.appearable{background-color:var(--bg-color)}.controls.appearable *{opacity:0}.controls.appearable:hover *{opacity:1}.controls.appearable:hover{background-color:#222}.controls .up-down{width:100px;align-self:stretch;display:flex;flex-direction:column;justify-content:space-between}.controls a{display:flex;align-items:center;justify-content:center;color:#000;background-color:#cf0;-webkit-user-select:none;user-select:none;cursor:pointer}.controls a.add{padding:10px 30px}.controls a.up,.controls a.down,.controls a.remove{padding:4px 8px}.controls a.remove{align-self:flex-end}.controls a:hover{color:#cf0;background-color:#333}.controls a.remove:hover{color:#fff;background-color:#ff355e}.templates{width:220px;height:380px;position:absolute;left:50%;display:flex;flex-direction:column;margin-left:-110px;margin-top:-190px;z-index:10;padding:6px;background-color:#cf0;box-shadow:0 0 78px #000000bf}.templates a{display:flex;flex:1;align-items:center;justify-content:center;background-color:#cf0;text-align:center;cursor:pointer;text-transform:lowercase}.templates a:hover{text-decoration:underline;font-weight:700}\n"] }]
1409
+ }], ctorParameters: () => [], propDecorators: { boneEditorContainerList: [{ type: i0.ViewChildren, args: ["boneEditorContainer", { isSignal: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: true }] }], device: [{ type: i0.Input, args: [{ isSignal: true, alias: "device", required: false }] }], map: [{ type: i0.Input, args: [{ isSignal: true, alias: "map", required: true }] }], templates: [{ type: i0.Input, args: [{ isSignal: true, alias: "templates", required: true }] }] } });
1653
1410
 
1654
1411
  /**
1655
1412
  * Generated bundle index. Do not edit.