mirror of https://github.com/Squidex/squidex.git
30 changed files with 639 additions and 85 deletions
@ -0,0 +1,106 @@ |
|||||
|
<div class="table-items-row"> |
||||
|
<div class="field-summary"> |
||||
|
<div class="row"> |
||||
|
<div class="col-xs-8"> |
||||
|
{{field.name}} |
||||
|
</div> |
||||
|
<div class="col-xs-4"> |
||||
|
<div class="float-xs-right"> |
||||
|
<button type="button" class="btn btn-secondary field-edit-button" [class.active]="isEditing" (click)="toggleEditing()"> |
||||
|
<i class="icon-settings"></i> |
||||
|
</button> |
||||
|
<button type="button" class="btn btn-simple"> |
||||
|
<i class="icon-dots"></i> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="field-details" *ngIf="isEditing"> |
||||
|
<form [formGroup]="editForm" (submit)="save()"> |
||||
|
<div class="field-details-tabs clearfix"> |
||||
|
<ul class="nav nav-inline nav-field-tabs"> |
||||
|
<li class="nav-item"> |
||||
|
<a class="nav-link" (click)="selectTab(0)" [class.active]="selectedTab === 0">Common</a> |
||||
|
</li> |
||||
|
<li class="nav-item"> |
||||
|
<a class="nav-link" (click)="selectTab(1)" [class.active]="selectedTab === 1">Validation</a> |
||||
|
</li> |
||||
|
<li class="nav-item"> |
||||
|
<a class="nav-link" (click)="selectTab(2)" [class.active]="selectedTab === 2">User Interface</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
|
||||
|
<div class="float-xs-right"> |
||||
|
<button type="reset" class="btn btn-link" (click)="cancel()">Cancel</button> |
||||
|
<button type="submit" class="btn btn-primary">Save</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="field-details-tab" *ngIf="selectedTab == 0"> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-label" class="col-xs-3 col-form-label">Label</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<div class="errors-box" *ngIf="editForm.get('label').invalid && editForm.get('label').touched" [@fade]> |
||||
|
<div class="errors"> |
||||
|
<span *ngIf="editForm.get('label').hasError('maxlength')"> |
||||
|
Label can not have more than 100 characters. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<input type="text" class="form-control" id="field-label" maxlength="100" formControlName="label" /> |
||||
|
|
||||
|
<span class="form-hint"> |
||||
|
Define the display name for the field for documentation and user interfaces. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label for="field-hints" class="col-xs-3 col-form-label">Hints</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<div class="errors-box" *ngIf="editForm.get('hints').invalid && editForm.get('hints').touched" [@fade]> |
||||
|
<div class="errors"> |
||||
|
<span *ngIf="editForm.get('hints').hasError('maxlength')"> |
||||
|
Hints can not have more than 100 characters. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<input type="text" class="form-control" id="field-hints" maxlength="100" formControlName="hints" /> |
||||
|
|
||||
|
<span class="form-hint"> |
||||
|
Define some hints for the user and editor for the field for documentation and user interfaces. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="field-details-tab" *ngIf="selectedTab == 1"> |
||||
|
<div [ngSwitch]="field.properties.fieldType"> |
||||
|
<div *ngSwitchCase="'number'"> |
||||
|
asdsad |
||||
|
</div> |
||||
|
<div *ngSwitchCase="'string'"> |
||||
|
<sqx-string-validation [editForm]="editForm" [properties]="field.properties"></sqx-string-validation> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="field-details-tab" *ngIf="selectedTab == 2"> |
||||
|
<div [ngSwitch]="field.properties.fieldType"> |
||||
|
<div *ngSwitchCase="'number'"> |
||||
|
asdsad |
||||
|
</div> |
||||
|
<div *ngSwitchCase="'string'"> |
||||
|
<sqx-string-ui [editForm]="editForm" [properties]="field.properties"></sqx-string-ui> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,78 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
$field-header: #e7ebef; |
||||
|
|
||||
|
.table-items-row { |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
.field { |
||||
|
&-summary { |
||||
|
padding: 15px 20px; |
||||
|
line-height: 40px; |
||||
|
} |
||||
|
|
||||
|
&-edit-button { |
||||
|
& { |
||||
|
color: $color-theme-blue; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
color: $color-theme-blue-dark; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background: $color-theme-blue; |
||||
|
border: 0; |
||||
|
color: $color-accent-dark; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&-details { |
||||
|
& { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
&::before { |
||||
|
@include caret-top; |
||||
|
@include absolute(-10px, 90px, auto, auto); |
||||
|
border-color: transparent transparent $color-border; |
||||
|
border-width: 10px; |
||||
|
} |
||||
|
|
||||
|
&-tab { |
||||
|
padding: 15px 20px 20px; |
||||
|
} |
||||
|
|
||||
|
&-tabs { |
||||
|
background: $color-border; |
||||
|
position: relative; |
||||
|
padding: 15px 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.nav-field-tabs { |
||||
|
& { |
||||
|
@include absolute(auto, auto, 0, 20px); |
||||
|
} |
||||
|
|
||||
|
& .nav-link { |
||||
|
& { |
||||
|
color: $color-text; |
||||
|
cursor: pointer; |
||||
|
padding: 18px 10px; |
||||
|
border-bottom: 4px solid transparent; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
&.active, |
||||
|
&:hover { |
||||
|
border-color: $color-theme-blue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { Observable} from 'rxjs'; |
||||
|
import { |
||||
|
createProperties, |
||||
|
fadeAnimation, |
||||
|
FieldDto, |
||||
|
FieldPropertiesDto |
||||
|
} from 'shared'; |
||||
|
|
||||
|
const ESCAPE_KEY = 27; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-field', |
||||
|
styleUrls: ['./field.component.scss'], |
||||
|
templateUrl: './field.component.html', |
||||
|
animations: [ |
||||
|
fadeAnimation |
||||
|
] |
||||
|
}) |
||||
|
export class FieldComponent implements OnInit { |
||||
|
private oldValue: any; |
||||
|
|
||||
|
@Input() |
||||
|
public field: FieldDto; |
||||
|
|
||||
|
@Output() |
||||
|
public saved = new EventEmitter<FieldDto>(); |
||||
|
|
||||
|
public isEditing: boolean = false; |
||||
|
public selectedTab = 0; |
||||
|
|
||||
|
public editForm: FormGroup = |
||||
|
this.formBuilder.group({ |
||||
|
label: ['', |
||||
|
[ |
||||
|
Validators.maxLength(100) |
||||
|
]], |
||||
|
hints: ['', |
||||
|
[ |
||||
|
Validators.maxLength(100) |
||||
|
]], |
||||
|
isRequired: [false] |
||||
|
}); |
||||
|
|
||||
|
constructor( |
||||
|
private readonly formBuilder: FormBuilder |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.resetForm(this.field.properties); |
||||
|
} |
||||
|
|
||||
|
public save() { |
||||
|
this.editForm.markAsTouched(); |
||||
|
|
||||
|
if (this.editForm.valid) { |
||||
|
const properties = createProperties(this.field.properties['fieldType'], this.editForm.value); |
||||
|
|
||||
|
const field = |
||||
|
new FieldDto( |
||||
|
this.field.name, |
||||
|
this.field.isHidden, |
||||
|
this.field.isHidden, |
||||
|
properties); |
||||
|
|
||||
|
this.saved.emit(field); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public cancel() { |
||||
|
this.resetForm(this.oldValue); |
||||
|
} |
||||
|
|
||||
|
public toggleEditing() { |
||||
|
this.isEditing = true; |
||||
|
} |
||||
|
|
||||
|
public selectTab(tab: number) { |
||||
|
this.selectedTab = tab; |
||||
|
} |
||||
|
|
||||
|
private resetForm(properties: any) { |
||||
|
this.editForm.reset(); |
||||
|
|
||||
|
for (let property in properties) { |
||||
|
if (properties.hasOwnProperty(property)) { |
||||
|
const controlName = property + ''; |
||||
|
|
||||
|
if (this.editForm.contains(controlName)) { |
||||
|
this.editForm.get(controlName).setValue(properties[property]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.oldValue = Object.assign({}, this.editForm.value); |
||||
|
|
||||
|
this.isEditing = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,21 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-input-placeholder" class="col-xs-3 col-form-label">Placeholder</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<div class="errors-box" *ngIf="editForm.get('placeholder').invalid && editForm.get('placeholder').touched" [@fade]> |
||||
|
<div class="errors"> |
||||
|
<span *ngIf="editForm.get('placeholder').hasError('maxlength')"> |
||||
|
Placeholder can not have more than 100 characters. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<input type="text" class="form-control" id="field-input-placeholder" maxlength="100" formControlName="placeholder" /> |
||||
|
|
||||
|
<span class="form-hint"> |
||||
|
Define the placeholder for the input control. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,2 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
@ -0,0 +1,34 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, Input, OnInit } from '@angular/core'; |
||||
|
import { FormControl, FormGroup, Validators } from '@angular/forms'; |
||||
|
|
||||
|
import { fadeAnimation, StringFieldPropertiesDto } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-string-ui', |
||||
|
styleUrls: ['string-ui.component.scss'], |
||||
|
templateUrl: 'string-ui.component.html', |
||||
|
animations: [ |
||||
|
fadeAnimation |
||||
|
] |
||||
|
}) |
||||
|
export class StringUIComponent implements OnInit { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: StringFieldPropertiesDto; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.editForm.addControl('placeholder', |
||||
|
new FormControl('', [ |
||||
|
Validators.maxLength(100) |
||||
|
])); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-required" class="col-xs-3">Required</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<input type="checkbox" class="form-check-input" id="field-required" formControlName="isRequired" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col-xs-3 col-form-label">Length</label> |
||||
|
|
||||
|
<div class="col-xs-3 minlength-col"> |
||||
|
<input type="number" class="form-control" id="field-min-length" formControlName="minLength" placeholder="Min Length" /> |
||||
|
|
||||
|
<label class="col-form-label minlength-label">-</label> |
||||
|
</div> |
||||
|
<div class="col-xs-3"> |
||||
|
<input type="number" class="form-control" id="field-max-length" formControlName="maxLength" placeholder="Max Length" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col-xs-3 col-form-label" for="field-pattern">Pattern</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<input type="text" class="form-control" id="field-pattern" formControlName="pattern" placeholder="Regex Pattern" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group row" [class.hide]="hidePatternMessage | async"> |
||||
|
<label class="col-xs-3 col-form-label" for="field-pattern-message">Pattern Message</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<input type="text" class="form-control" id="field-pattern-message" formControlName="patternMessage" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row" [class.hide]="(hideDefaultValue | async)"> |
||||
|
<label class="col-xs-3 col-form-label" for="field-default-value">Default Value</label> |
||||
|
|
||||
|
<div class="col-xs-6"> |
||||
|
<input type="text" class="form-control" id="field-default-value" formControlName="defaultValue" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,16 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
.minlength { |
||||
|
&-col { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
&-label { |
||||
|
@include absolute(0, -4px, auto, auto); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.form-check-input { |
||||
|
margin: 0; |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, Input, OnInit } from '@angular/core'; |
||||
|
import { FormControl, FormGroup } from '@angular/forms'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
import { StringFieldPropertiesDto } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-string-validation', |
||||
|
styleUrls: ['string-validation.component.scss'], |
||||
|
templateUrl: 'string-validation.component.html' |
||||
|
}) |
||||
|
export class StringValidationComponent implements OnInit { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: StringFieldPropertiesDto; |
||||
|
|
||||
|
public hidePatternMessage: Observable<boolean>; |
||||
|
public hideDefaultValue: Observable<boolean>; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.editForm.addControl('maxLength', |
||||
|
new FormControl()); |
||||
|
this.editForm.addControl('minLength', |
||||
|
new FormControl()); |
||||
|
this.editForm.addControl('pattern', |
||||
|
new FormControl()); |
||||
|
this.editForm.addControl('patternMessage', |
||||
|
new FormControl()); |
||||
|
this.editForm.addControl('defaultValue', |
||||
|
new FormControl()); |
||||
|
|
||||
|
this.hideDefaultValue = |
||||
|
Observable.of(false) |
||||
|
.merge(this.editForm.get('isRequired').valueChanges) |
||||
|
.map(x => !!x); |
||||
|
|
||||
|
this.hidePatternMessage = |
||||
|
Observable.of(false) |
||||
|
.merge(this.editForm.get('pattern').valueChanges) |
||||
|
.map(x => !x || x.trim().length === 0); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 14 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue