diff --git a/src/Squidex/app/framework/angular/autocomplete.component.ts b/src/Squidex/app/framework/angular/autocomplete.component.ts
index 443711063..e93345090 100644
--- a/src/Squidex/app/framework/angular/autocomplete.component.ts
+++ b/src/Squidex/app/framework/angular/autocomplete.component.ts
@@ -26,8 +26,7 @@ export class AutocompleteItem {
const KEY_ENTER = 13;
const KEY_UP = 38;
const KEY_DOWN = 40;
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AutocompleteComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/date-time-editor.component.ts b/src/Squidex/app/framework/angular/date-time-editor.component.ts
index 8f863d1cc..8a104e206 100644
--- a/src/Squidex/app/framework/angular/date-time-editor.component.ts
+++ b/src/Squidex/app/framework/angular/date-time-editor.component.ts
@@ -11,8 +11,7 @@ import * as moment from 'moment';
let Pikaday = require('pikaday/pikaday');
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateTimeEditorComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/indeterminate.directive.ts b/src/Squidex/app/framework/angular/indeterminate.directive.ts
new file mode 100644
index 000000000..231756c9e
--- /dev/null
+++ b/src/Squidex/app/framework/angular/indeterminate.directive.ts
@@ -0,0 +1,22 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Sebastian Stehle. All rights reserved
+ */
+
+import { Directive, ElementRef, OnInit } from '@angular/core';
+
+@Directive({
+ selector: '[indeterminate]'
+})
+export class IndeterminateDirective implements OnInit {
+ constructor(
+ private readonly element: ElementRef
+ ) {
+ }
+
+ public ngOnInit() {
+ this.element.nativeElement.indeterminate = true;
+ }
+}
\ No newline at end of file
diff --git a/src/Squidex/app/framework/angular/json-editor.component.ts b/src/Squidex/app/framework/angular/json-editor.component.ts
index 739293039..69f2de38f 100644
--- a/src/Squidex/app/framework/angular/json-editor.component.ts
+++ b/src/Squidex/app/framework/angular/json-editor.component.ts
@@ -13,8 +13,7 @@ import { ResourceLoaderService } from './../services/resource-loader.service';
declare var ace: any;
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => JsonEditorComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/markdown-editor.component.ts b/src/Squidex/app/framework/angular/markdown-editor.component.ts
index addf4be06..a5f9de910 100644
--- a/src/Squidex/app/framework/angular/markdown-editor.component.ts
+++ b/src/Squidex/app/framework/angular/markdown-editor.component.ts
@@ -12,8 +12,7 @@ import { ResourceLoaderService } from './../services/resource-loader.service';
declare var SimpleMDE: any;
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/rich-editor.component.ts b/src/Squidex/app/framework/angular/rich-editor.component.ts
index 62f21fe73..a0f1da652 100644
--- a/src/Squidex/app/framework/angular/rich-editor.component.ts
+++ b/src/Squidex/app/framework/angular/rich-editor.component.ts
@@ -12,8 +12,7 @@ import { ResourceLoaderService } from './../services/resource-loader.service';
declare var tinymce: any;
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/slider.component.ts b/src/Squidex/app/framework/angular/slider.component.ts
index d4c0bbbcb..a893e0e84 100644
--- a/src/Squidex/app/framework/angular/slider.component.ts
+++ b/src/Squidex/app/framework/angular/slider.component.ts
@@ -8,8 +8,7 @@
import { Component, ElementRef, forwardRef, Input, Renderer, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export const SQX_SLIDER_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SliderComponent), multi: true
diff --git a/src/Squidex/app/framework/angular/tag-editor.component.ts b/src/Squidex/app/framework/angular/tag-editor.component.ts
index 7f4310cb8..65cb07aa2 100644
--- a/src/Squidex/app/framework/angular/tag-editor.component.ts
+++ b/src/Squidex/app/framework/angular/tag-editor.component.ts
@@ -9,8 +9,7 @@ import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
const KEY_ENTER = 13;
-/* tslint:disable:no-empty */
-const NOOP = () => { };
+const NOOP = () => { /* NOOP */ };
export interface Converter {
convert(input: string): any;
diff --git a/src/Squidex/app/framework/angular/toggle.component.html b/src/Squidex/app/framework/angular/toggle.component.html
new file mode 100644
index 000000000..1e98594bc
--- /dev/null
+++ b/src/Squidex/app/framework/angular/toggle.component.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/src/Squidex/app/framework/angular/toggle.component.scss b/src/Squidex/app/framework/angular/toggle.component.scss
new file mode 100644
index 000000000..0520d0784
--- /dev/null
+++ b/src/Squidex/app/framework/angular/toggle.component.scss
@@ -0,0 +1,60 @@
+@import '_mixins';
+@import '_vars';
+
+$toggle-width: 3.2rem;
+$toggle-height: 2rem;
+$toggle-button-size: $toggle-height - .3rem;
+
+.toggle {
+ &-button {
+ @include circle($toggle-button-size);
+ @include box-shadow(0, 2px, 2px, .2);
+ @include absolute($toggle-height * .5, auto, auto, $toggle-width * .5);
+ @include transition(left .3s ease);
+ background: $color-accent-dark;
+ border: 0;
+ margin-left: -$toggle-button-size * .5;
+ margin-top: -$toggle-button-size * .5;
+ }
+
+ &-container {
+ & {
+ @include border-radius($toggle-height * .5);
+ @include box-shadow-inner;
+ @include transition(background-color .3s ease);
+ position: relative;
+ background: lighten($color-border, 6%);
+ border: 0;
+ height: $toggle-height;
+ width: $toggle-width;
+ }
+
+ &.checked {
+ & {
+ background: $color-theme-green-dark;
+ }
+
+ .toggle-button {
+ left: $toggle-height * .5;
+ }
+ }
+
+ &.unchecked {
+ & {
+ background: $color-theme-error;
+ }
+
+ .toggle-button {
+ left: $toggle-width - $toggle-height * .5;
+ }
+ }
+
+ &.disabled {
+ & {
+ background: $color-disabled;
+ border: 0;
+ cursor: not-allowed;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Squidex/app/framework/angular/toggle.component.ts b/src/Squidex/app/framework/angular/toggle.component.ts
new file mode 100644
index 000000000..8073ecf9d
--- /dev/null
+++ b/src/Squidex/app/framework/angular/toggle.component.ts
@@ -0,0 +1,55 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Sebastian Stehle. All rights reserved
+ */
+
+import { Component, forwardRef } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+const NOOP = () => { /* NOOP */ };
+
+export const SQX_TOGGLE_CONTROL_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ToggleComponent), multi: true
+};
+
+@Component({
+ selector: 'sqx-toggle',
+ styleUrls: ['./toggle.component.scss'],
+ templateUrl: './toggle.component.html',
+ providers: [SQX_TOGGLE_CONTROL_VALUE_ACCESSOR]
+})
+export class ToggleComponent implements ControlValueAccessor {
+ private changeCallback: (value: any) => void = NOOP;
+ private touchedCallback: () => void = NOOP;
+
+ public isChecked: boolean | undefined = undefined;
+ public isDisabled = false;
+
+ public writeValue(value: any) {
+ this.isChecked = value;
+ }
+
+ public setDisabledState(isDisabled: boolean): void {
+ this.isDisabled = isDisabled;
+ }
+
+ public registerOnChange(fn: any) {
+ this.changeCallback = fn;
+ }
+
+ public registerOnTouched(fn: any) {
+ this.touchedCallback = fn;
+ }
+
+ public changeState() {
+ if (this.isDisabled) {
+ return;
+ }
+ this.isChecked = !(this.isChecked === true);
+
+ this.changeCallback(this.isChecked);
+ this.touchedCallback();
+ }
+}
\ No newline at end of file
diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts
index d8137fa51..9174436c7 100644
--- a/src/Squidex/app/framework/declarations.ts
+++ b/src/Squidex/app/framework/declarations.ts
@@ -16,6 +16,7 @@ export * from './angular/date-time.pipes';
export * from './angular/focus-on-change.directive';
export * from './angular/focus-on-init.directive';
export * from './angular/http-utils';
+export * from './angular/indeterminate.directive';
export * from './angular/json-editor.component';
export * from './angular/markdown-editor.component';
export * from './angular/modal-view.directive';
@@ -29,6 +30,7 @@ export * from './angular/shortcut.component';
export * from './angular/slider.component';
export * from './angular/tag-editor.component';
export * from './angular/title.component';
+export * from './angular/toggle.component';
export * from './angular/user-report.component';
export * from './configurations';
diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts
index 33a5101bc..7255f99aa 100644
--- a/src/Squidex/app/framework/module.ts
+++ b/src/Squidex/app/framework/module.ts
@@ -47,6 +47,7 @@ import {
TagEditorComponent,
TitleService,
TitleComponent,
+ ToggleComponent,
UserReportComponent
} from './declarations';
@@ -86,6 +87,7 @@ import {
SliderComponent,
TagEditorComponent,
TitleComponent,
+ ToggleComponent,
UserReportComponent
],
exports: [
@@ -116,6 +118,7 @@ import {
SliderComponent,
TagEditorComponent,
TitleComponent,
+ ToggleComponent,
UserReportComponent,
HttpModule,
FormsModule,
diff --git a/src/Squidex/app/theme/_forms.scss b/src/Squidex/app/theme/_forms.scss
index 027edbeeb..a946b35b7 100644
--- a/src/Squidex/app/theme/_forms.scss
+++ b/src/Squidex/app/theme/_forms.scss
@@ -79,6 +79,10 @@ select {
&:last-child {
margin-bottom: 0;
}
+
+ &~.hidden {
+ margin-bottom: 0;
+ }
}
.search-form {