@acorex/layout 4.1.4 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. package/karma.conf.js +32 -0
  2. package/ng-package.json +9 -0
  3. package/package.json +11 -31
  4. package/src/lib/widget-board/editors/widget-size-editor/widget-size.editor.html +10 -0
  5. package/src/lib/widget-board/editors/widget-size-editor/widget-size.editor.ts +55 -0
  6. package/src/lib/widget-board/editors/widget-size-editor/widget-size.module.ts +19 -0
  7. package/src/lib/widget-board/widget-board.component.html +7 -0
  8. package/src/lib/widget-board/widget-board.component.scss +198 -0
  9. package/src/lib/widget-board/widget-board.component.ts +510 -0
  10. package/src/lib/widget-board/widget-board.module.ts +74 -0
  11. package/src/lib/widget-board/widget-config.component.html +16 -0
  12. package/src/lib/widget-board/widget-config.component.ts +99 -0
  13. package/src/lib/widget-board/widget-host.component.html +30 -0
  14. package/src/lib/widget-board/widget-host.component.ts +343 -0
  15. package/src/lib/widget-board/widget-save.component.html +43 -0
  16. package/src/lib/widget-board/widget-save.component.ts +88 -0
  17. package/src/lib/widget-board/widget.class.ts +178 -0
  18. package/{public-api.d.ts → src/public-api.ts} +0 -0
  19. package/src/test.ts +28 -0
  20. package/tsconfig.lib.json +23 -0
  21. package/tsconfig.lib.prod.json +6 -0
  22. package/tsconfig.spec.json +17 -0
  23. package/tslint.json +17 -0
  24. package/acorex-layout.d.ts +0 -5
  25. package/esm2020/acorex-layout.mjs +0 -5
  26. package/esm2020/lib/widget-board/editors/widget-size-editor/widget-size.editor.mjs +0 -46
  27. package/esm2020/lib/widget-board/editors/widget-size-editor/widget-size.module.mjs +0 -21
  28. package/esm2020/lib/widget-board/widget-board.component.mjs +0 -441
  29. package/esm2020/lib/widget-board/widget-board.module.mjs +0 -111
  30. package/esm2020/lib/widget-board/widget-config.component.mjs +0 -82
  31. package/esm2020/lib/widget-board/widget-host.component.mjs +0 -290
  32. package/esm2020/lib/widget-board/widget-save.component.mjs +0 -79
  33. package/esm2020/lib/widget-board/widget.class.mjs +0 -127
  34. package/esm2020/public-api.mjs +0 -7
  35. package/fesm2015/acorex-layout.mjs +0 -1166
  36. package/fesm2015/acorex-layout.mjs.map +0 -1
  37. package/fesm2020/acorex-layout.mjs +0 -1159
  38. package/fesm2020/acorex-layout.mjs.map +0 -1
  39. package/lib/widget-board/editors/widget-size-editor/widget-size.editor.d.ts +0 -19
  40. package/lib/widget-board/editors/widget-size-editor/widget-size.module.d.ts +0 -10
  41. package/lib/widget-board/widget-board.component.d.ts +0 -61
  42. package/lib/widget-board/widget-board.module.d.ts +0 -18
  43. package/lib/widget-board/widget-config.component.d.ts +0 -23
  44. package/lib/widget-board/widget-host.component.d.ts +0 -51
  45. package/lib/widget-board/widget-save.component.d.ts +0 -19
  46. package/lib/widget-board/widget.class.d.ts +0 -56
@@ -0,0 +1,510 @@
1
+ import {
2
+ Component,
3
+ OnInit,
4
+ Input,
5
+ ViewEncapsulation,
6
+ ElementRef,
7
+ ViewChild,
8
+ NgZone,
9
+ ChangeDetectionStrategy,
10
+ ChangeDetectorRef,
11
+ Output,
12
+ EventEmitter, ViewChildren, QueryList
13
+ } from '@angular/core';
14
+ import {
15
+ AXWidgetConfig,
16
+ AXWidgetConfigChanged,
17
+ AXWidgetBoardConfigChanged,
18
+ AXWidgetConfigSavedEvent
19
+ } from './widget.class';
20
+ import { AXWidgetHostComponent } from './widget-host.component';
21
+ import { AXHtmlUtil, AXClientRec } from '@acorex/core';
22
+ import { Observable } from 'rxjs';
23
+ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
24
+
25
+ @Component({
26
+ selector: 'ax-widget-board',
27
+ templateUrl: './widget-board.component.html',
28
+ styleUrls: ['./widget-board.component.scss'],
29
+ host: { class: 'ax widget-board' },
30
+ encapsulation: ViewEncapsulation.None,
31
+ changeDetection: ChangeDetectionStrategy.OnPush
32
+ })
33
+ export class AXWidgetBoardComponent implements OnInit {
34
+
35
+ @ViewChild('container')
36
+ private container: ElementRef<HTMLDivElement>;
37
+
38
+ @ViewChildren(AXWidgetHostComponent)
39
+ private widgetHosts: QueryList<AXWidgetHostComponent>;
40
+
41
+ widgets: AXWidgetConfig[] = [];
42
+
43
+ @Input()
44
+ galleryItems: AXWidgetConfig[] = [];
45
+
46
+ @Input()
47
+ tileSize: number = 80;
48
+
49
+ @Input()
50
+ gapSize: number = 5;
51
+
52
+ private newWidget: AXWidgetConfig = null;
53
+
54
+ public rtl: boolean;
55
+
56
+ private _isInEditing: boolean = false;
57
+ public isInEditing(): boolean {
58
+ return this._isInEditing;
59
+ }
60
+ private isDragging: boolean = false;
61
+ private dragItem: HTMLElement;
62
+
63
+ @Input()
64
+ provideValue: (e: any) => any;
65
+
66
+ private readonly DATA_COL = 'data-col';
67
+ private readonly DATA_ROW = 'data-row';
68
+ private readonly DATA_SIZE_X = 'data-size-x';
69
+ private readonly DATA_SIZE_Y = 'data-size-y';
70
+ private readonly DATA_OLD_COL = 'data-old-col';
71
+ private readonly DATA_OLD_ROW = 'data-old-row';
72
+
73
+
74
+ @Output()
75
+ onConfigChanged: EventEmitter<AXWidgetBoardConfigChanged> = new EventEmitter<AXWidgetBoardConfigChanged>();
76
+
77
+ @Output()
78
+ onWidgetSave: EventEmitter<AXWidgetConfigSavedEvent> = new EventEmitter<AXWidgetConfigSavedEvent>();
79
+
80
+ constructor(
81
+ private ref: ElementRef<HTMLDivElement>,
82
+ private zone: NgZone,
83
+ private cdr: ChangeDetectorRef
84
+ ) { }
85
+
86
+
87
+ ngOnInit() {
88
+ if (this.rtl == null) {
89
+ this.rtl = window.getComputedStyle(this.ref.nativeElement, null).getPropertyValue('direction') === 'rtl';
90
+ }
91
+ }
92
+
93
+ ngAfterViewInit() {
94
+ this.zone.runOutsideAngular((c) => {
95
+ const style = document.createElement('style');
96
+ style.type = 'text/css';
97
+ this.ref.nativeElement.appendChild(style);
98
+ // add css data classes
99
+ for (let i = 1; i <= 50; i++) {
100
+ style.innerHTML += `[${this.DATA_COL}="${i}"] { ${this.rtl ? 'right' : 'left'}: ${(i - 1) * (this.tileSize + this.gapSize)}px; }`;
101
+ style.innerHTML += `[${this.DATA_ROW}="${i}"] { top: ${(i - 1) * (this.tileSize + this.gapSize)}px; }`;
102
+ style.innerHTML += `[${this.DATA_SIZE_X}="${i}"] { width: ${(i * this.tileSize) + ((i - 1) * this.gapSize)}px; }`;
103
+ style.innerHTML += `[${this.DATA_SIZE_Y}="${i}"] { height: ${(i * this.tileSize) + ((i - 1) * this.gapSize)}px; }`;
104
+ }
105
+ });
106
+ }
107
+
108
+ private calcGridSize() {
109
+ this.zone.runOutsideAngular(() => {
110
+ const width =
111
+ (Math.max(...this.widgets.map((c) => c.col + c.sizeX - 1))) * (this.tileSize + this.gapSize);
112
+ const height =
113
+ (Math.max(...this.widgets.map((c) => c.row + c.sizeY - 1))) * (this.tileSize + this.gapSize);
114
+ this.container.nativeElement.style.width = width + 'px';
115
+ this.container.nativeElement.style.height = height + 'px';
116
+ });
117
+ }
118
+
119
+ toggleEdit() {
120
+ this._isInEditing ? this.finishEdit() : this.startEdit();
121
+ }
122
+
123
+
124
+
125
+ private dragStart(e: MouseEvent) {
126
+ e.preventDefault();
127
+ e.stopPropagation();
128
+ this.zone.runOutsideAngular(() => {
129
+ if (this._isInEditing && e.which === 1) {
130
+ this.dragItem = e.currentTarget as any;
131
+ this.dragItem.setAttribute('data-x-offset', (this.dragItem.offsetLeft - e.clientX).toString());
132
+ //
133
+ this.dragItem.setAttribute('data-y-offset', (this.dragItem.offsetTop - e.clientY).toString());
134
+ this.dragItem.classList.add('widget-dragging');
135
+ this.dragItem.classList.remove('animate__animated', 'animate__pulse');
136
+ }
137
+ });
138
+ return false;
139
+ }
140
+
141
+ removePositionData() {
142
+ this.zone.runOutsideAngular(() => {
143
+ if (this.dragItem && this._isInEditing && this.dragItem.getAttribute(this.DATA_OLD_COL) == null) {
144
+ this.dragItem.setAttribute(this.DATA_OLD_COL, this.dragItem.getAttribute(this.DATA_COL));
145
+ this.dragItem.setAttribute(this.DATA_OLD_ROW, this.dragItem.getAttribute(this.DATA_ROW));
146
+ this.dragItem.removeAttribute(this.DATA_COL);
147
+ this.dragItem.removeAttribute(this.DATA_ROW);
148
+ }
149
+ });
150
+ }
151
+
152
+ resetPositionData() {
153
+ this.zone.runOutsideAngular(() => {
154
+ if (this.dragItem && this._isInEditing) {
155
+ this.setPosition(this.dragItem, this.dragItem.getAttribute(this.DATA_OLD_COL), this.dragItem.getAttribute(this.DATA_OLD_ROW));
156
+ }
157
+ });
158
+ }
159
+
160
+ private setPosition(element: HTMLElement, col: string, row: string) {
161
+ this.zone.runOutsideAngular(() => {
162
+ element.setAttribute(this.DATA_COL, col);
163
+ element.setAttribute(this.DATA_ROW, row);
164
+ element.removeAttribute(this.DATA_OLD_COL);
165
+ element.removeAttribute(this.DATA_OLD_ROW);
166
+ element.style.removeProperty('top');
167
+ element.style.removeProperty('left');
168
+ const widget = this.widgets.find(c => c['__meta__'].instance.element === element);
169
+ widget.col = Number(col);
170
+ widget.row = Number(row);
171
+ });
172
+ }
173
+
174
+ private drag(e) {
175
+ e.preventDefault();
176
+ e.stopPropagation();
177
+ this.zone.runOutsideAngular(() => {
178
+ if (this.dragItem && this._isInEditing) {
179
+ this.isDragging = true;
180
+ this.addPlaceholder();
181
+ const xOffset = Number(this.dragItem.getAttribute('data-x-offset'));
182
+ const yOffset = Number(this.dragItem.getAttribute('data-y-offset'));
183
+ this.dragItem.style.left = e.clientX + xOffset + 'px';
184
+ this.dragItem.style.top = e.clientY + yOffset + 'px';
185
+ //
186
+ this.detectBestPlacement();
187
+ this.removePositionData();
188
+ }
189
+ });
190
+ return false;
191
+ }
192
+
193
+ private dragEnd(e) {
194
+ this.zone.runOutsideAngular(() => {
195
+ if (this.dragItem && this._isInEditing && this.isDragging) {
196
+ this.dragItem.classList.remove('widget-dragging');
197
+ this.dragItem.classList.add('animate__animated', 'animate__pulse');
198
+ //
199
+ const p = this.container.nativeElement.querySelector('.widget-blank-placeholder');
200
+ if (this.newWidget) {
201
+ const w = {
202
+ uniqueName: this.newWidget.uniqueName,
203
+ component: this.newWidget.component,
204
+ title: this.newWidget.title,
205
+ options: this.newWidget.options,
206
+ sizeX: this.newWidget.sizeX,
207
+ sizeY: this.newWidget.sizeY,
208
+ col: Number(p.getAttribute(this.DATA_COL)),
209
+ row: Number(p.getAttribute(this.DATA_ROW))
210
+ };
211
+
212
+ this.widgets.push(w);
213
+ this.newWidget = null;
214
+ this.container.nativeElement.removeChild(this.dragItem);
215
+ this.zone.run(() => {
216
+ this.cdr.detectChanges();
217
+ this.calcGridSize();
218
+ setTimeout(() => {
219
+ w['__meta__'].instance.element.addEventListener('mousedown', this.dragStart.bind(this), false);
220
+ }, 1000);
221
+ });
222
+ } else {
223
+ if (p) {
224
+ this.setPosition(this.dragItem, p.getAttribute(this.DATA_COL), p.getAttribute(this.DATA_ROW));
225
+ }
226
+ else {
227
+ this.setPosition(this.dragItem, this.dragItem.getAttribute(this.DATA_OLD_COL), this.dragItem.getAttribute(this.DATA_OLD_ROW));
228
+ }
229
+ }
230
+ //
231
+ this.removePlaceholder();
232
+ this.dragItem = null;
233
+ this.calcGridSize();
234
+ this.emitConfigChanged();
235
+ }
236
+ this.isDragging = false;
237
+ });
238
+ }
239
+
240
+ private detectFirstEmptySlot(w: AXWidgetConfig) {
241
+ const xTile = Math.floor(this.ref.nativeElement.parentElement.offsetWidth / this.tileSize);
242
+ for (let j = 1; j <= 100; j++) {
243
+ for (let i = 1; i <= xTile - w.sizeX; i++) {
244
+ const rec = new AXClientRec({
245
+ left: i,
246
+ top: j,
247
+ width: w.sizeX,
248
+ height: w.sizeY
249
+ });
250
+ const el = this.widgets.filter(c => c !== w).find((c) =>
251
+ rec.intersect({
252
+ left: c.col,
253
+ top: c.row,
254
+ width: c.sizeX,
255
+ height: c.sizeY
256
+ })
257
+ );
258
+ if (el == null) {
259
+ w.col = i;
260
+ w.row = j;
261
+ return;
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+ private detectBestPlacement() {
268
+ this.zone.runOutsideAngular(() => {
269
+ const p = this.container.nativeElement.querySelector('.widget-blank-placeholder') as HTMLElement;
270
+ let col = Math.ceil(this.dragItem.offsetLeft / this.tileSize);
271
+ if (this.rtl) {
272
+ col = Math.ceil((this.container.nativeElement.clientWidth - (this.dragItem.offsetLeft + this.dragItem.clientWidth)) / this.tileSize);
273
+ }
274
+
275
+ let row = Math.ceil(this.dragItem.offsetTop / this.tileSize);
276
+ if (col < 1) {
277
+ col = 1;
278
+ }
279
+ if (row < 1) {
280
+ row = 1;
281
+ }
282
+ const widgets = Array.from(this.ref.nativeElement.querySelectorAll('.widget-host')).map((c) => c as HTMLElement);
283
+ p.setAttribute(this.DATA_COL, col.toString());
284
+ p.setAttribute(this.DATA_ROW, row.toString());
285
+ const collision = widgets.filter(c => c !== this.dragItem).some(c => AXHtmlUtil.collision(c, this.dragItem));
286
+ if (collision) {
287
+ this.removePlaceholder();
288
+ }
289
+ });
290
+ }
291
+
292
+
293
+
294
+
295
+ private addPlaceholder() {
296
+ this.zone.runOutsideAngular(() => {
297
+ this.removePlaceholder();
298
+ if (this.dragItem) {
299
+ const p = document.createElement('div');
300
+ p.classList.add('widget-blank-placeholder');
301
+ p.setAttribute(this.DATA_COL, this.dragItem.getAttribute(this.DATA_COL));
302
+ p.setAttribute(this.DATA_ROW, this.dragItem.getAttribute(this.DATA_ROW));
303
+ p.setAttribute(this.DATA_SIZE_X, this.dragItem.getAttribute(this.DATA_SIZE_X));
304
+ p.setAttribute(this.DATA_SIZE_Y, this.dragItem.getAttribute(this.DATA_SIZE_Y));
305
+ this.container.nativeElement.appendChild(p);
306
+ }
307
+ });
308
+ }
309
+
310
+ private removePlaceholder() {
311
+ const p = this.container.nativeElement.querySelector('.widget-blank-placeholder');
312
+ p?.remove();
313
+ }
314
+
315
+ public startEdit() {
316
+ this._isInEditing = true;
317
+ this.calcGridSize();
318
+ this.ref.nativeElement.classList.add('grid-background');
319
+ const bg = this.ref.nativeElement as HTMLDivElement;
320
+ bg.style.setProperty('background-position', `${this.rtl ? 'right' : 'left'} top`);
321
+ // tslint:disable-next-line: max-line-length
322
+ const pattern = `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${this.tileSize + this.gapSize}" height="${this.tileSize + this.gapSize}"> <rect style="fill: %23dadada" x="${this.rtl ? this.gapSize : 0}" width="${this.tileSize}" height="${this.tileSize}" y="0"></rect></svg>')`;
323
+ bg.style.setProperty('background-image', pattern);
324
+ //
325
+ const widgets = Array.from(this.ref.nativeElement.querySelectorAll('.widget-host'));
326
+ widgets.forEach((w) => {
327
+ w.addEventListener('mousedown', this.dragStart.bind(this), false);
328
+ });
329
+ this.ref.nativeElement.addEventListener('mousemove', this.drag.bind(this), false);
330
+ this.ref.nativeElement.addEventListener('mouseup', this.dragEnd.bind(this), false);
331
+ }
332
+
333
+ public finishEdit() {
334
+ this.ref.nativeElement.classList.remove('grid-background');
335
+ const bg = this.ref.nativeElement as HTMLDivElement;
336
+ bg.style.removeProperty('background-image');
337
+ //
338
+ this._isInEditing = false;
339
+ const widgets = Array.from(this.ref.nativeElement.querySelectorAll('.widget-host'));
340
+ widgets.forEach((w) => {
341
+ w.removeEventListener('mousedown', this.dragStart.bind(this), false);
342
+ });
343
+ this.ref.nativeElement.removeEventListener('mousemove', this.drag.bind(this), false);
344
+ this.ref.nativeElement.removeEventListener('mouseup', this.dragEnd.bind(this), false);
345
+ this.emitConfigChanged();
346
+ }
347
+
348
+
349
+ addWidget(widget: AXWidgetConfig) {
350
+ this.zone.runOutsideAngular(() => {
351
+ this.newWidget = {
352
+ uniqueName: widget.uniqueName,
353
+ component: widget.component,
354
+ title: widget.title,
355
+ col: 1,
356
+ row: 1,
357
+ sizeX: widget.sizeX,
358
+ sizeY: widget.sizeY,
359
+ options: widget.options,
360
+ props: widget.props
361
+ };
362
+ this.detectFirstEmptySlot(this.newWidget);
363
+ const w = {
364
+ uniqueName: this.newWidget.uniqueName,
365
+ component: this.newWidget.component,
366
+ title: this.newWidget.title,
367
+ options: this.newWidget.options,
368
+ props: this.newWidget.props,
369
+ sizeX: this.newWidget.sizeX,
370
+ sizeY: this.newWidget.sizeY,
371
+ col: this.newWidget.col,
372
+ row: this.newWidget.row
373
+ };
374
+ this.widgets.push(w);
375
+ this.newWidget = null;
376
+ this.zone.run(() => {
377
+ this.cdr.detectChanges();
378
+ this.calcGridSize();
379
+ this.emitConfigChanged();
380
+ setTimeout(() => {
381
+ w['__meta__'].instance.element.addEventListener('mousedown', this.dragStart.bind(this), false);
382
+ }, 1000);
383
+ });
384
+ });
385
+ }
386
+
387
+
388
+ handleOnRemove(w: AXWidgetHostComponent) {
389
+ w.element.classList.add('animate__animated', 'animate__zoomOut');
390
+ w.element.addEventListener('animationend', () => {
391
+ this.widgets = this.widgets.filter((c) => (c as any).__meta__.id !== (w.config as any).__meta__.id);
392
+ this.cdr.detectChanges();
393
+ this.calcGridSize();
394
+ this.emitConfigChanged();
395
+ });
396
+ }
397
+
398
+ handleOnSave(e: AXWidgetConfigSavedEvent) {
399
+ this.onWidgetSave.emit(e);
400
+ }
401
+
402
+ trackByFn(index, item) {
403
+ if (!item['__meta__']?.id) {
404
+ item['__meta__'] = {};
405
+ item['__meta__'].id = AXHtmlUtil.getUID();
406
+ }
407
+ return item['__meta__']?.id;
408
+ }
409
+
410
+
411
+ load(widgets: string | AXWidgetConfig[]): Promise<void> {
412
+ this.clear();
413
+ return new Promise<void>((resolve, reject) => {
414
+ if (widgets) {
415
+ const loadedWidgets: AXWidgetConfig[] = [];
416
+ if (typeof widgets === 'string') {
417
+ try {
418
+ loadedWidgets.push(...JSON.parse(widgets));
419
+ } catch (error) {
420
+ reject('Invalid widget json data!');
421
+ }
422
+ }
423
+ else {
424
+ loadedWidgets.push(...widgets);
425
+ }
426
+ let intervalId: number = -1;
427
+ const loadFunc = () => {
428
+
429
+ if (this.galleryItems && this.galleryItems.length > 0) {
430
+ loadedWidgets.forEach(w => {
431
+ const gitem = this.galleryItems.find(c => c.uniqueName === w.uniqueName);
432
+ if (gitem) {
433
+ w.component = gitem.component;
434
+ if (gitem.props) {
435
+ w.props = JSON.parse(JSON.stringify(gitem.props));
436
+ }
437
+ }
438
+ });
439
+ this.widgets.push(...loadedWidgets);
440
+ window.clearInterval(intervalId);
441
+ this.cdr.detectChanges();
442
+ resolve();
443
+ }
444
+ };
445
+ intervalId = window.setInterval(loadFunc, 200);
446
+ }
447
+ else {
448
+ resolve();
449
+ }
450
+ });
451
+ }
452
+
453
+ clear() {
454
+ if (this.widgets.length) {
455
+ this.widgets = [];
456
+ this.cdr.detectChanges();
457
+ this.emitConfigChanged();
458
+ }
459
+ }
460
+
461
+ save(): Promise<string> {
462
+ const obj = this.widgets.map(c => ({
463
+ uniqueName: c.uniqueName,
464
+ component: c.component,
465
+ title: c.title,
466
+ sizeX: c.sizeX,
467
+ sizeY: c.sizeY,
468
+ col: c.col,
469
+ row: c.row,
470
+ options: c.options,
471
+ props: c.props
472
+ }));
473
+ return Promise.resolve(JSON.stringify(obj));
474
+ }
475
+
476
+ refresh() {
477
+ this.widgetHosts.forEach(host => {
478
+ host.widget.refresh();
479
+ });
480
+ }
481
+
482
+ handleOnConfigChanged(e: AXWidgetConfigChanged) {
483
+ this.cdr.detectChanges();
484
+ this.emitConfigChanged();
485
+ }
486
+
487
+ handleOnResizedChanged(e: AXWidgetConfigChanged) {
488
+ this.detectFirstEmptySlot(e.config);
489
+ this.cdr.detectChanges();
490
+ this.emitConfigChanged();
491
+ }
492
+
493
+ private resizeChangeObserver: any;
494
+ private emitConfigChanged() {
495
+ if (!this.resizeChangeObserver) {
496
+ this.resizeChangeObserver = new Observable();
497
+ Observable.create(observer => {
498
+ this.resizeChangeObserver = observer;
499
+ })
500
+ .pipe(debounceTime(750))
501
+ .pipe(distinctUntilChanged())
502
+ .subscribe(c => {
503
+ this.onConfigChanged.emit({
504
+ component: this
505
+ });
506
+ });
507
+ }
508
+ this.resizeChangeObserver.next(new Date());
509
+ }
510
+ }
@@ -0,0 +1,74 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { AXWidgetBoardComponent } from './widget-board.component';
3
+ import { CommonModule } from '@angular/common';
4
+ import { AXWidgetHostComponent } from './widget-host.component';
5
+ import {
6
+ AXSearchBoxModule,
7
+ AXLoadingModule,
8
+ AXProppertyEditorModule,
9
+ AXPageModule, AXLabelModule,
10
+ AXToolbarModule,
11
+ AXTabStripModule,
12
+ AXMenuModule,
13
+ AXFormGroupModule,
14
+ AXTextAreaModule, AXTextBoxModule, AXFieldsetModule, AXCheckBoxModule, AXValidationModule
15
+ } from '@acorex/components';
16
+ import { RouterModule } from '@angular/router';
17
+ import { AXWidgetConfigComponent } from './widget-config.component';
18
+ import { AXTranslator, AXTranslatorModule } from '@acorex/core';
19
+ import { AXWidgetSizePropertyEditorComponent } from './editors/widget-size-editor/widget-size.editor';
20
+ import { AXWidgetSizePropertyEditorModule } from './editors/widget-size-editor/widget-size.module';
21
+ import { AXWidgetSaveComponent } from './widget-save.component';
22
+
23
+ @NgModule({
24
+ imports: [
25
+ CommonModule,
26
+ AXSearchBoxModule,
27
+ AXLoadingModule,
28
+ RouterModule,
29
+ AXProppertyEditorModule,
30
+ AXPageModule,
31
+ AXToolbarModule,
32
+ AXTranslatorModule,
33
+ AXLabelModule,
34
+ AXTextAreaModule,
35
+ AXFieldsetModule,
36
+ AXTextBoxModule,
37
+ AXFormGroupModule,
38
+ AXTabStripModule,
39
+ AXCheckBoxModule,
40
+ AXMenuModule,
41
+ AXTranslatorModule,
42
+ AXValidationModule,
43
+ RouterModule.forChild([
44
+ {
45
+ component: AXWidgetSizePropertyEditorComponent,
46
+ path: 'ax/editors/widget-size'
47
+ },
48
+ ])
49
+ ],
50
+ exports: [AXWidgetBoardComponent, AXProppertyEditorModule],
51
+ declarations: [AXWidgetBoardComponent, AXWidgetHostComponent, AXWidgetConfigComponent, AXWidgetSaveComponent],
52
+ providers: []
53
+ })
54
+ export class AXWidgetBoardModule {
55
+ /**
56
+ *
57
+ */
58
+ constructor() {
59
+ AXTranslator.load('en', {
60
+ 'widget-board': {
61
+ 'configurable-props': 'Configurable Properties',
62
+ configure: 'Configure Widget'
63
+ }
64
+ });
65
+ AXTranslator.load('fa', {
66
+ 'widget-board': {
67
+ 'configurable-props': 'ویژگی های قابل تنظیم',
68
+ configure: 'پیکر بندی ابزارک'
69
+ }
70
+ });
71
+
72
+ }
73
+
74
+ }
@@ -0,0 +1,16 @@
1
+ <ax-page>
2
+ <ax-page-content>
3
+ <div class="container">
4
+ <ax-validation-form #form>
5
+ <div class="row" *ngFor="let p of displayProps; trackBy: identify">
6
+ <div class="col-12">
7
+ <ax-label>{{p.property.title | trans}}</ax-label>
8
+ <ng-container ax-property-editor-renderer [property]="p" [context]="context" [host]="widget" [validationForm]="form"
9
+ (onValueChange)="handleValueChange($event)">
10
+ </ng-container>
11
+ </div>
12
+ </div>
13
+ </ax-validation-form>
14
+ </div>
15
+ </ax-page-content>
16
+ </ax-page>
@@ -0,0 +1,99 @@
1
+ import { Component, ViewEncapsulation, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild } from '@angular/core';
2
+ import { AXPropertyConfig, AXBasePopupPageComponent, AXMenuItemClickEvent, AXProperyEditorValueChangeEvent, AXValidationFormComponent } from '@acorex/components';
3
+ import { AXButtonItem, AXTranslator } from '@acorex/core';
4
+ import { AXWidgetComponent } from './widget.class';
5
+
6
+
7
+ @Component({
8
+ templateUrl: './widget-config.component.html',
9
+ encapsulation: ViewEncapsulation.None,
10
+ changeDetection: ChangeDetectionStrategy.OnPush
11
+ })
12
+
13
+ export class AXWidgetConfigComponent extends AXBasePopupPageComponent {
14
+
15
+ @ViewChild(AXValidationFormComponent) form: AXValidationFormComponent;
16
+
17
+ constructor(private cdr: ChangeDetectorRef) {
18
+ super();
19
+ }
20
+
21
+
22
+ widget: AXWidgetComponent;
23
+ props: AXPropertyConfig[] = [];
24
+ displayProps: AXPropertyConfig[] = [];
25
+
26
+
27
+ changes: AXPropertyConfig[] = [];
28
+
29
+ context: any = {};
30
+
31
+
32
+ getFooterButtons(): AXButtonItem[] {
33
+ return [
34
+ {
35
+ name: 'okay',
36
+ submitBehavior: true,
37
+ text: AXTranslator.get('common.confirm'),
38
+ style: 'success'
39
+ },
40
+ {
41
+ name: 'cancel',
42
+ cancelBehavior: true,
43
+ text: AXTranslator.get('common.cancel'),
44
+ style: 'danger blank'
45
+ }
46
+ ];
47
+ }
48
+
49
+
50
+
51
+
52
+
53
+ onFooterButtonClick(e: AXMenuItemClickEvent) {
54
+ if (e.name === 'cancel') {
55
+ this.close();
56
+ }
57
+ if (e.name === 'okay') {
58
+ this.form.validate().then(c => {
59
+ if (c.result) {
60
+ this.close(this.changes);
61
+ }
62
+ });
63
+ }
64
+ }
65
+
66
+ handleValueChange(e: AXProperyEditorValueChangeEvent) {
67
+ const prop = this.changes.find(c => c.property.name === e.property.name);
68
+ if (prop) {
69
+ prop.value = e.value;
70
+ }
71
+ else {
72
+ this.changes.push({ property: e.property, value: e.value });
73
+ }
74
+ this.updateContext();
75
+ }
76
+
77
+ ngOnInit() {
78
+ this.displayProps = this.props.filter(c => c.property.visible !== false).sort((a, b) => a.property.order - b.property.order);
79
+ this.updateContext();
80
+ }
81
+
82
+
83
+ private updateContext() {
84
+ const ctx = {};
85
+ this.props.forEach(p => {
86
+ ctx[p.property.name] = p.value;
87
+ });
88
+ this.changes.forEach(p => {
89
+ ctx[p.property.name] = p.value;
90
+ });
91
+ this.context = ctx;
92
+ }
93
+
94
+
95
+ identify(index, item: AXPropertyConfig) {
96
+ return item.property.name;
97
+ }
98
+
99
+ }