Browse Source

Collapse all items in array.

pull/425/head
Sebastian Stehle 7 years ago
parent
commit
3e96c21410
  1. 23
      src/Squidex/app/features/content/shared/array-editor.component.html
  2. 34
      src/Squidex/app/features/content/shared/array-editor.component.ts
  3. 74
      src/Squidex/app/features/content/shared/array-item.component.html
  4. 10
      src/Squidex/app/features/content/shared/array-item.component.scss
  5. 89
      src/Squidex/app/features/content/shared/array-item.component.ts

23
src/Squidex/app/features/content/shared/array-editor.component.html

@ -11,7 +11,6 @@
[form]="form" [form]="form"
[formContext]="formContext" [formContext]="formContext"
[field]="field" [field]="field"
[isHidden]="snapshot.isHidden"
[isDisabled]="arrayControl.disabled" [isDisabled]="arrayControl.disabled"
[isFirst]="i === 0" [isFirst]="i === 0"
[isLast]="i === arrayControl.controls.length - 1" [isLast]="i === arrayControl.controls.length - 1"
@ -21,16 +20,28 @@
[languages]="languages" [languages]="languages"
(clone)="itemAdd(itemForm)" (clone)="itemAdd(itemForm)"
(move)="move(itemForm, $event)" (move)="move(itemForm, $event)"
(remove)="itemRemove(i)" (remove)="itemRemove(i)">
(toggle)="hide($event)">
<i cdkDragHandle class="icon-drag2"></i> <i cdkDragHandle class="icon-drag2"></i>
</sqx-array-item> </sqx-array-item>
</div> </div>
</div> </div>
<button type="button" class="btn btn-success" [disabled]="field.nested.length === 0 || arrayControl.disabled" (click)="itemAdd(undefined)"> <div class="row">
Add Item <div class="col">
</button> <button type="button" class="btn btn-success" [disabled]="field.nested.length === 0 || arrayControl.disabled" (click)="itemAdd(undefined)">
Add Item
</button>
</div>
<div class="col-auto" *ngIf="arrayControl.controls.length > 0">
<button type="button" class="btn btn-text-secondary" (click)="expandAll()" title="Expand all items">
<i class="icon-plus-square"></i>
</button>
<button type="button" class="btn btn-text-secondary" (click)="collapseAll()" title="Collapse all items">
<i class="icon-minus-square"></i>
</button>
</div>
</div>
<small class="text-muted ml-2" *ngIf="field.nested.length === 0"> <small class="text-muted ml-2" *ngIf="field.nested.length === 0">
Add a nested field first to add items. Add a nested field first to add items.

34
src/Squidex/app/features/content/shared/array-editor.component.ts

@ -6,20 +6,17 @@
*/ */
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, QueryList, ViewChildren } from '@angular/core';
import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
import { import {
AppLanguageDto, AppLanguageDto,
EditContentForm, EditContentForm,
RootFieldDto, RootFieldDto,
sorted, sorted
StatefulComponent
} from '@app/shared'; } from '@app/shared';
interface State { import { ArrayItemComponent } from './array-item.component';
isHidden: boolean;
}
@Component({ @Component({
selector: 'sqx-array-editor', selector: 'sqx-array-editor',
@ -27,7 +24,7 @@ interface State {
templateUrl: './array-editor.component.html', templateUrl: './array-editor.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ArrayEditorComponent extends StatefulComponent<State> { export class ArrayEditorComponent {
@Input() @Input()
public form: EditContentForm; public form: EditContentForm;
@ -46,15 +43,8 @@ export class ArrayEditorComponent extends StatefulComponent<State> {
@Input() @Input()
public arrayControl: FormArray; public arrayControl: FormArray;
constructor(changeDetector: ChangeDetectorRef) { @ViewChildren(ArrayItemComponent)
super(changeDetector, { public children: QueryList<ArrayItemComponent>;
isHidden: false
});
}
public hide(isHidden: boolean) {
this.next(s => ({ ...s, isHidden }));
}
public itemRemove(index: number) { public itemRemove(index: number) {
this.form.arrayItemRemove(this.field, this.language, index); this.form.arrayItemRemove(this.field, this.language, index);
@ -68,6 +58,18 @@ export class ArrayEditorComponent extends StatefulComponent<State> {
this.sortInternal(sorted(event)); this.sortInternal(sorted(event));
} }
public collapseAll() {
this.children.forEach(component => {
component.collapse();
});
}
public expandAll() {
this.children.forEach(component => {
component.expand();
});
}
public move(control: AbstractControl, index: number) { public move(control: AbstractControl, index: number) {
let controls = [...this.arrayControl.controls]; let controls = [...this.arrayControl.controls];

74
src/Squidex/app/features/content/shared/array-item.component.html

@ -1,41 +1,45 @@
<div class="card item" [class.invalid]="isInvalid | async"> <div class="card item" [class.invalid]="isInvalid | async">
<div class="card-header drag-handle"> <div class="card-header drag-handle">
<span class="pull-left"> <div class="row">
<span class="mr-1"> <div class="col-auto pr-1">
<ng-content></ng-content> <ng-content></ng-content>
</span> </div>
<div class="col">
<span class="header-text text-decent">Item #{{index + 1}}</span> <div class="truncate">
<span class="header-index">#{{index + 1}}</span>
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isFirst" (click)="emitMoveTop()"> <span class="header-title">{{title}}</span>
<i class="icon-caret-top"></i> </div>
</button> </div>
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isFirst" (click)="emitMoveUp()"> <div class="col-auto pr-4">
<i class="icon-caret-up"></i> <button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isFirst" (click)="emitMoveTop()">
</button> <i class="icon-caret-top"></i>
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isLast" (click)="emitMoveDown()"> </button>
<i class="icon-caret-down"></i> <button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isFirst" (click)="emitMoveUp()">
</button> <i class="icon-caret-up"></i>
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isLast" (click)="emitMoveBottom()"> </button>
<i class="icon-caret-bottom"></i> <button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isLast" (click)="emitMoveDown()">
</button> <i class="icon-caret-down"></i>
<button type="button" class="btn btn-text-secondary" [class.hidden]="!isHidden" (click)="emitToggle(false)" title="Open all items"> </button>
<i class="icon-plus-square"></i> <button type="button" class="btn btn-text-secondary" [disabled]="isDisabled || isLast" (click)="emitMoveBottom()">
</button> <i class="icon-caret-bottom"></i>
<button type="button" class="btn btn-text-secondary" [class.hidden]="isHidden" (click)="emitToggle(true)" title="Close all items"> </button>
<i class="icon-minus-square"></i> <button type="button" class="btn btn-text-secondary" [class.hidden]="!isHidden" (click)="expand()" title="Expand this item">
</button> <i class="icon-plus-square"></i>
</span> </button>
<button type="button" class="btn btn-text-secondary" [class.hidden]="isHidden" (click)="collapse()" title="Collapse this item">
<span class="float-right"> <i class="icon-minus-square"></i>
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled" (click)="emitClone()"> </button>
<i class="icon-clone"></i> </div>
</button> <div class="col-auto">
<button type="button" class="btn btn-text-secondary" [disabled]="isDisabled" (click)="emitClone()">
<button type="button" class="btn btn-text-danger" [disabled]="isDisabled" (click)="emitRemove()"> <i class="icon-clone"></i>
<i class="icon-bin2"></i> </button>
</button>
</span> <button type="button" class="btn btn-text-danger" [disabled]="isDisabled" (click)="emitRemove()">
<i class="icon-bin2"></i>
</button>
</div>
</div>
</div> </div>
<div class="card-body" [class.hidden]="isHidden"> <div class="card-body" [class.hidden]="isHidden">

10
src/Squidex/app/features/content/shared/array-item.component.scss

@ -19,13 +19,17 @@
line-height: 2.2rem; line-height: 2.2rem;
} }
.header-text { .header-index {
display: inline-block; display: inline-block;
min-width: 70px; min-width: 3rem;
max-width: 100px; max-width: 3rem;
} }
} }
.col {
overflow: hidden;
}
.btn-text-secondary { .btn-text-secondary {
padding: .375rem; padding: .375rem;
} }

89
src/Squidex/app/features/content/shared/array-item.component.ts

@ -5,25 +5,31 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges } from '@angular/core';
import { AbstractControl, FormGroup } from '@angular/forms'; import { AbstractControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { import {
AppLanguageDto, AppLanguageDto,
EditContentForm, EditContentForm,
FieldDto, FieldDto,
FieldFormatter,
invalid$, invalid$,
RootFieldDto RootFieldDto
} from '@app/shared'; } from '@app/shared';
type FieldControl = { field: FieldDto, control: AbstractControl };
@Component({ @Component({
selector: 'sqx-array-item', selector: 'sqx-array-item',
styleUrls: ['./array-item.component.scss'], styleUrls: ['./array-item.component.scss'],
templateUrl: './array-item.component.html', templateUrl: './array-item.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ArrayItemComponent implements OnChanges { export class ArrayItemComponent implements OnChanges, OnDestroy {
private subscription: Subscription;
@Output() @Output()
public remove = new EventEmitter(); public remove = new EventEmitter();
@ -33,9 +39,6 @@ export class ArrayItemComponent implements OnChanges {
@Output() @Output()
public clone = new EventEmitter(); public clone = new EventEmitter();
@Output()
public toggle = new EventEmitter<boolean>();
@Input() @Input()
public form: EditContentForm; public form: EditContentForm;
@ -45,9 +48,6 @@ export class ArrayItemComponent implements OnChanges {
@Input() @Input()
public field: RootFieldDto; public field: RootFieldDto;
@Input()
public isHidden = false;
@Input() @Input()
public isFirst = false; public isFirst = false;
@ -69,22 +69,85 @@ export class ArrayItemComponent implements OnChanges {
@Input() @Input()
public languages: ReadonlyArray<AppLanguageDto>; public languages: ReadonlyArray<AppLanguageDto>;
public isHidden = false;
public isInvalid: Observable<boolean>; public isInvalid: Observable<boolean>;
public fieldControls: ReadonlyArray<{ field: FieldDto, control: AbstractControl }>; public title: string;
public fieldControls: ReadonlyArray<FieldControl> = [];
constructor(
private readonly changeDetector: ChangeDetectorRef
) {
}
public ngOnDestroy() {
this.unsubscribeFromForm();
}
private unsubscribeFromForm() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
public ngOnChanges(changes: SimpleChanges) { public ngOnChanges(changes: SimpleChanges) {
if (changes['itemForm']) { if (changes['itemForm']) {
this.isInvalid = invalid$(this.itemForm); this.isInvalid = invalid$(this.itemForm);
this.unsubscribeFromForm();
this.subscription =
this.itemForm.valueChanges.pipe(startWith(this.itemForm.value))
.subscribe(() => {
this.updateTitle();
});
} }
if (changes['itemForm'] || changes['field']) { if (changes['itemForm'] || changes['field']) {
this.fieldControls = this.field.nested.map(field => ({ field, control: this.itemForm.get(field.name)! })).filter(x => !x.field.properties.isContentField || !!x.control); this.updateFields();
this.updateTitle();
} }
} }
public emitToggle(value: boolean) { private updateFields() {
this.toggle.emit(value); const fields: FieldControl[] = [];
for (let field of this.field.nested) {
const control = this.itemForm.get(field.name)!;
if (control || this.field.properties.isContentField) {
fields.push({ field, control });
}
}
this.fieldControls = fields;
}
private updateTitle() {
const values: string[] = [];
for (let { control, field } of this.fieldControls) {
const formatted = FieldFormatter.format(field, control.value);
if (formatted) {
values.push(formatted);
}
}
this.title = values.join(', ');
}
public collapse() {
this.isHidden = true;
this.changeDetector.detectChanges();
}
public expand() {
this.isHidden = false;
this.changeDetector.detectChanges();
} }
public emitClone() { public emitClone() {

Loading…
Cancel
Save