mirror of https://github.com/Squidex/squidex.git
43 changed files with 526 additions and 275 deletions
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// TypeNameNotFoundException.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure |
|||
{ |
|||
public class TypeNameNotFoundException : Exception |
|||
{ |
|||
public TypeNameNotFoundException() |
|||
{ |
|||
} |
|||
|
|||
public TypeNameNotFoundException(string message) |
|||
: base(message) |
|||
{ |
|||
} |
|||
|
|||
public TypeNameNotFoundException(string message, Exception inner) |
|||
: base(message, inner) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// ==========================================================================
|
|||
// SetMasterLanguage.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Write.Apps.Commands |
|||
{ |
|||
public sealed class SetMasterLanguage : AppAggregateCommand, IValidatable |
|||
{ |
|||
public Language Language { get; set; } |
|||
|
|||
public void Validate(IList<ValidationError> errors) |
|||
{ |
|||
if (Language == null) |
|||
{ |
|||
errors.Add(new ValidationError("Language cannot be null", nameof(Language))); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
<div class="table-items-row language"> |
|||
<div class="language-summary"> |
|||
<div class="row"> |
|||
<div class="col col-2"> |
|||
<span class="language-code" [class.language-master]="language.isMaster">{{language.iso2Code}}</span> |
|||
</div> |
|||
<div class="col col-6"> |
|||
<span class="language-name" [class.language-master]="language.isMaster">{{language.englishName}}</span> |
|||
</div> |
|||
<div class="col col-4"> |
|||
<div class="float-right"> |
|||
<button type="button" class="btn btn-secondary language-edit-button" [class.active]="isEditing" (click)="toggleEditing()" *ngIf="!language.isMaster || allLanguages.length > 1"> |
|||
<i class="icon-settings2"></i> |
|||
</button> |
|||
|
|||
<button type="button" class="btn btn-link btn-danger" (click)="removing.emit(language)" [class.invisible]="language.isMaster"> |
|||
<i class="icon-bin2"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="language-details" *ngIf="isEditing"> |
|||
<form [formGroup]="editForm" (ngSubmit)="save()"> |
|||
<div class="language-details-tabs clearfix"> |
|||
<div class="float-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="language-details-tab"> |
|||
<div class="form-group row" *ngIf="allLanguages.length > 1"> |
|||
<label for="field-label" class="col col-3 col-form-label fallback-label">Fallback</label> |
|||
|
|||
<div class="col col-9"> |
|||
<div class="fallback-languages" dnd-sortable-container [sortableData]="fallbackLanguages" *ngIf="fallbackLanguages.length > 0"> |
|||
<div class="fallback-language" *ngFor="let language of fallbackLanguages; let i = index" dnd-sortable [sortableIndex]="i"> |
|||
{{language.englishName}} |
|||
|
|||
<button type="button" class="btn btn-link btn-sm float-right" (click)="removeFallbackLanguage(language)"> |
|||
<i class="icon-bin2"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<form class="form-inline fallback-form" [formGroup]="addLanguageForm" (ngSubmit)="addLanguage()" *ngIf="otherLanguages.length > 0"> |
|||
<div class="form-group mr-2"> |
|||
<select class="form-control fallback-select" formControlName="language"> |
|||
<option *ngFor="let language of otherLanguages" [ngValue]="language">{{language.englishName}}</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<button type="submit" class="btn btn-success" [disabled]="!addLanguageForm.valid">Add Language</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-check offset-3 col col-9" *ngIf="!language.isMaster"> |
|||
<label class="form-check-label"> |
|||
<input class="form-check-input" type="checkbox" formControlName="isMaster"> Is Master |
|||
</label> |
|||
|
|||
<div class="form-hint"> |
|||
Master language is the last fallback language, when no value for a content and a language is available. |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-check offset-3 col col-9" *ngIf="!language.isMaster"> |
|||
<label class="form-check-label"> |
|||
<input class="form-check-input" type="checkbox" formControlName="isOptional"> Is Optional |
|||
</label> |
|||
|
|||
<div class="form-hint"> |
|||
Values for optional languages must not be specified, even if the field is set to required. |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,110 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
$field-header: #e7ebef; |
|||
|
|||
.table-items-row { |
|||
padding: 0; |
|||
} |
|||
|
|||
.language { |
|||
&-summary { |
|||
padding: 1rem 1.25rem; |
|||
position: relative; |
|||
line-height: 2.5rem; |
|||
} |
|||
|
|||
&-edit-button { |
|||
& { |
|||
color: $color-theme-blue; |
|||
line-height: 1rem; |
|||
font-size: 1rem; |
|||
font-weight: normal; |
|||
} |
|||
|
|||
&:hover { |
|||
color: $color-theme-blue-dark; |
|||
} |
|||
|
|||
&.active { |
|||
background: $color-theme-blue; |
|||
border-color: $color-theme-blue; |
|||
color: $color-dark-foreground; |
|||
} |
|||
} |
|||
|
|||
&-name { |
|||
@include truncate; |
|||
} |
|||
|
|||
&-master { |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&-details { |
|||
& { |
|||
position: relative; |
|||
} |
|||
|
|||
&::before { |
|||
@include caret-top; |
|||
@include absolute(-.5rem, 5.5rem, auto, auto); |
|||
border-color: transparent transparent $color-border; |
|||
border-width: .6rem; |
|||
} |
|||
|
|||
&-tab { |
|||
padding: 1rem 1.25rem 1.25rem; |
|||
} |
|||
|
|||
&-tabs { |
|||
background: $color-border; |
|||
position: relative; |
|||
padding: 1rem 1.25rem; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.btn-danger { |
|||
width: 3.2rem; |
|||
} |
|||
|
|||
.master { |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.fallback { |
|||
&-languages { |
|||
@include border-radius; |
|||
min-height: 2rem; |
|||
background: $color-border; |
|||
border: 0; |
|||
padding: .5rem; |
|||
} |
|||
|
|||
&-language { |
|||
& { |
|||
@include border-radius(2px); |
|||
padding: .5rem; |
|||
background: $color-dark-foreground; |
|||
border: 0; |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
&:last-child { |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
&-label { |
|||
margin-top: .4rem; |
|||
} |
|||
|
|||
&-form { |
|||
margin-top: .5rem; |
|||
} |
|||
|
|||
&-select { |
|||
width: 14rem; |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { Component, EventEmitter, Input, OnChanges, Output, OnInit } from '@angular/core'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
|
|||
import { |
|||
AppLanguageDto, |
|||
fadeAnimation, |
|||
ImmutableArray |
|||
} from 'shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-language', |
|||
styleUrls: ['./language.component.scss'], |
|||
templateUrl: './language.component.html', |
|||
animations: [ |
|||
fadeAnimation |
|||
] |
|||
}) |
|||
export class LanguageComponent implements OnInit, OnChanges { |
|||
@Input() |
|||
public language: AppLanguageDto; |
|||
|
|||
@Input() |
|||
public allLanguages: ImmutableArray<AppLanguageDto>; |
|||
|
|||
@Output() |
|||
public removing = new EventEmitter<AppLanguageDto>(); |
|||
|
|||
@Output() |
|||
public saving = new EventEmitter<AppLanguageDto>(); |
|||
|
|||
public otherLanguages: ImmutableArray<AppLanguageDto>; |
|||
|
|||
public fallbackLanguages: AppLanguageDto[] = []; |
|||
|
|||
public isEditing = false; |
|||
|
|||
public editFormSubmitted = false; |
|||
public editForm: FormGroup = |
|||
this.formBuilder.group({ |
|||
isMaster: [false, []], |
|||
isOptional: [false, []] |
|||
}); |
|||
|
|||
public addLanguageForm: FormGroup = |
|||
this.formBuilder.group({ |
|||
language: [null, |
|||
Validators.required |
|||
] |
|||
}); |
|||
|
|||
constructor( |
|||
private readonly formBuilder: FormBuilder |
|||
) { |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.resetForm(); |
|||
} |
|||
|
|||
public ngOnChanges() { |
|||
this.resetForm(); |
|||
} |
|||
|
|||
public cancel() { |
|||
this.resetForm(); |
|||
} |
|||
|
|||
public toggleEditing() { |
|||
this.isEditing = !this.isEditing; |
|||
} |
|||
|
|||
public addLanguage() { |
|||
this.addFallbackLanguage(this.addLanguageForm.get('language')!.value); |
|||
} |
|||
|
|||
public removeFallbackLanguage(language: AppLanguageDto) { |
|||
this.fallbackLanguages.splice(this.fallbackLanguages.indexOf(language), 1); |
|||
|
|||
this.otherLanguages = this.otherLanguages.push(language); |
|||
} |
|||
|
|||
public addFallbackLanguage(language: AppLanguageDto) { |
|||
this.fallbackLanguages.push(language); |
|||
|
|||
this.otherLanguages = this.otherLanguages.filter(l => l.iso2Code !== language.iso2Code); |
|||
} |
|||
|
|||
public save() { |
|||
this.editFormSubmitted = true; |
|||
|
|||
if (this.editForm.valid) { |
|||
const newLanguage = |
|||
new AppLanguageDto( |
|||
this.language.iso2Code, |
|||
this.language.englishName, |
|||
this.editForm.get('isMaster')!.value, |
|||
this.editForm.get('isOptional')!.value, |
|||
this.fallbackLanguages.map(l => l.iso2Code)); |
|||
|
|||
this.saving.emit(newLanguage); |
|||
} |
|||
} |
|||
|
|||
private resetForm() { |
|||
this.editFormSubmitted = false; |
|||
this.editForm.reset(this.language); |
|||
|
|||
this.isEditing = false; |
|||
|
|||
if (this.language && this.allLanguages) { |
|||
this.otherLanguages = |
|||
this.allLanguages.filter(l => |
|||
this.language.iso2Code !== l.iso2Code && |
|||
this.language.fallback.indexOf(l.iso2Code) < 0); |
|||
} |
|||
|
|||
if (this.language) { |
|||
this.fallbackLanguages = |
|||
this.allLanguages.filter(l => |
|||
this.language.fallback.indexOf(l.iso2Code) >= 0).values; |
|||
} |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue