mirror of https://github.com/Squidex/squidex.git
74 changed files with 747 additions and 378 deletions
@ -0,0 +1,54 @@ |
|||||
|
// ==========================================================================
|
||||
|
// InstantSerializer.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using MongoDB.Bson.Serialization; |
||||
|
using MongoDB.Bson.Serialization.Serializers; |
||||
|
using NodaTime; |
||||
|
using NodaTime.Text; |
||||
|
|
||||
|
// ReSharper disable InvertIf
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.MongoDb |
||||
|
{ |
||||
|
public sealed class InstantSerializer : SerializerBase<Instant> |
||||
|
{ |
||||
|
private static bool isRegistered; |
||||
|
private static readonly object LockObject = new object(); |
||||
|
|
||||
|
public static bool Register() |
||||
|
{ |
||||
|
if (!isRegistered) |
||||
|
{ |
||||
|
lock (LockObject) |
||||
|
{ |
||||
|
if (!isRegistered) |
||||
|
{ |
||||
|
BsonSerializer.RegisterSerializer(new InstantSerializer()); |
||||
|
|
||||
|
isRegistered = true; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Instant Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) |
||||
|
{ |
||||
|
var value = context.Reader.ReadDateTime(); |
||||
|
|
||||
|
return Instant.FromUnixTimeMilliseconds(value); |
||||
|
} |
||||
|
|
||||
|
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Instant value) |
||||
|
{ |
||||
|
context.Writer.WriteDateTime(value.ToUnixTimeMilliseconds()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
// ==========================================================================
|
||||
|
// InstantConverter.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Newtonsoft.Json; |
||||
|
using NodaTime; |
||||
|
using NodaTime.Text; |
||||
|
|
||||
|
// ReSharper disable ConvertIfStatementToSwitchStatement
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.Json |
||||
|
{ |
||||
|
public sealed class InstantConverter : JsonConverter |
||||
|
{ |
||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) |
||||
|
{ |
||||
|
if (value != null) |
||||
|
{ |
||||
|
writer.WriteValue(value.ToString()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
writer.WriteNull(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) |
||||
|
{ |
||||
|
if (reader.TokenType == JsonToken.String) |
||||
|
{ |
||||
|
return InstantPattern.General.Parse(reader.Value.ToString()).Value; |
||||
|
} |
||||
|
if (reader.TokenType == JsonToken.Date) |
||||
|
{ |
||||
|
return Instant.FromDateTimeUtc((DateTime)reader.Value); |
||||
|
} |
||||
|
if (reader.TokenType == JsonToken.Null && objectType == typeof(Instant?)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
throw new JsonException($"Not a valid date time, expected String or Date, but got {reader.TokenType}."); |
||||
|
} |
||||
|
|
||||
|
public override bool CanConvert(Type objectType) |
||||
|
{ |
||||
|
return objectType == typeof(Instant) || objectType == typeof(Instant?); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,16 +1,6 @@ |
|||||
@import '_vars'; |
@import '_vars'; |
||||
@import '_mixins'; |
@import '_mixins'; |
||||
|
|
||||
.minlength { |
|
||||
&-col { |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
&-label { |
|
||||
@include absolute(0, -.25rem, auto, auto); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.form-check-input { |
.form-check-input { |
||||
margin: 0; |
margin: 0; |
||||
} |
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-placeholder" class="col col-3 col-form-label">Placeholder</label> |
||||
|
|
||||
|
<div class="col col-6"> |
||||
|
<input type="text" class="form-control" id="field-placeholder" maxlength="100" formControlName="placeholder" /> |
||||
|
|
||||
|
<span class="form-hint"> |
||||
|
Define the placeholder for the input control. |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-editor" class="col col-3 col-form-label">Editor</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls.editor.value === 'Date'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="Date" /> |
||||
|
|
||||
|
<i class="icon-control-date"></i> |
||||
|
|
||||
|
<span class="radio-label">Date</span> |
||||
|
</label> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls.editor.value === 'DateTime'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="DateTime" /> |
||||
|
|
||||
|
<i class="icon-control-datetime"></i> |
||||
|
|
||||
|
<span class="radio-label" clas>DateTime</span> |
||||
|
</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,2 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
@ -0,0 +1,40 @@ |
|||||
|
/* |
||||
|
* 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 { Observable } from 'rxjs'; |
||||
|
|
||||
|
import { FloatConverter, NumberFieldPropertiesDto } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-date-time-ui', |
||||
|
styleUrls: ['date-time-ui.component.scss'], |
||||
|
templateUrl: 'date-time-ui.component.html' |
||||
|
}) |
||||
|
export class DateTimeUIComponent implements OnInit { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: NumberFieldPropertiesDto; |
||||
|
|
||||
|
public converter = new FloatConverter(); |
||||
|
|
||||
|
public hideAllowedValues: Observable<boolean>; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.editForm.addControl('editor', |
||||
|
new FormControl(this.properties.editor, [ |
||||
|
Validators.required |
||||
|
])); |
||||
|
this.editForm.addControl('placeholder', |
||||
|
new FormControl(this.properties.placeholder, [ |
||||
|
Validators.maxLength(100) |
||||
|
])); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-checkbox-label" for="field-required">Required</label> |
||||
|
|
||||
|
<div class="col col-6"> |
||||
|
<input type="checkbox" class="form-check-input" id="field-required" formControlName="isRequired" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label">Min Value</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-date-time-editor enforceTime="true" mode="DateTime" formControlName="minValue"></sqx-date-time-editor> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label">Max Value</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-date-time-editor enforceTime="true" mode="DateTime" formControlName="maxValue"></sqx-date-time-editor> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row" [class.hidden]="(hideDefaultValue | async)"> |
||||
|
<label class="col col-3 col-form-label" for="field-default-value">Default Value</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-date-time-editor enforceTime="true" formControlName="defaultValue"></sqx-date-time-editor> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,6 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
.form-check-input { |
||||
|
margin: 0; |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
/* |
||||
|
* 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 { NumberFieldPropertiesDto, ValidatorsEx } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-date-time-validation', |
||||
|
styleUrls: ['date-time-validation.component.scss'], |
||||
|
templateUrl: 'date-time-validation.component.html' |
||||
|
}) |
||||
|
export class DateTimeValidationComponent implements OnInit { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: NumberFieldPropertiesDto; |
||||
|
|
||||
|
public hideDefaultValue: Observable<boolean>; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.editForm.addControl('maxValue', |
||||
|
new FormControl(this.properties.maxValue, [ |
||||
|
ValidatorsEx.validDateTime() |
||||
|
])); |
||||
|
|
||||
|
this.editForm.addControl('minValue', |
||||
|
new FormControl(this.properties.minValue, [ |
||||
|
ValidatorsEx.validDateTime() |
||||
|
])); |
||||
|
|
||||
|
this.editForm.addControl('defaultValue', |
||||
|
new FormControl(this.properties.defaultValue, [ |
||||
|
ValidatorsEx.validDateTime() |
||||
|
])); |
||||
|
|
||||
|
this.hideDefaultValue = |
||||
|
Observable.of(this.properties.isRequired) |
||||
|
.merge(this.editForm.get('isRequired').valueChanges) |
||||
|
.map(x => !!x); |
||||
|
} |
||||
|
} |
||||
@ -1,15 +1,10 @@ |
|||||
<div> |
<div> |
||||
<div class="form-inline"> |
<div class="form-inline"> |
||||
<div class="form-group date-group"> |
<div class="form-group date-group"> |
||||
<input type="text" class="form-control" #dateInput [disabled]="isDisabled" readonly /> |
<input type="text" class="form-control" [formControl]="dateControl" (blur)="touched()" #dateInput /> |
||||
</div> |
</div> |
||||
<div class="form-group time-group" *ngIf="showTime"> |
<div class="form-group time-group" *ngIf="showTime"> |
||||
<input type="text" class="form-control" [formControl]="timeControl" (blur)="touched()" /> |
<input type="text" class="form-control" [formControl]="timeControl" (blur)="touched()" /> |
||||
</div> |
</div> |
||||
<div class="form-group timezone-group" *ngIf="showTimezone"> |
|
||||
<select class="form-control" [formControl]="timeZoneControl"> |
|
||||
<option *ngFor="let timezone of timezones" [ngValue]="timezone.value">{{timezone.label}}</option> |
|
||||
</select> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue