mirror of https://github.com/Squidex/squidex.git
34 changed files with 455 additions and 242 deletions
@ -0,0 +1,16 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Schemas |
||||
|
{ |
||||
|
public enum TagsFieldEditor |
||||
|
{ |
||||
|
Tags, |
||||
|
Checkboxes, |
||||
|
Dropdown |
||||
|
} |
||||
|
} |
||||
@ -1,3 +1,58 @@ |
|||||
<div [formGroup]="editForm"> |
<div [formGroup]="editForm"> |
||||
<span>Nothing to setup</span> |
<div class="form-group row"> |
||||
<div> |
<label class="col-3 col-form-label" for="{{field.fieldId}}_editorUrl">Editor Url</label> |
||||
|
|
||||
|
<div class="col-6"> |
||||
|
<input type="text" class="form-control" id="{{field.fieldId}}_editorUrl" formControlName="editorUrl" /> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
Url to your plugin if you use a custom editor. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col-3 col-form-label" for="{{field.fieldId}}_fieldPlaceholder">Placeholder</label> |
||||
|
|
||||
|
<div class="col-6"> |
||||
|
<input type="text" class="form-control" id="{{field.fieldId}}_fieldPlaceholder" maxlength="100" formControlName="placeholder" /> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
Define the placeholder for the input control. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col-3 col-form-label">Editor</label> |
||||
|
|
||||
|
<div class="col-9"> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls['editor'].value === 'Tags'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="Tags" /> |
||||
|
|
||||
|
<i class="icon-control-Input"></i> |
||||
|
|
||||
|
<span class="radio-label">Tags</span> |
||||
|
</label> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls['editor'].value === 'Dropdown'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="Dropdown" /> |
||||
|
|
||||
|
<i class="icon-control-Dropdown"></i> |
||||
|
|
||||
|
<span class="radio-label" clas>Dropdown</span> |
||||
|
</label> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls['editor'].value === 'Checkboxes'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="Checkboxes" /> |
||||
|
|
||||
|
<i class="icon-control-Checkboxes"></i> |
||||
|
|
||||
|
<span class="radio-label">Checkboxes</span> |
||||
|
</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col-3 col-form-label">Allowed Values</label> |
||||
|
|
||||
|
<div class="col-6"> |
||||
|
<sqx-tag-editor formControlName="allowedValues"></sqx-tag-editor> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,9 @@ |
|||||
|
<span class="form-check" *ngFor="let value of values"> |
||||
|
<input type="checkbox" class="form-check-input" id="{{control}}{{value}}" |
||||
|
(blur)="blur()" |
||||
|
(change)="check($event.target.checked, value)" |
||||
|
[checked]="isChecked(value)" |
||||
|
[disabled]="isDisabled"> |
||||
|
|
||||
|
<label class="form-check-label" for="{{control}}{{value}}">{{value}}</label> |
||||
|
</span> |
||||
@ -0,0 +1,12 @@ |
|||||
|
@import '_mixins'; |
||||
|
@import '_vars'; |
||||
|
|
||||
|
.form-check { |
||||
|
display: inline-block; |
||||
|
margin-left: 0; |
||||
|
margin-right: 1rem; |
||||
|
} |
||||
|
|
||||
|
.form-check-input { |
||||
|
margin-top: .4rem; |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core'; |
||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
||||
|
|
||||
|
import { Types } from '@app/framework/internal'; |
||||
|
import { MathHelper } from '@app/shared'; |
||||
|
|
||||
|
export const SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR: any = { |
||||
|
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CheckboxGroupComponent), multi: true |
||||
|
}; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-checkbox-group', |
||||
|
styleUrls: ['./checkbox-group.component.scss'], |
||||
|
templateUrl: './checkbox-group.component.html', |
||||
|
providers: [SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush |
||||
|
}) |
||||
|
export class CheckboxGroupComponent implements ControlValueAccessor { |
||||
|
private callChange = (v: any) => { /* NOOP */ }; |
||||
|
private callTouched = () => { /* NOOP */ }; |
||||
|
private checkedValues: string[] = []; |
||||
|
|
||||
|
@Input() |
||||
|
public values: string[] = []; |
||||
|
|
||||
|
public isDisabled = false; |
||||
|
|
||||
|
public control = MathHelper.guid(); |
||||
|
|
||||
|
constructor( |
||||
|
private readonly changeDetector: ChangeDetectorRef |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public writeValue(obj: any) { |
||||
|
this.checkedValues = Types.isArrayOfString(obj) ? obj.filter(x => this.values.indexOf(x) >= 0) : []; |
||||
|
|
||||
|
this.changeDetector.markForCheck(); |
||||
|
} |
||||
|
|
||||
|
public setDisabledState(isDisabled: boolean): void { |
||||
|
this.isDisabled = isDisabled; |
||||
|
|
||||
|
this.changeDetector.markForCheck(); |
||||
|
} |
||||
|
|
||||
|
public registerOnChange(fn: any) { |
||||
|
this.callChange = fn; |
||||
|
} |
||||
|
|
||||
|
public registerOnTouched(fn: any) { |
||||
|
this.callTouched = fn; |
||||
|
} |
||||
|
|
||||
|
public blur() { |
||||
|
this.callTouched(); |
||||
|
} |
||||
|
|
||||
|
public check(isChecked: boolean, value: string) { |
||||
|
if (isChecked) { |
||||
|
this.checkedValues = [value, ...this.checkedValues]; |
||||
|
} else { |
||||
|
this.checkedValues = this.checkedValues.filter(x => x !== value); |
||||
|
} |
||||
|
|
||||
|
this.callChange(this.checkedValues); |
||||
|
} |
||||
|
|
||||
|
public isChecked(value: string) { |
||||
|
return this.checkedValues.indexOf(value) >= 0; |
||||
|
} |
||||
|
} |
||||
@ -1,26 +0,0 @@ |
|||||
<span> |
|
||||
<div class="selection"> |
|
||||
<input type="text" class="form-control" [disabled]="isDisabled" (click)="open()" readonly (keydown)="onKeyDown($event)" #input |
|
||||
autocomplete="off" |
|
||||
autocorrect="off" |
|
||||
autocapitalize="off"> |
|
||||
|
|
||||
<div class="control-dropdown-item" *ngIf="selectedItem"> |
|
||||
<ng-container *ngIf="!selectionTemplate">{{selectedItem}}</ng-container> |
|
||||
|
|
||||
<ng-template *ngIf="selectionTemplate" [sqxTemplateWrapper]="selectionTemplate" [item]="selectedItem"></ng-template> |
|
||||
</div> |
|
||||
|
|
||||
<i class="icon-caret-down"></i> |
|
||||
</div> |
|
||||
|
|
||||
<div class="items-container"> |
|
||||
<div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input" position="bottomLeft"> |
|
||||
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === selectedIndex" [container]="container"> |
|
||||
<ng-container *ngIf="!itemTemplate">{{item}}</ng-container> |
|
||||
|
|
||||
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</span> |
|
||||
@ -1,38 +0,0 @@ |
|||||
@import '_mixins'; |
|
||||
@import '_vars'; |
|
||||
|
|
||||
$color-input-disabled: #eef1f4; |
|
||||
|
|
||||
.form-control { |
|
||||
& { |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
&[readonly] { |
|
||||
background: $color-input-background; |
|
||||
} |
|
||||
|
|
||||
&:disabled { |
|
||||
background: $color-input-disabled; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.selection { |
|
||||
& { |
|
||||
position: relative; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.control-dropdown-item { |
|
||||
@include absolute(0, 1rem, 0, 0); |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
|
|
||||
.icon-caret-down { |
|
||||
@include absolute(30%, .4rem, auto, auto); |
|
||||
cursor: pointer; |
|
||||
font-size: .9rem; |
|
||||
font-weight: normal; |
|
||||
pointer-events: none; |
|
||||
} |
|
||||
} |
|
||||
@ -1,149 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, forwardRef, Input, QueryList, TemplateRef } from '@angular/core'; |
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|
||||
|
|
||||
const KEY_ENTER = 13; |
|
||||
const KEY_ESCAPE = 27; |
|
||||
const KEY_UP = 38; |
|
||||
const KEY_DOWN = 40; |
|
||||
|
|
||||
import { ModalModel } from '@app/framework/internal'; |
|
||||
|
|
||||
export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { |
|
||||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DropdownComponent), multi: true |
|
||||
}; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'sqx-dropdown', |
|
||||
styleUrls: ['./dropdown.component.scss'], |
|
||||
templateUrl: './dropdown.component.html', |
|
||||
providers: [SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR], |
|
||||
changeDetection: ChangeDetectionStrategy.OnPush |
|
||||
}) |
|
||||
export class DropdownComponent implements AfterContentInit, ControlValueAccessor { |
|
||||
private callChange = (v: any) => { /* NOOP */ }; |
|
||||
private callTouched = () => { /* NOOP */ }; |
|
||||
|
|
||||
@Input() |
|
||||
public items: any[] = []; |
|
||||
|
|
||||
@ContentChildren(TemplateRef) |
|
||||
public templates: QueryList<any>; |
|
||||
|
|
||||
public dropdown = new ModalModel(); |
|
||||
|
|
||||
public selectedItem: any; |
|
||||
public selectedIndex = -1; |
|
||||
public selectionTemplate: TemplateRef<any>; |
|
||||
|
|
||||
public itemTemplate: TemplateRef<any>; |
|
||||
|
|
||||
public isDisabled = false; |
|
||||
|
|
||||
constructor( |
|
||||
private readonly changeDetector: ChangeDetectorRef |
|
||||
) { |
|
||||
} |
|
||||
|
|
||||
public ngAfterContentInit() { |
|
||||
if (this.templates.length === 1) { |
|
||||
this.itemTemplate = this.selectionTemplate = this.templates.first; |
|
||||
} else { |
|
||||
this.templates.forEach(template => { |
|
||||
if (template.name === 'selection') { |
|
||||
this.selectionTemplate = template; |
|
||||
} else { |
|
||||
this.itemTemplate = template; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public writeValue(obj: any) { |
|
||||
this.selectIndex(this.items && obj ? this.items.indexOf(obj) : 0); |
|
||||
|
|
||||
this.changeDetector.markForCheck(); |
|
||||
} |
|
||||
|
|
||||
public setDisabledState(isDisabled: boolean): void { |
|
||||
this.isDisabled = isDisabled; |
|
||||
|
|
||||
this.changeDetector.markForCheck(); |
|
||||
} |
|
||||
|
|
||||
public registerOnChange(fn: any) { |
|
||||
this.callChange = fn; |
|
||||
} |
|
||||
|
|
||||
public registerOnTouched(fn: any) { |
|
||||
this.callTouched = fn; |
|
||||
} |
|
||||
|
|
||||
public onKeyDown(event: KeyboardEvent) { |
|
||||
switch (event.keyCode) { |
|
||||
case KEY_UP: |
|
||||
this.up(); |
|
||||
return false; |
|
||||
case KEY_DOWN: |
|
||||
this.down(); |
|
||||
return false; |
|
||||
case KEY_ESCAPE: |
|
||||
case KEY_ENTER: |
|
||||
if (this.dropdown.isOpen) { |
|
||||
this.close(); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
public open() { |
|
||||
this.dropdown.show(); |
|
||||
this.callTouched(); |
|
||||
} |
|
||||
|
|
||||
public selectIndexAndClose(selectedIndex: number) { |
|
||||
this.selectIndex(selectedIndex); |
|
||||
this.close(); |
|
||||
} |
|
||||
|
|
||||
private close() { |
|
||||
this.dropdown.hide(); |
|
||||
} |
|
||||
|
|
||||
private up() { |
|
||||
this.selectIndex(this.selectedIndex - 1); |
|
||||
} |
|
||||
|
|
||||
private down() { |
|
||||
this.selectIndex(this.selectedIndex + 1); |
|
||||
} |
|
||||
|
|
||||
private selectIndex(selectedIndex: number) { |
|
||||
if (selectedIndex < 0) { |
|
||||
selectedIndex = 0; |
|
||||
} |
|
||||
|
|
||||
const items = this.items || []; |
|
||||
|
|
||||
if (selectedIndex >= items.length) { |
|
||||
selectedIndex = items.length - 1; |
|
||||
} |
|
||||
|
|
||||
const value = items[selectedIndex]; |
|
||||
|
|
||||
if (value !== this.selectedItem) { |
|
||||
this.selectedIndex = selectedIndex; |
|
||||
this.selectedItem = value; |
|
||||
|
|
||||
this.callChange(value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 81 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue