\ No newline at end of file
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.scss
new file mode 100644
index 000000000..fbb752506
--- /dev/null
+++ b/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.scss
@@ -0,0 +1,2 @@
+@import '_vars';
+@import '_mixins';
\ No newline at end of file
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.ts
new file mode 100644
index 000000000..ee57702d2
--- /dev/null
+++ b/src/Squidex/app/features/schemas/pages/schema/types/tags-ui.component.ts
@@ -0,0 +1,25 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Sebastian Stehle. All rights reserved
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+
+import { AssetsFieldPropertiesDto } from 'shared';
+
+@Component({
+ selector: 'sqx-tags-ui',
+ styleUrls: ['tags-ui.component.scss'],
+ templateUrl: 'tags-ui.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TagsUIComponent {
+ @Input()
+ public editForm: FormGroup;
+
+ @Input()
+ public properties: AssetsFieldPropertiesDto;
+}
\ No newline at end of file
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.html b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.html
new file mode 100644
index 000000000..35be4c232
--- /dev/null
+++ b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.html
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.scss
new file mode 100644
index 000000000..2edff0687
--- /dev/null
+++ b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.scss
@@ -0,0 +1,20 @@
+@import '_vars';
+@import '_mixins';
+
+.minitems {
+ &-col {
+ position: relative;
+ }
+
+ &-label {
+ @include absolute(0, -.2rem, auto, auto);
+ }
+}
+
+.form-check-input {
+ margin: 0;
+}
+
+.form-group {
+ margin-top: .5rem;
+}
\ No newline at end of file
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.ts
new file mode 100644
index 000000000..f5a6d6d96
--- /dev/null
+++ b/src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.ts
@@ -0,0 +1,33 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Sebastian Stehle. All rights reserved
+ */
+
+import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+
+import { AssetsFieldPropertiesDto } from 'shared';
+
+@Component({
+ selector: 'sqx-tags-validation',
+ styleUrls: ['tags-validation.component.scss'],
+ templateUrl: 'tags-validation.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TagsValidationComponent implements OnInit {
+ @Input()
+ public editForm: FormGroup;
+
+ @Input()
+ public properties: AssetsFieldPropertiesDto;
+
+ public ngOnInit() {
+ this.editForm.setControl('maxItems',
+ new FormControl(this.properties.maxItems));
+
+ this.editForm.setControl('minItems',
+ new FormControl(this.properties.minItems));
+ }
+}
\ No newline at end of file
diff --git a/src/Squidex/app/shared/services/schemas.fields.spec.ts b/src/Squidex/app/shared/services/schemas.fields.spec.ts
index 45965263b..0ac71ed4e 100644
--- a/src/Squidex/app/shared/services/schemas.fields.spec.ts
+++ b/src/Squidex/app/shared/services/schemas.fields.spec.ts
@@ -16,7 +16,8 @@ import {
JsonFieldPropertiesDto,
NumberFieldPropertiesDto,
ReferencesFieldPropertiesDto,
- StringFieldPropertiesDto
+ StringFieldPropertiesDto,
+ TagsFieldPropertiesDto
} from './../';
describe('FieldDto', () => {
@@ -85,6 +86,26 @@ describe('AssetsField', () => {
});
});
+describe('TagsField', () => {
+ const field = createField(new TagsFieldPropertiesDto(null, null, null, true, false, 1, 1));
+
+ it('should create validators', () => {
+ expect(field.createValidators(false).length).toBe(3);
+ });
+
+ it('should format to empty string if null', () => {
+ expect(field.formatValue(null)).toBe('');
+ });
+
+ it('should format to asset count', () => {
+ expect(field.formatValue(['hello', 'squidex', 'cms'])).toBe('hello, squidex, cms');
+ });
+
+ it('should return zero formatting if other type', () => {
+ expect(field.formatValue(1)).toBe('');
+ });
+});
+
describe('BooleanField', () => {
const field = createField(new BooleanFieldPropertiesDto(null, null, null, true, false, 'Checkbox'));
diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts
index 3c549a02a..2f8ec096e 100644
--- a/src/Squidex/app/shared/services/schemas.service.spec.ts
+++ b/src/Squidex/app/shared/services/schemas.service.spec.ts
@@ -386,6 +386,17 @@ describe('SchemasService', () => {
properties: {
fieldType: 'References'
}
+ },
+ {
+ fieldId: 9,
+ name: 'field9',
+ isLocked: true,
+ isHidden: true,
+ isDisabled: true,
+ partitioning: 'language',
+ properties: {
+ fieldType: 'Tags'
+ }
}
],
scriptQuery: '
',
@@ -412,7 +423,8 @@ describe('SchemasService', () => {
new FieldDto(5, 'field5', true, true, true, 'language', createProperties('Json')),
new FieldDto(6, 'field6', true, true, true, 'language', createProperties('Geolocation')),
new FieldDto(7, 'field7', true, true, true, 'language', createProperties('Assets')),
- new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References'))
+ new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References')),
+ new FieldDto(9, 'field9', true, true, true, 'language', createProperties('Tags'))
],
'',
'',
diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts
index 0efde00d8..22c7da6b1 100644
--- a/src/Squidex/app/shared/services/schemas.service.ts
+++ b/src/Squidex/app/shared/services/schemas.service.ts
@@ -31,7 +31,8 @@ export const fieldTypes: string[] = [
'Json',
'Number',
'References',
- 'String'
+ 'String',
+ 'Tags'
];
export function createProperties(fieldType: string, values: Object | null = null): FieldPropertiesDto {
@@ -62,6 +63,9 @@ export function createProperties(fieldType: string, values: Object | null = null
case 'Assets':
properties = new AssetsFieldPropertiesDto(null, null, null, false, false);
break;
+ case 'Tags':
+ properties = new TagsFieldPropertiesDto(null, null, null, false, false);
+ break;
default:
throw 'Invalid properties type';
}
@@ -607,6 +611,47 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
}
}
+export class TagsFieldPropertiesDto extends FieldPropertiesDto {
+ constructor(label: string | null, hints: string | null, placeholder: string | null,
+ isRequired: boolean,
+ isListField: boolean,
+ public readonly minItems?: number,
+ public readonly maxItems?: number
+ ) {
+ super('Tags', label, hints, placeholder, isRequired, isListField);
+ }
+
+ public formatValue(value: any): string {
+ if (!value) {
+ return '';
+ }
+
+ if (value.length) {
+ return value.join(', ');
+ } else {
+ return '';
+ }
+ }
+
+ public createValidators(isOptional: boolean): ValidatorFn[] {
+ const validators: ValidatorFn[] = [];
+
+ if (this.isRequired && !isOptional) {
+ validators.push(Validators.required);
+ }
+
+ if (this.minItems) {
+ validators.push(Validators.minLength(this.minItems));
+ }
+
+ if (this.maxItems) {
+ validators.push(Validators.maxLength(this.maxItems));
+ }
+
+ return validators;
+ }
+}
+
export class JsonFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null,
isRequired: boolean,