@candy-kingdom/bonnie-cms 0.26.16 → 0.27.0

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,13 +36,13 @@ 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
41
  if (url === undefined || url === null || url.trim().length === 0)
108
42
  return true;
109
- if (url.startsWith('//'))
43
+ if (url.startsWith("//"))
110
44
  return false;
111
- if (url.startsWith('./') || url.startsWith('/'))
45
+ if (url.startsWith("./") || url.startsWith("/"))
112
46
  return true;
113
47
  return false;
114
48
  }
@@ -119,67 +53,225 @@ function setOrRemoveFlag(value, flag, enabled) {
119
53
  return (enabled ? value | flag : value & ~flag);
120
54
  }
121
55
 
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 = {}));
56
+ class AdminControlsComponent {
57
+ DeviceType = DeviceType;
58
+ editableGroup = input.required(...(ngDevMode ? [{ debugName: "editableGroup" }] : []));
59
+ deviceControls = input(false, ...(ngDevMode ? [{ debugName: "deviceControls" }] : []));
60
+ locale = "en";
61
+ device = this.DeviceType.Desktop;
62
+ changeLocale() {
63
+ this.locale = this.locale === "en" ? "ru" : "en"; // move locales to CMS config
64
+ }
65
+ changeDevice() {
66
+ switch (this.device) {
67
+ case DeviceType.Desktop:
68
+ this.device = DeviceType.Tablet;
69
+ return;
70
+ case DeviceType.Tablet:
71
+ this.device = DeviceType.Mobile;
72
+ return;
73
+ case DeviceType.Mobile:
74
+ this.device = DeviceType.Desktop;
75
+ return;
76
+ default:
77
+ this.device = DeviceType.Desktop;
78
+ return;
79
+ }
80
+ }
81
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
82
+ 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"] });
83
+ }
84
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminControlsComponent, decorators: [{
85
+ type: Component,
86
+ 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"] }]
87
+ }], propDecorators: { editableGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableGroup", required: true }] }], deviceControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "deviceControls", required: false }] }] } });
88
+
89
+ class BoneEditorBaseComponent {
90
+ editing = output();
91
+ saved = output();
92
+ removed = output();
93
+ boneEtalon = model.required(...(ngDevMode ? [{ debugName: "boneEtalon" }] : []));
94
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
95
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
96
+ _presets = signal([], ...(ngDevMode ? [{ debugName: "_presets" }] : []));
97
+ presets = this._presets.asReadonly();
98
+ _currentPreset = signal(undefined, ...(ngDevMode ? [{ debugName: "_currentPreset" }] : []));
99
+ currentPreset = this._currentPreset.asReadonly();
100
+ _isDirty = signal(false, ...(ngDevMode ? [{ debugName: "_isDirty" }] : []));
101
+ isDirty = this._isDirty.asReadonly();
102
+ _isEditing = signal(false, ...(ngDevMode ? [{ debugName: "_isEditing" }] : []));
103
+ isEditing = this._isEditing.asReadonly();
104
+ noPresets = computed(() => this.presets().length === 0, ...(ngDevMode ? [{ debugName: "noPresets" }] : []));
105
+ // effect gonna fix this undefined value
106
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
107
+ bone = signal(undefined, ...(ngDevMode ? [{ debugName: "bone" }] : []));
108
+ _storedBoneJson = "";
109
+ constructor() {
110
+ effect(() => {
111
+ const newData = this.boneEtalon();
112
+ this._isDirty.set(false);
113
+ this._storedBoneJson = JSON.stringify(newData);
114
+ this.bone.set(JSON.parse(this._storedBoneJson));
115
+ this.updatePresetByData();
116
+ });
117
+ const presets = this.getPresets();
118
+ if (presets.length === 0) {
119
+ presets.push({
120
+ title: "default",
121
+ isActive: () => true,
122
+ transformer: (bone) => bone,
123
+ clean: (bone) => bone,
124
+ });
125
+ }
126
+ // todo: remove this, use just regular presets
127
+ this._presets.set(presets);
128
+ }
129
+ get isMobile() {
130
+ return this.device() === DeviceType.Mobile;
131
+ }
132
+ get isTablet() {
133
+ return this.device() === DeviceType.Tablet;
134
+ }
135
+ get isDesktop() {
136
+ return this.device() === DeviceType.Desktop;
137
+ }
138
+ resetData() {
139
+ this._isEditing.set(false);
140
+ this.boneEtalon.set(JSON.parse(this._storedBoneJson));
141
+ this.updatePresetByData();
142
+ if (this.onReset !== undefined)
143
+ this.onReset();
144
+ this.editing.emit(false);
145
+ }
146
+ updateDirty() {
147
+ this._isDirty.set(JSON.stringify(this.bone()) !== this._storedBoneJson);
148
+ }
149
+ markAsDirty() {
150
+ this._isDirty.set(true);
151
+ }
152
+ remove() {
153
+ this.removed.emit();
154
+ }
155
+ save() {
156
+ if (!this.isDirty())
157
+ return;
158
+ this._isDirty.set(false);
159
+ this.finishEditing();
160
+ this._storedBoneJson = JSON.stringify(this.bone());
161
+ const clonedData = JSON.parse(this._storedBoneJson);
162
+ this.saved.emit(clonedData);
163
+ }
164
+ startEditing() {
165
+ if (this.isEditing()) {
166
+ this.updateDirty();
167
+ return;
168
+ }
169
+ this._isEditing.set(true);
170
+ this.updateDirty();
171
+ this.editing.emit(true);
172
+ }
173
+ finishEditing() {
174
+ if (this._isDirty())
175
+ throw new Error("Нельзя закрывать редактирование когда есть изменения. Надо сохранить либо зарезетить.");
176
+ if (this.onFinishEditing !== undefined)
177
+ this.onFinishEditing();
178
+ this._isEditing.set(false);
179
+ }
180
+ nextPreset() {
181
+ const currentPreset = this.currentPreset();
182
+ const newIndex = currentPreset === undefined ? 0 : this.presets().indexOf(currentPreset) + 1;
183
+ this.applyPresetAtIndex(newIndex);
184
+ }
185
+ applyPresetAtIndex = (newIndex) => {
186
+ newIndex = newIndex < 0 ? 0 : newIndex % this.presets().length;
187
+ const currentPreset = this.currentPreset();
188
+ const currentIndex = currentPreset === undefined ? -1 : this.presets().indexOf(currentPreset);
189
+ if (currentIndex === newIndex)
190
+ return;
191
+ const newPreset = this.presets()[newIndex];
192
+ this._currentPreset.set(newPreset);
193
+ newPreset.transformer(this.bone());
194
+ this.updateDirty();
195
+ };
196
+ updatePresetByData = () => {
197
+ const countOfActive = this.presets()
198
+ .map((p) => p.isActive(this.bone()))
199
+ .filter((p) => p).length;
200
+ if (countOfActive !== 1)
201
+ throw new Error(`active preset count should be equal 1, but it was: ${countOfActive}. ${this.constructor.name}`);
202
+ for (let i = 0; i < this.presets().length; i++) {
203
+ const preset = this.presets()[i];
204
+ if (preset.isActive(this.bone())) {
205
+ this.applyPresetAtIndex(i);
206
+ break;
207
+ }
208
+ }
209
+ };
210
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
211
+ 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 });
212
+ }
213
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorBaseComponent, decorators: [{
214
+ type: Component,
215
+ args: [{ template: "" }]
216
+ }], 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: [{
217
+ type: HostBinding,
218
+ args: ["class.mobile"]
219
+ }], isTablet: [{
220
+ type: HostBinding,
221
+ args: ["class.tablet"]
222
+ }], isDesktop: [{
223
+ type: HostBinding,
224
+ args: ["class.desktop"]
225
+ }] } });
226
+
227
+ class UnknownBoneEditorComponent extends BoneEditorBaseComponent {
228
+ onFinishEditing() {
229
+ // no-op for unknown bone type
230
+ }
231
+ onReset() {
232
+ // no-op for unknown bone type
233
+ }
234
+ getPresets() {
235
+ return [];
236
+ }
237
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
238
+ 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" }] });
239
+ }
240
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownBoneEditorComponent, decorators: [{
241
+ type: Component,
242
+ 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"] }]
243
+ }] });
130
244
 
131
245
  class EditableDirective {
132
- saved = new EventEmitter();
133
- editModeChange = new EventEmitter();
134
- externalSaveCall = new EventEmitter();
135
- canceled = new EventEmitter();
136
- valueChange = new EventEmitter();
246
+ saved = output();
247
+ editModeChange = output();
248
+ externalSaveCall = output();
249
+ canceled = output();
250
+ valueChange = output();
251
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
137
252
  propagateChange = () => { };
138
- _inEditMode = false;
139
- _isDirty = false;
253
+ _inEditMode = signal(false, ...(ngDevMode ? [{ debugName: "_inEditMode" }] : []));
254
+ inEditMode = this._inEditMode.asReadonly();
255
+ _isDirty = signal(false, ...(ngDevMode ? [{ debugName: "_isDirty" }] : []));
256
+ isDirty = this._isDirty.asReadonly();
140
257
  _value;
141
258
  _originalValue;
142
259
  _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
260
  requestSave() {
169
261
  this.externalSaveCall.emit();
170
262
  }
171
263
  startEditing = () => {
172
- if (this._inEditMode) {
264
+ if (this.inEditMode()) {
173
265
  this.updateDirty();
174
266
  return;
175
267
  }
176
- this._inEditMode = true;
268
+ this._inEditMode.set(true);
177
269
  this.updateDirty();
178
270
  this.editModeChange.emit(true);
179
271
  };
180
272
  save(newData) {
181
- if (!this._inEditMode) {
182
- console.warn('save before edit mode'); //todo: fix that
273
+ if (!this.inEditMode()) {
274
+ console.warn("save before edit mode"); //todo: fix that
183
275
  }
184
276
  this.finishEditing();
185
277
  const newUnqieNotEmptyData = this.value === newData || newData === undefined
@@ -194,20 +286,19 @@ class EditableDirective {
194
286
  if (this._originalValue === undefined || this._originalValue === null)
195
287
  return;
196
288
  if (typeof this._originalValue !== "object") {
197
- console.warn('patch save called on not object type');
289
+ console.warn("patch save called on not object type");
198
290
  return;
199
291
  }
200
292
  this._originalValue[propName] = newDataParts;
201
293
  this.setOriginal(this._originalValue);
202
294
  this.updateDirty();
203
- if (!this.isDirty)
295
+ if (!this.isDirty())
204
296
  this.close();
205
297
  }
206
298
  // change model without opening or closing the editor
207
299
  silentPatch(dict) {
208
300
  for (const key in dict) {
209
- // eslint-disable-next-line no-prototype-builtins
210
- if (!dict.hasOwnProperty(key))
301
+ if (!Object.hasOwn(dict, key))
211
302
  continue;
212
303
  const propVal = dict[key];
213
304
  if (this._value !== undefined && this._value !== null) {
@@ -220,7 +311,7 @@ class EditableDirective {
220
311
  this._storedData = JSON.stringify(this._originalValue);
221
312
  }
222
313
  cancel() {
223
- if (!this._inEditMode)
314
+ if (!this.inEditMode())
224
315
  return;
225
316
  this.finishEditing();
226
317
  // reset value
@@ -231,15 +322,16 @@ class EditableDirective {
231
322
  this.finishEditing();
232
323
  }
233
324
  finishEditing() {
234
- if (!this._inEditMode)
325
+ if (!this.inEditMode())
235
326
  return;
236
- this._inEditMode = false;
327
+ this._inEditMode.set(false);
237
328
  this.editModeChange.emit(false);
238
329
  }
239
330
  setValue(newValue) {
240
- this._value = (newValue === null || newValue === undefined)
241
- ? undefined
242
- : JSON.parse(JSON.stringify(newValue));
331
+ this._value =
332
+ newValue === null || newValue === undefined
333
+ ? undefined
334
+ : JSON.parse(JSON.stringify(newValue));
243
335
  this.valueChange.emit(this._value);
244
336
  }
245
337
  set value(newValue) {
@@ -249,13 +341,10 @@ class EditableDirective {
249
341
  return this._value;
250
342
  }
251
343
  updateDirty() {
252
- this._isDirty = JSON.stringify(this._value) !== this._storedData;
344
+ this._isDirty.set(JSON.stringify(this._value) !== this._storedData);
253
345
  }
254
346
  markAsDirty() {
255
- this._isDirty = true;
256
- }
257
- get isDirty() {
258
- return this._isDirty;
347
+ this._isDirty.set(true);
259
348
  }
260
349
  setOriginal(newOriginalValue) {
261
350
  this._originalValue = newOriginalValue;
@@ -265,10 +354,11 @@ class EditableDirective {
265
354
  writeValue(newValue) {
266
355
  this.finishEditing();
267
356
  this.setOriginal(newValue);
268
- this._isDirty = false;
357
+ this._isDirty.set(false);
269
358
  this.setValue(newValue);
270
359
  }
271
360
  setDisabledState() {
361
+ // required by ControlValueAccessor
272
362
  }
273
363
  registerOnChange(tellAngularThatSomethingIsChanged) {
274
364
  this.propagateChange = (newValue) => {
@@ -276,139 +366,106 @@ class EditableDirective {
276
366
  };
277
367
  }
278
368
  registerOnTouched() {
369
+ // required by ControlValueAccessor
279
370
  }
280
371
  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 });
372
+ 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: [
373
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true },
374
+ ], ngImport: i0 });
282
375
  }
283
376
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableDirective, decorators: [{
284
377
  type: Directive,
285
378
  args: [{
286
- standalone: true,
287
- selector: '[boncEditable]',
288
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true }]
379
+ selector: "[boncEditable]",
380
+ providers: [
381
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableDirective), multi: true },
382
+ ],
289
383
  }]
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
- }] } });
384
+ }], 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
385
 
302
386
  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
387
+ editModeChange = output();
388
+ saved = output();
389
+ requestEditorClose = output();
390
+ editables = contentChildren(EditableDirective, { ...(ngDevMode ? { debugName: "editables" } : {}), descendants: true });
391
+ _subscriptions = [];
392
+ inEditMode = signal(false, ...(ngDevMode ? [{ debugName: "inEditMode" }] : []));
393
+ _saveSubject = new Subject(); // todo: use signal
394
+ constructor() {
395
+ const saveSubscription = this._saveSubject
312
396
  .asObservable()
313
- .pipe(debounceTime(100))
314
- .subscribe(() => this.saved.emit());
315
- this.updateSubscriptions();
316
- this.editables.changes.subscribe(() => {
317
- this.clearSubscribtions();
397
+ .pipe(debounceTime(100), takeUntilDestroyed())
398
+ .subscribe(() => this.saved.emit(undefined));
399
+ effect(() => {
400
+ this.clearSubscriptions();
318
401
  this.updateSubscriptions();
319
402
  });
320
- }
321
- get inEditMode() {
322
- return this._inEditMode;
403
+ inject(DestroyRef).onDestroy(() => {
404
+ saveSubscription.unsubscribe();
405
+ this.clearSubscriptions();
406
+ });
323
407
  }
324
408
  saveAll() {
325
- this.editables.forEach(x => x.requestSave());
409
+ this.editables().forEach((x) => x.requestSave());
326
410
  }
327
411
  cancelAll() {
328
- this.editables.forEach(x => x.cancel());
412
+ this.editables().forEach((x) => x.cancel());
329
413
  }
330
414
  closeAll() {
331
415
  this.requestEditorClose.emit(true);
332
- this.editables.forEach(x => x.close());
416
+ this.editables().forEach((x) => x.close());
333
417
  }
334
- clearSubscribtions() {
335
- this.subscriptions.forEach(x => x.unsubscribe());
336
- this.subscriptions.splice(0, this.subscriptions.length);
418
+ clearSubscriptions() {
419
+ this._subscriptions.forEach((x) => x.unsubscribe());
420
+ this._subscriptions.splice(0, this._subscriptions.length);
337
421
  }
338
422
  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
- }));
423
+ this.editables().forEach((editable) => {
424
+ this._subscriptions.push(editable.saved.subscribe(this.onSave.bind(this)), editable.editModeChange.subscribe(this.updateEditMode.bind(this)));
344
425
  });
345
426
  }
346
427
  onSave() {
347
- this.saveSubject.next();
428
+ this._saveSubject.next();
348
429
  }
349
430
  updateEditMode() {
350
- const newEditMode = this.editables
351
- .filter(x => x.inEditMode)
352
- .length > 0;
353
- if (newEditMode === this._inEditMode)
431
+ const newEditMode = this.editables().filter((x) => x.inEditMode()).length > 0;
432
+ if (newEditMode === this.inEditMode())
354
433
  return;
355
- this._inEditMode = newEditMode;
356
- this.editModeChange.next(newEditMode);
357
- }
358
- ngOnDestroy() {
359
- this.clearSubscribtions();
434
+ this.inEditMode.set(newEditMode);
435
+ this.editModeChange.emit(newEditMode);
360
436
  }
361
437
  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 });
438
+ 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
439
  }
364
440
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EditableGroupComponent, decorators: [{
365
441
  type: Component,
366
442
  args: [{
367
- selector: 'bonc-editable-group',
368
- standalone: true,
369
- template: '<ng-content></ng-content>'
443
+ selector: "bonc-editable-group",
444
+ template: "<ng-content></ng-content>",
370
445
  }]
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
- }] } });
446
+ }], 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
447
 
382
448
  class FormBaseComponent {
383
- static inputs = ['locale'];
384
- name = '';
385
- _locale = '';
449
+ name = "";
386
450
  editable = inject((EditableDirective), { host: true });
387
- set locale(value) {
388
- this._locale = value;
389
- }
390
- get locale() {
391
- return this._locale;
392
- }
451
+ locale = input("", ...(ngDevMode ? [{ debugName: "locale" }] : []));
393
452
  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 });
453
+ 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
454
  }
396
455
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormBaseComponent, decorators: [{
397
456
  type: Component,
398
457
  args: [{
399
- template: ''
458
+ template: "",
400
459
  }]
401
- }], propDecorators: { locale: [{
402
- type: Input
403
- }] } });
460
+ }], propDecorators: { locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }] } });
404
461
 
405
462
  class TranslationInputComponent {
406
- text;
407
- locale;
408
- device = DeviceType.NotSet;
409
- startEditing = new EventEmitter();
410
- changed = new EventEmitter();
411
- blurred = new EventEmitter();
463
+ text = input.required(...(ngDevMode ? [{ debugName: "text" }] : []));
464
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
465
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
466
+ startEditing = output();
467
+ changed = output();
468
+ blurred = output();
412
469
  onClick() {
413
470
  this.startEditing.emit();
414
471
  }
@@ -419,42 +476,35 @@ class TranslationInputComponent {
419
476
  this.blurred.emit();
420
477
  }
421
478
  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"] }] });
479
+ 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
480
  }
424
481
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationInputComponent, decorators: [{
425
482
  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
- }] } });
483
+ 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"] }]
484
+ }], 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
485
 
443
486
  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);
487
+ autosizeList = viewChildren(CdkTextareaAutosize, ...(ngDevMode ? [{ debugName: "autosizeList" }] : []));
488
+ text = input.required(...(ngDevMode ? [{ debugName: "text" }] : []));
489
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
490
+ minRows = input(...(ngDevMode ? [undefined, { debugName: "minRows" }] : []));
491
+ maxRows = input(...(ngDevMode ? [undefined, { debugName: "maxRows" }] : []));
492
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
493
+ startEditing = output();
494
+ changed = output();
495
+ blurred = output();
496
+ constructor() {
497
+ // todo: check if it still necessary
498
+ effect((onCleanup) => {
499
+ this.text();
500
+ this.locale();
501
+ this.minRows();
502
+ this.maxRows();
503
+ const timer = setTimeout(() => this.triggerResize(), 500);
504
+ onCleanup(() => {
505
+ clearTimeout(timer);
506
+ });
507
+ });
458
508
  }
459
509
  onClick() {
460
510
  this.startEditing.emit();
@@ -466,63 +516,36 @@ class TranslationTextareaComponent {
466
516
  this.blurred.emit();
467
517
  }
468
518
  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
- // });
519
+ this.autosizeList().forEach((x) => x.resizeToFitContent(true));
475
520
  }
476
521
  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"] }] });
522
+ 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
523
  }
479
524
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationTextareaComponent, decorators: [{
480
525
  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
- }] } });
526
+ 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"] }]
527
+ }], 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
528
 
505
529
  class LinkPopupComponent {
506
530
  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
531
  LinkPopupField = TextEditorField;
515
- maxRows;
516
- minRows;
517
- linkTitle; // todo: allready in weblink
518
- locale;
519
- link;
532
+ linkChange = output();
533
+ startEditing = output();
534
+ changed = output();
535
+ blurred = output();
536
+ open = output();
537
+ closed = output();
538
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
539
+ maxRows = input(...(ngDevMode ? [undefined, { debugName: "maxRows" }] : []));
540
+ minRows = input(...(ngDevMode ? [undefined, { debugName: "minRows" }] : []));
541
+ linkTitle = input.required(...(ngDevMode ? [{ debugName: "linkTitle" }] : []));
542
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
543
+ link = input.required(...(ngDevMode ? [{ debugName: "link" }] : []));
520
544
  popupIsShown = false;
521
545
  onClick() {
522
546
  this.startEditing.emit();
523
547
  }
524
548
  onChange() {
525
- console.log('change link pop up');
526
549
  this.changed.emit();
527
550
  }
528
551
  onBlur() {
@@ -537,121 +560,66 @@ class LinkPopupComponent {
537
560
  this.closed.emit();
538
561
  }
539
562
  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"] }] });
563
+ 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
564
  }
542
565
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LinkPopupComponent, decorators: [{
543
566
  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
- }] } });
567
+ 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"] }]
568
+ }], 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
569
 
588
570
  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;
571
+ fileInput = viewChild.required("fileInput");
572
+ srcChange = output();
573
+ uploadUrlMap = input.required(...(ngDevMode ? [{ debugName: "uploadUrlMap" }] : []));
574
+ src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
575
+ uploadTypes = input([], ...(ngDevMode ? [{ debugName: "uploadTypes" }] : []));
576
+ fileTypeMask = computed(() => {
577
+ const types = this.uploadTypes();
578
+ return types.length === 0 ? undefined : types.join(",");
579
+ }, ...(ngDevMode ? [{ debugName: "fileTypeMask" }] : []));
580
+ progress = signal(0, ...(ngDevMode ? [{ debugName: "progress" }] : []));
581
+ isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
582
+ clipStyle = signal(undefined, ...(ngDevMode ? [{ debugName: "clipStyle" }] : []));
599
583
  sanitizer = inject(DomSanitizer);
600
584
  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
585
  onFileSelect(fileInput) {
620
586
  if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1)
621
587
  return;
622
588
  const file = fileInput.files[0];
623
- const uploadUrl = this.uploadUrlMap.get(file.type) ?? this.uploadUrlMap.get("");
589
+ const uploadUrl = this.uploadUrlMap().get(file.type) ?? this.uploadUrlMap().get("");
624
590
  if (uploadUrl === undefined || uploadUrl === null) {
625
591
  console.error(`upload map doesn't have url for type '${file.type}'`);
626
592
  return;
627
593
  }
628
594
  const formData = new FormData();
629
- formData.append('file', file);
630
- this.progress = 0;
631
- this.isUploading = true;
595
+ formData.append("file", file);
596
+ this.progress.set(0);
597
+ this.isUploading.set(true);
632
598
  this.updateClip();
633
- const request = new HttpRequest('POST', uploadUrl, formData, {
634
- reportProgress: true
599
+ const request = new HttpRequest("POST", uploadUrl, formData, {
600
+ reportProgress: true,
635
601
  });
636
602
  this.http
637
603
  .request(request)
638
- .pipe(map(event => this.getEventMessage(event)),
604
+ .pipe(map((event) => this.getEventMessage(event)),
639
605
  // tap(message => this.showProgress(message)),
640
606
  last(), // return last (completed) message to caller
641
- catchError(this.handleError(file))).subscribe(() => { this.isUploading = false; });
607
+ catchError(this.handleError(file)))
608
+ .subscribe(() => {
609
+ this.isUploading.set(false);
610
+ });
642
611
  }
643
612
  selectFile(event) {
644
- // ignore buble click on file picker
645
- if (event.target === this.fileInput.nativeElement)
613
+ if (event.target === this.fileInput().nativeElement)
646
614
  return;
647
- this.fileInput.nativeElement.click();
615
+ this.fileInput().nativeElement.click();
648
616
  }
649
617
  getEventMessage(event) {
650
618
  switch (event.type) {
651
619
  case HttpEventType.Sent:
652
620
  break;
653
621
  case HttpEventType.UploadProgress:
654
- this.progress = event.total === undefined ? 0.5 : event.loaded / event.total;
622
+ this.progress.set(event.total === undefined ? 0.5 : event.loaded / event.total);
655
623
  this.updateClip();
656
624
  break;
657
625
  case HttpEventType.Response:
@@ -664,10 +632,9 @@ class FileUploaderComponent {
664
632
  default:
665
633
  break;
666
634
  }
667
- this.cd.detectChanges();
668
635
  }
669
636
  updateClip() {
670
- this.clipStyle = this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`);
637
+ this.clipStyle.set(this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`));
671
638
  }
672
639
  handleError(file) {
673
640
  const func = (error, p2) => {
@@ -679,85 +646,62 @@ class FileUploaderComponent {
679
646
  return func;
680
647
  }
681
648
  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" }] });
649
+ 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
650
  }
684
651
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileUploaderComponent, decorators: [{
685
652
  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
- }] } });
653
+ 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"] }]
654
+ }], 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
655
 
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() {
656
+ class FormControlsComponent {
657
+ editable = input.required(...(ngDevMode ? [{ debugName: "editable" }] : []));
658
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
659
+ 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"] });
660
+ }
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormControlsComponent, decorators: [{
662
+ type: Component,
663
+ 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"] }]
664
+ }], propDecorators: { editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: true }] }] } });
665
+
666
+ class FileFormComponent extends FormBaseComponent {
667
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
668
+ uploadTypes = input.required(...(ngDevMode ? [{ debugName: "uploadTypes" }] : []));
669
+ uploadMap = input.required(...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
670
+ constructor() {
671
+ super();
709
672
  this.editable.externalSaveCall.subscribe(() => {
710
673
  this.editable.save();
711
674
  });
712
675
  }
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) {
676
+ onFileUploaded(fileSrc) {
724
677
  this.editable.startEditing();
725
- if (this.editable.value !== undefined) {
726
- this.editable.value.image[this.locale] = $event;
727
- }
678
+ const svgSrc = fileSrc;
679
+ this.editable.value = svgSrc;
728
680
  this.editable.updateDirty();
729
681
  }
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"] }] });
682
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
683
+ 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
684
  }
733
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, decorators: [{
685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, decorators: [{
734
686
  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
- }] } });
687
+ 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"] }]
688
+ }], 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
689
 
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';
690
+ class LottieFormComponent extends FormBaseComponent {
691
+ LottieMimeType = "application/json";
692
+ // todo: convert to signal
693
+ animOptions;
694
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
695
+ uploadMap = input.required(...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
748
696
  constructor() {
749
697
  super();
750
- this.uploadMap.set(this.SvgMime, this._uploadUrl);
751
- }
752
- ngOnInit() {
753
698
  this.editable.externalSaveCall.subscribe(() => {
754
699
  this.editable.save();
755
700
  });
756
- }
757
- set uploadUrl(newValue) {
758
- this._uploadUrl = newValue;
759
- this.uploadMap.set(this.SvgMime, this._uploadUrl);
760
- this.cd.detectChanges();
701
+ this.editable.valueChange.subscribe((x) => {
702
+ const url = x?.url ?? "";
703
+ this.animOptions = url.length === 0 ? undefined : { path: url };
704
+ });
761
705
  }
762
706
  onFileUploaded(fileSrc) {
763
707
  this.editable.startEditing();
@@ -765,51 +709,31 @@ class SvgFormComponent extends FormBaseComponent {
765
709
  this.editable.value = svgSrc;
766
710
  this.editable.updateDirty();
767
711
  }
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"] }] });
712
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
713
+ 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
714
  }
771
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, decorators: [{
715
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LottieFormComponent, decorators: [{
772
716
  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
- }] } });
717
+ 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"] }]
718
+ }], 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
719
 
780
- const DefaultImageMimeTypes = ['image/png', 'image/jpeg'];
720
+ const DefaultImageMimeTypes = ["image/png", "image/jpeg"];
781
721
  class OneImageFormComponent extends FormBaseComponent {
782
- cd = inject(ChangeDetectorRef);
783
- uploadMap = new Map();
784
- _mimeTypes = DefaultImageMimeTypes;
785
- _uploadUrl = '/api/admin/upload/image';
786
- _label = '';
722
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
723
+ uploadUrl = input("/api/admin/upload/image", ...(ngDevMode ? [{ debugName: "uploadUrl" }] : []));
724
+ mimeTypes = input(DefaultImageMimeTypes, ...(ngDevMode ? [{ debugName: "mimeTypes" }] : []));
725
+ uploadMap = computed(() => {
726
+ const m = new Map();
727
+ m.set("", this.uploadUrl());
728
+ return m;
729
+ }, ...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
730
+ destroyRef = inject(DestroyRef);
787
731
  constructor() {
788
732
  super();
789
- this.uploadMap.set('', this._uploadUrl);
790
- }
791
- ngOnInit() {
792
- this.editable.externalSaveCall.subscribe(() => {
733
+ const sub = this.editable.externalSaveCall.subscribe(() => {
793
734
  this.editable.save();
794
735
  });
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;
736
+ this.destroyRef.onDestroy(() => sub.unsubscribe());
813
737
  }
814
738
  onFileUploaded(fileSrc) {
815
739
  this.editable.startEditing();
@@ -818,50 +742,77 @@ class OneImageFormComponent extends FormBaseComponent {
818
742
  this.editable.updateDirty();
819
743
  }
820
744
  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"] }] });
745
+ 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
746
  }
823
747
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OneImageFormComponent, decorators: [{
824
748
  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
- }] } });
749
+ 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"] }]
750
+ }], 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
751
 
834
- class FileFormComponent extends FormBaseComponent {
835
- label = '';
836
- ngOnInit() {
752
+ const defaultUploadMap = new Map();
753
+ defaultUploadMap.set("", `/api/admin/upload/image?width=${1200}&height=${630}&format=image/jpeg`);
754
+ class SeoFormComponent extends FormBaseComponent {
755
+ uploadMap = defaultUploadMap;
756
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
757
+ pageId = input("", ...(ngDevMode ? [{ debugName: "pageId" }] : []));
758
+ ogImageUploadUrl = computed(() => `/api/admin/page/Og-Image?pageId=${this.pageId()}`, ...(ngDevMode ? [{ debugName: "ogImageUploadUrl" }] : []));
759
+ constructor() {
760
+ super();
761
+ this.editable.externalSaveCall.subscribe(() => {
762
+ this.editable.save();
763
+ });
764
+ }
765
+ ResToSrc(res) {
766
+ return res.url;
767
+ }
768
+ replaceImage($event) {
769
+ this.editable.startEditing();
770
+ if (this.editable.value !== undefined) {
771
+ this.editable.value.image[this.locale()] = $event;
772
+ }
773
+ this.editable.updateDirty();
774
+ }
775
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
776
+ 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"] }] });
777
+ }
778
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SeoFormComponent, decorators: [{
779
+ type: Component,
780
+ args: [{ selector: "bonc-seo-form", imports: [
781
+ FormControlsComponent,
782
+ TranslationInputComponent,
783
+ TranslationTextareaComponent,
784
+ FileUploaderComponent,
785
+ ], 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"] }]
786
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], pageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageId", required: false }] }] } });
787
+
788
+ class SvgFormComponent extends FormBaseComponent {
789
+ SvgMime = "image/svg+xml";
790
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
791
+ uploadUrl = input("/api/admin/upload/image/svg", ...(ngDevMode ? [{ debugName: "uploadUrl" }] : []));
792
+ uploadMap = computed(() => {
793
+ const m = new Map();
794
+ m.set(this.SvgMime, this.uploadUrl());
795
+ return m;
796
+ }, ...(ngDevMode ? [{ debugName: "uploadMap" }] : []));
797
+ constructor() {
798
+ super();
837
799
  this.editable.externalSaveCall.subscribe(() => {
838
800
  this.editable.save();
839
801
  });
840
802
  }
841
- uploadTypes = [];
842
- uploadMap = new Map();
843
- ;
844
803
  onFileUploaded(fileSrc) {
845
804
  this.editable.startEditing();
846
805
  const svgSrc = fileSrc;
847
806
  this.editable.value = svgSrc;
848
807
  this.editable.updateDirty();
849
808
  }
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"] }] });
809
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
810
+ 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
811
  }
853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FileFormComponent, decorators: [{
812
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SvgFormComponent, decorators: [{
854
813
  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
- }] } });
814
+ 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"] }]
815
+ }], 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
816
 
866
817
  var TextInputStyle;
867
818
  (function (TextInputStyle) {
@@ -872,336 +823,218 @@ var TextInputStyle;
872
823
 
873
824
  class TextFormComponent extends FormBaseComponent {
874
825
  TextInputStyle = TextInputStyle;
875
- label = '';
876
- type = TextInputStyle.SingleLine;
877
- ngOnInit() {
826
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
827
+ type = input(TextInputStyle.SingleLine, ...(ngDevMode ? [{ debugName: "type" }] : []));
828
+ constructor() {
829
+ super();
878
830
  this.editable.externalSaveCall.subscribe(() => {
879
831
  this.editable.save();
880
832
  });
881
833
  }
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"] }] });
834
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TextFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
835
+ 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
836
  }
885
837
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TextFormComponent, decorators: [{
886
838
  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
- }] } });
839
+ 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"] }]
840
+ }], 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
841
 
928
842
  class TranslationFormComponent extends FormBaseComponent {
929
843
  TextEditorField = TextEditorField;
930
- field;
931
- label;
932
- ngOnInit() {
844
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
845
+ label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
846
+ constructor() {
847
+ super();
933
848
  this.editable.externalSaveCall.subscribe(() => {
934
849
  this.editable.save();
935
850
  });
936
851
  }
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"] }] });
852
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
853
+ 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
854
  }
940
855
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslationFormComponent, decorators: [{
941
856
  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
- }] } });
857
+ 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"] }]
858
+ }], 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
859
 
950
860
  class UnknownFormComponent extends FormBaseComponent {
951
- label = '';
952
- ngOnInit() {
861
+ label = input("", ...(ngDevMode ? [{ debugName: "label" }] : []));
862
+ constructor() {
863
+ super();
953
864
  this.editable.externalSaveCall.subscribe(() => {
954
865
  this.editable.save();
955
866
  });
956
867
  }
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 }] });
868
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
869
+ 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
870
  }
960
871
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UnknownFormComponent, decorators: [{
961
872
  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
- }] } });
873
+ 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"] }]
874
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
966
875
 
967
- const API_BASE_URL = new InjectionToken('ApiBaseUrl');
876
+ /**
877
+ * This is a TypeGen auto-generated file.
878
+ * Any changes made to this file can be lost when this file is regenerated.
879
+ */
968
880
 
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
- }] });
881
+ /**
882
+ * This is a TypeGen auto-generated file.
883
+ * Any changes made to this file can be lost when this file is regenerated.
884
+ */
1032
885
 
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: () => [] });
886
+ /**
887
+ * This is a TypeGen auto-generated file.
888
+ * Any changes made to this file can be lost when this file is regenerated.
889
+ */
1061
890
 
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
- }] } });
891
+ /**
892
+ * This is a TypeGen auto-generated file.
893
+ * Any changes made to this file can be lost when this file is regenerated.
894
+ */
895
+
896
+ /**
897
+ * This is a TypeGen auto-generated file.
898
+ * Any changes made to this file can be lost when this file is regenerated.
899
+ */
900
+
901
+ /**
902
+ * This is a TypeGen auto-generated file.
903
+ * Any changes made to this file can be lost when this file is regenerated.
904
+ */
905
+
906
+ /**
907
+ * This is a TypeGen auto-generated file.
908
+ * Any changes made to this file can be lost when this file is regenerated.
909
+ */
910
+
911
+ /**
912
+ * This is a TypeGen auto-generated file.
913
+ * Any changes made to this file can be lost when this file is regenerated.
914
+ */
915
+
916
+ /**
917
+ * This is a TypeGen auto-generated file.
918
+ * Any changes made to this file can be lost when this file is regenerated.
919
+ */
920
+
921
+ /**
922
+ * This is a TypeGen auto-generated file.
923
+ * Any changes made to this file can be lost when this file is regenerated.
924
+ */
1099
925
 
1100
- const imageMimeTypes = ['image/png', 'image/jpeg'];
1101
- const videoMimeTypes = ['video/mp4'];
1102
- const imageFileTypes = imageMimeTypes.join(',');
1103
- const videoFileTypes = videoMimeTypes.join(',');
926
+ /**
927
+ * This is a TypeGen auto-generated file.
928
+ * Any changes made to this file can be lost when this file is regenerated.
929
+ */
930
+
931
+ /**
932
+ * This is a TypeGen auto-generated file.
933
+ * Any changes made to this file can be lost when this file is regenerated.
934
+ */
935
+
936
+ /**
937
+ * This is a TypeGen auto-generated file.
938
+ * Any changes made to this file can be lost when this file is regenerated.
939
+ */
940
+ var TextSettingType;
941
+ (function (TextSettingType) {
942
+ TextSettingType[TextSettingType["SingleLine"] = 0] = "SingleLine";
943
+ TextSettingType[TextSettingType["MultiLine"] = 1] = "MultiLine";
944
+ })(TextSettingType || (TextSettingType = {}));
945
+
946
+ /**
947
+ * This is a TypeGen auto-generated file.
948
+ * Any changes made to this file can be lost when this file is regenerated.
949
+ */
950
+
951
+ const imageMimeTypes = ["image/png", "image/jpeg"];
952
+ const videoMimeTypes = ["video/mp4"];
953
+ const imageFileTypes = imageMimeTypes.join(",");
954
+ const videoFileTypes = videoMimeTypes.join(",");
1104
955
  const allMediaFileTypes = `${imageFileTypes},${videoFileTypes}`;
1105
956
  class MediaUploaderComponent {
1106
957
  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) {
958
+ fileInput = viewChild.required("fileInput");
959
+ srcChange = output();
960
+ uploadUrlMap = input.required(...(ngDevMode ? [{ debugName: "uploadUrlMap" }] : []));
961
+ forceRatio = input(...(ngDevMode ? [undefined, { debugName: "forceRatio" }] : []));
962
+ src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
963
+ uploadType = input(...(ngDevMode ? [undefined, { debugName: "uploadType" }] : []));
964
+ fileTypeMask = computed(() => {
965
+ switch (this.uploadType()) {
1131
966
  case "image":
1132
- this._uploadType = newUploadType;
1133
- this.fileTypeMask = imageFileTypes;
1134
- break;
967
+ return imageFileTypes;
1135
968
  case "video":
1136
- this._uploadType = newUploadType;
1137
- this.fileTypeMask = videoFileTypes;
1138
- break;
1139
- case undefined:
969
+ return videoFileTypes;
1140
970
  default:
1141
- this._uploadType = newUploadType;
1142
- this.fileTypeMask = allMediaFileTypes;
1143
- break;
971
+ return allMediaFileTypes;
1144
972
  }
1145
- }
1146
- get uploadType() {
1147
- return this._uploadType;
1148
- }
973
+ }, ...(ngDevMode ? [{ debugName: "fileTypeMask" }] : []));
974
+ progress = signal(0, ...(ngDevMode ? [{ debugName: "progress" }] : []));
975
+ isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
976
+ clipStyle = signal(undefined, ...(ngDevMode ? [{ debugName: "clipStyle" }] : []));
977
+ sanitizer = inject(DomSanitizer);
978
+ http = inject(HttpClient);
1149
979
  onFileSelect(fileInput) {
1150
980
  if (fileInput.files === undefined || fileInput.files === null || fileInput.files.length !== 1)
1151
981
  return;
1152
982
  const file = fileInput.files[0];
1153
983
  let uploadingMediaType;
1154
- if (this._uploadType !== undefined && this._uploadType !== null) {
1155
- uploadingMediaType = this._uploadType;
984
+ const uploadType = this.uploadType();
985
+ if (uploadType !== undefined && uploadType !== null) {
986
+ uploadingMediaType = uploadType;
1156
987
  }
1157
988
  else if (imageMimeTypes.includes(file.type)) {
1158
- uploadingMediaType = 'image';
989
+ uploadingMediaType = "image";
1159
990
  }
1160
991
  else if (videoMimeTypes.includes(file.type)) {
1161
- uploadingMediaType = 'video';
992
+ uploadingMediaType = "video";
1162
993
  }
1163
994
  else {
1164
995
  console.error(`unknown media type ${file.type} (${file.name})`);
1165
996
  return;
1166
997
  }
1167
- const uploadUrl = this.uploadUrlMap.get(uploadingMediaType);
998
+ const uploadUrl = this.uploadUrlMap().get(uploadingMediaType);
1168
999
  if (uploadUrl === undefined || uploadUrl === null) {
1169
1000
  console.error(`upload map doesn't have url for type ${uploadingMediaType}`);
1170
1001
  return;
1171
1002
  }
1172
1003
  const formData = new FormData();
1173
- formData.append('file', file);
1174
- formData.append('ratio', `${this.forceRatio ?? 0}`);
1175
- this.progress = 0;
1176
- this.isUploading = true;
1004
+ formData.append("file", file);
1005
+ formData.append("ratio", `${this.forceRatio() ?? 0}`);
1006
+ this.progress.set(0);
1007
+ this.isUploading.set(true);
1177
1008
  this.updateClip();
1178
- const request = new HttpRequest('POST', uploadUrl, formData, {
1179
- reportProgress: true
1009
+ const request = new HttpRequest("POST", uploadUrl, formData, {
1010
+ reportProgress: true,
1180
1011
  });
1181
1012
  this.http
1182
1013
  .request(request)
1183
- .pipe(map(event => this.getEventMessage(event)),
1014
+ .pipe(map((event) => this.getEventMessage(event)),
1184
1015
  // tap(message => this.showProgress(message)),
1185
1016
  last(), // return last (completed) message to caller
1186
- catchError(this.handleError(file))).subscribe(() => { this.isUploading = false; });
1017
+ catchError(this.handleError(file)))
1018
+ .subscribe(() => {
1019
+ this.isUploading.set(false);
1020
+ });
1187
1021
  }
1188
1022
  selectFile(event) {
1189
- // ignore buble click on file picker
1190
- if (event.target === this.fileInput.nativeElement)
1023
+ if (event.target === this.fileInput().nativeElement)
1191
1024
  return;
1192
- this.fileInput.nativeElement.click();
1025
+ this.fileInput().nativeElement.click();
1193
1026
  }
1194
1027
  getEventMessage(event) {
1195
1028
  switch (event.type) {
1196
1029
  case HttpEventType.Sent:
1197
1030
  break;
1198
1031
  case HttpEventType.UploadProgress:
1199
- this.progress = event.total === undefined ? 0.5 : event.loaded / event.total;
1032
+ this.progress.set(event.total === undefined ? 0.5 : event.loaded / event.total);
1200
1033
  this.updateClip();
1201
1034
  break;
1202
1035
  case HttpEventType.Response:
1203
1036
  if (event.body === undefined || event.body === null) {
1204
- console.error('media deserialization error. Response body in undefined');
1037
+ console.error("media deserialization error. Response body in undefined");
1205
1038
  }
1206
1039
  else {
1207
1040
  // remove this
@@ -1216,10 +1049,9 @@ class MediaUploaderComponent {
1216
1049
  default:
1217
1050
  break;
1218
1051
  }
1219
- this.cd.detectChanges();
1220
1052
  }
1221
1053
  updateClip() {
1222
- this.clipStyle = this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`);
1054
+ this.clipStyle.set(this.sanitizer.bypassSecurityTrustStyle(`inset(0px 100% 0px 0%)`));
1223
1055
  }
1224
1056
  handleError(file) {
1225
1057
  const func = (error, p2) => {
@@ -1231,256 +1063,180 @@ class MediaUploaderComponent {
1231
1063
  return func;
1232
1064
  }
1233
1065
  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" }] });
1066
+ 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
1067
  }
1236
1068
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MediaUploaderComponent, decorators: [{
1237
1069
  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
- }] } });
1070
+ 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"] }]
1071
+ }], 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
1072
 
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
- }] });
1073
+ const API_BASE_URL = new InjectionToken("ApiBaseUrl");
1267
1074
 
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;
1075
+ class AdminDataService {
1076
+ _http = inject(HttpClient);
1077
+ _baseHref = inject(API_BASE_URL);
1281
1078
  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;
1079
+ console.log(`baseHref: ${this._baseHref}`);
1296
1080
  }
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;
1081
+ getSettingGroups() {
1082
+ const pageOb = this._http.get(`${this._baseHref}api/admin/settings`);
1083
+ return pageOb;
1333
1084
  }
1334
- markAsDirty() {
1335
- this._isDirty = true;
1085
+ getPage(url) {
1086
+ const pageOb = this._http.get(`${this._baseHref}api/admin/pages/${url}`);
1087
+ return pageOb;
1336
1088
  }
1337
- remove() {
1338
- this.removed.next();
1089
+ storePage(page) {
1090
+ const ob = this._http.post(`${this._baseHref}api/admin/pages`, page);
1091
+ return ob;
1339
1092
  }
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);
1093
+ updateSettings(settings) {
1094
+ const ob = this._http.post(`${this._baseHref}api/admin/settings`, settings);
1095
+ return ob;
1348
1096
  }
1349
- startEditing() {
1350
- if (this._isEditing) {
1351
- this.updateDirty();
1352
- return;
1353
- }
1354
- this._isEditing = true;
1355
- this.updateDirty();
1356
- this.editing.emit(true);
1097
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1098
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService });
1099
+ }
1100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AdminDataService, decorators: [{
1101
+ type: Injectable
1102
+ }], ctorParameters: () => [] });
1103
+
1104
+ class DataService {
1105
+ http = inject(HttpClient);
1106
+ baseHref = inject(API_BASE_URL);
1107
+ getView(viewCode) {
1108
+ const pageOb = this.getSkeleton(`${this.baseHref}api/views/${viewCode}`);
1109
+ return pageOb;
1357
1110
  }
1358
- finishEditing() {
1359
- if (this._isDirty)
1360
- throw new Error('Нельзя закрывать редактирование когда есть изменения. Надо сохранить либо зарезетить.');
1361
- if (this.onFinishEditing !== undefined)
1362
- this.onFinishEditing();
1363
- this._isEditing = false;
1111
+ getPage(pageRoute) {
1112
+ const pageUrl = `/${pageRoute}`;
1113
+ const pageOb = this.getSkeleton(`${this.baseHref}api/pages/?url=${encodeURIComponent(pageUrl)}`);
1114
+ return pageOb;
1364
1115
  }
1365
- nextPreset() {
1366
- const newIndex = this._currentPreset === undefined ? 0 : this.presets.indexOf(this._currentPreset) + 1;
1367
- this.applyPresetAtIndex(newIndex);
1116
+ getSettings(ids) {
1117
+ const pageOb = this.http.get(`${this.baseHref}api/settings`, {
1118
+ params: { ids: ids },
1119
+ });
1120
+ return pageOb;
1368
1121
  }
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;
1122
+ getSkeleton(url) {
1123
+ const skeletonOb = this.http.get(url);
1124
+ const routeDataObs = skeletonOb.pipe(map((x) => {
1125
+ const notEmptyDataRoutes = x.bones
1126
+ .map((b) => ("dataRoute" in b && typeof b.dataRoute === "string" ? b.dataRoute : ""))
1127
+ .filter((x) => x.length > 0);
1128
+ if (notEmptyDataRoutes.length === 0) {
1129
+ const emptyData = {};
1130
+ return { page: of(x), data: of(emptyData) };
1387
1131
  }
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 });
1132
+ return {
1133
+ page: of(x),
1134
+ data: combineLatest(notEmptyDataRoutes
1135
+ .map((dataRoute) => {
1136
+ const url = `${this.baseHref}api/Pages/Children/?url=${dataRoute}`;
1137
+ return {
1138
+ route: dataRoute,
1139
+ json: this.http.get(url),
1140
+ };
1141
+ })
1142
+ .reduce((prev, curr) => {
1143
+ prev[curr.route] = curr.json;
1144
+ return prev;
1145
+ }, {})),
1146
+ };
1147
+ }), map((x) => combineLatest(x)), mergeMap((res) => merge(res)), map((x) => {
1148
+ for (const bone of x.page.bones) {
1149
+ if (!("dataRoute" in bone) ||
1150
+ typeof bone.dataRoute !== "string" ||
1151
+ bone.dataRoute.length === 0)
1152
+ continue;
1153
+ const data = x.data[bone.dataRoute];
1154
+ if (data === undefined || data === null)
1155
+ throw new Error(`Data ${bone.dataRoute} have not been preloaded`);
1156
+ bone.data = data; // todo: fix
1157
+ }
1158
+ return x.page;
1159
+ }));
1160
+ return routeDataObs;
1161
+ }
1162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1163
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService });
1392
1164
  }
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
- }] } });
1165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataService, decorators: [{
1166
+ type: Injectable
1167
+ }] });
1412
1168
 
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" }] });
1169
+ class SkeletonEditorAnchorDirective {
1170
+ viewContainerRef = inject(ViewContainerRef);
1171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1172
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: SkeletonEditorAnchorDirective, isStandalone: true, selector: "[boncSkeletonEditorAnchor]", ngImport: i0 });
1421
1173
  }
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"] }]
1174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorAnchorDirective, decorators: [{
1175
+ type: Directive,
1176
+ args: [{
1177
+ selector: "[boncSkeletonEditorAnchor]",
1178
+ }]
1425
1179
  }] });
1426
1180
 
1427
- // todo: rename class
1428
1181
  class BoneEditorContainerComponent {
1429
- anchor;
1430
- removed = new EventEmitter();
1431
- saved = new EventEmitter();
1432
- editing = new EventEmitter();
1182
+ anchor = viewChild.required(SkeletonEditorAnchorDirective);
1183
+ removed = output();
1184
+ saved = output();
1185
+ editing = output();
1433
1186
  DeviceType = DeviceType;
1434
1187
  editor;
1435
1188
  themePopupIsShown = false;
1436
- _bone;
1189
+ bone = input.required(...(ngDevMode ? [{ debugName: "bone" }] : []));
1190
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
1191
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
1192
+ map = input.required(...(ngDevMode ? [{ debugName: "map" }] : []));
1193
+ boneEditorRef;
1437
1194
  removeSubscription;
1438
1195
  saveSubscription;
1439
1196
  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);
1197
+ viewReady = signal(false, ...(ngDevMode ? [{ debugName: "viewReady" }] : []));
1198
+ constructor() {
1199
+ afterNextRender(() => this.viewReady.set(true));
1200
+ effect(() => {
1201
+ if (!this.viewReady()) {
1202
+ return;
1203
+ }
1204
+ const newBone = this.bone();
1205
+ const editorMap = this.map();
1206
+ if (this.removeSubscription) {
1207
+ this.removeSubscription.unsubscribe();
1208
+ this.removeSubscription = undefined;
1209
+ }
1210
+ if (this.saveSubscription) {
1211
+ this.saveSubscription.unsubscribe();
1212
+ this.saveSubscription = undefined;
1213
+ }
1214
+ if (this.changedSubscription) {
1215
+ this.changedSubscription.unsubscribe();
1216
+ this.changedSubscription = undefined;
1217
+ }
1218
+ const viewContainerRef = this.anchor().viewContainerRef;
1219
+ viewContainerRef.clear();
1220
+ const componentType = editorMap.get(newBone.type) ?? UnknownBoneEditorComponent;
1221
+ this.boneEditorRef = viewContainerRef.createComponent(componentType);
1222
+ this.editor = this.boneEditorRef.instance;
1223
+ this.boneEditorRef.setInput("boneEtalon", newBone);
1224
+ this.boneEditorRef.setInput("locale", this.locale());
1225
+ this.boneEditorRef.setInput("device", this.device());
1226
+ this.removeSubscription = this.editor.removed.subscribe(() => {
1227
+ this.removed.emit();
1228
+ });
1229
+ this.changedSubscription = this.editor.editing.subscribe((isEditing) => this.editing.emit(isEditing));
1230
+ this.saveSubscription = this.editor.saved.subscribe((newBoneValue) => this.saved.emit(newBoneValue));
1479
1231
  });
1480
- this.saveSubscription = this.editor.saved.subscribe((newBoneValue) => {
1481
- this.saved.next(newBoneValue);
1232
+ effect(() => {
1233
+ const locale = this.locale();
1234
+ const device = this.device();
1235
+ if (this.boneEditorRef) {
1236
+ this.boneEditorRef.setInput("locale", locale);
1237
+ this.boneEditorRef.setInput("device", device);
1238
+ }
1482
1239
  });
1483
- this.ngOnChanges();
1484
1240
  }
1485
1241
  nextPreset = () => {
1486
1242
  if (this.editor === undefined || this.editor === null)
@@ -1500,9 +1256,13 @@ class BoneEditorContainerComponent {
1500
1256
  // this.editor.bone.visibility = setOrRemoveFlag(this.editor.bone.visibility, DeviceVisibility.Mobile, !disabled);
1501
1257
  // this.editor.updateDirty();
1502
1258
  // }
1259
+ // todo: use this feature
1260
+ // its not working now
1503
1261
  get disabled() {
1504
- if (this.editor === undefined || this.editor === null)
1505
- throw new Error('editor should have been set');
1262
+ if (this.editor === undefined || this.editor === null) {
1263
+ console.warn("editor should have been set before disabled is called");
1264
+ return false;
1265
+ }
1506
1266
  return false;
1507
1267
  // todo: add or remove visibility feature
1508
1268
  // const visibility = this.editor.bone.visibility;
@@ -1515,59 +1275,51 @@ class BoneEditorContainerComponent {
1515
1275
  // return true;
1516
1276
  }
1517
1277
  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]" }] });
1278
+ 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
1279
  }
1520
1280
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BoneEditorContainerComponent, decorators: [{
1521
1281
  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
- }] } });
1282
+ 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"] }]
1283
+ }], 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 }] }] } });
1284
+
1285
+ function createPreset(params) {
1286
+ return {
1287
+ title: params.title,
1288
+ isActive: (x) => x.style === params.style,
1289
+ transformer: (bone) => (bone.style = params.style),
1290
+ clean: (bone) => (bone.style = params.style),
1291
+ };
1292
+ }
1544
1293
 
1545
- // todo: rename class
1546
1294
  class SkeletonEditorComponent {
1547
- boneEditorContainerList;
1548
- locale;
1549
- device = DeviceType.NotSet;
1295
+ boneEditorContainerList = viewChildren("boneEditorContainer", ...(ngDevMode ? [{ debugName: "boneEditorContainerList" }] : []));
1296
+ locale = input.required(...(ngDevMode ? [{ debugName: "locale" }] : []));
1297
+ device = input(DeviceType.NotSet, ...(ngDevMode ? [{ debugName: "device" }] : []));
1298
+ map = input.required(...(ngDevMode ? [{ debugName: "map" }] : []));
1299
+ templates = input.required(...(ngDevMode ? [{ debugName: "templates" }] : []));
1550
1300
  templatesAreShown = [];
1551
1301
  editable = inject((EditableDirective), { host: true });
1552
- ngOnInit() {
1302
+ constructor() {
1303
+ effect(() => {
1304
+ this.map();
1305
+ this.templates();
1306
+ this.templatesAreShown.splice(0, this.templatesAreShown.length);
1307
+ const bones = this.editable.value ?? [];
1308
+ if (bones === undefined) {
1309
+ return;
1310
+ }
1311
+ bones.forEach(() => this.templatesAreShown.push(false));
1312
+ this.templatesAreShown.push(false);
1313
+ });
1553
1314
  this.editable.externalSaveCall.subscribe(() => {
1554
- this.boneEditorContainerList.forEach(editorContainer => editorContainer.editor?.save());
1315
+ this.boneEditorContainerList().forEach((editorContainer) => editorContainer.editor?.save());
1555
1316
  this.editable.save();
1556
1317
  });
1557
1318
  this.editable.canceled.subscribe(() => {
1558
- this.boneEditorContainerList.forEach(editorContainer => editorContainer.editor?.resetData());
1319
+ this.boneEditorContainerList().forEach((editorContainer) => editorContainer.editor?.resetData());
1559
1320
  this.editable.save();
1560
1321
  });
1561
1322
  }
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
1323
  get bones() {
1572
1324
  return this.editable?.value ?? [];
1573
1325
  }
@@ -1575,11 +1327,9 @@ class SkeletonEditorComponent {
1575
1327
  if (isEditing)
1576
1328
  this.editable.startEditing();
1577
1329
  else {
1578
- const allClosed = this.boneEditorContainerList
1579
- .filter(x => x.editor !== undefined && x.editor.isEditing)
1580
- .length === 0;
1330
+ const allClosed = this.boneEditorContainerList().filter((x) => x.editor?.isEditing).length === 0;
1581
1331
  this.editable.updateDirty();
1582
- if (!this.editable.isDirty && allClosed)
1332
+ if (!this.editable.isDirty() && allClosed)
1583
1333
  this.editable.close();
1584
1334
  }
1585
1335
  }
@@ -1592,10 +1342,10 @@ class SkeletonEditorComponent {
1592
1342
  removeBone(boneIndex) {
1593
1343
  if (this.bones === undefined || this.bones === null)
1594
1344
  return;
1595
- if (!confirm('Вы уверены, что хотите удалить компонент?'))
1345
+ if (!confirm("Вы уверены, что хотите удалить компонент?"))
1596
1346
  return;
1597
1347
  this.bones.splice(boneIndex, 1);
1598
- if (!this.editable.inEditMode)
1348
+ if (!this.editable.inEditMode())
1599
1349
  this.editable.startEditing();
1600
1350
  this.editable.save();
1601
1351
  }
@@ -1621,35 +1371,12 @@ class SkeletonEditorComponent {
1621
1371
  this.bones[index2] = tempBone;
1622
1372
  }
1623
1373
  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"] }] });
1374
+ 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
1375
  }
1626
1376
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SkeletonEditorComponent, decorators: [{
1627
1377
  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
- }
1378
+ 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"] }]
1379
+ }], 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
1380
 
1654
1381
  /**
1655
1382
  * Generated bundle index. Do not edit.