mirror of https://github.com/Squidex/squidex.git
53 changed files with 1673 additions and 1331 deletions
@ -1,7 +1,7 @@ |
|||
<div class="assets-container"> |
|||
<div class="assets-container" [class.disabled]="isDisabled"> |
|||
<div class="row"> |
|||
<div class="col-4 drop-area-container"> |
|||
<div class="drop-area" dnd-droppable (onDropSuccess)="onAssetDropped($event.dragData)" (sqxFileDrop)="addFiles($event)"> |
|||
<div class="col-4 drop-area-container" *ngIf="!isDisabled"> |
|||
<div class="drop-area" dnd-droppable (onDropSuccess)="onAssetDropped($event.dragData)" [allowDrop]="canDrop()" (sqxFileDrop)="addFiles($event)" routerLink="assets"> |
|||
Drop files or assets here to add them. |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,31 @@ |
|||
<div class="references-container" [class.disabled]="isDisabled"> |
|||
<div class="drop-area-container" *ngIf="schema && !isDisabled"> |
|||
<div class="drop-area" dnd-droppable (onDropSuccess)="onContentDropped($event.dragData.content)" [allowDrop]="canDrop()" [routerLink]="['references', schemaId, languageCode]"> |
|||
Drop content here to add them. |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="invalid" *ngIf="isInvalidSchema"> |
|||
Schema not found or not configured yet. |
|||
</div> |
|||
|
|||
<table class="table table-items table-fixed" [class.disabled]="isDisabled" *ngIf="contentItems && contentItems.length > 0"> |
|||
<colgroup> |
|||
<col *ngFor="let field of contentFields" [style.width]="columnWidth + '%'" /> |
|||
<col style="width: 180px" /> |
|||
<col style="width: 50px" /> |
|||
<col style="width: 80px" /> |
|||
</colgroup> |
|||
|
|||
<tbody dnd-sortable-container [sortableData]="contentItems.mutableValues"> |
|||
<ng-template ngFor let-content let-i="index" [ngForOf]="contentItems"> |
|||
<tr [sqxContent]="content" isReadOnly="true" dnd-sortable [sortableIndex]="i" (sorted)="onContentsSorted($event)" |
|||
[language]="languageSelected" |
|||
[fields]="contentFields" |
|||
[schema]="schema" |
|||
(deleting)="onContentRemoving(content)"></tr> |
|||
<tr class="spacer"></tr> |
|||
</ng-template> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
@ -0,0 +1,63 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
.disabled { |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.references { |
|||
&-container { |
|||
& { |
|||
background: $color-background; |
|||
overflow-x: hidden; |
|||
overflow-y: scroll; |
|||
padding: 1rem; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.invalid { |
|||
padding: 2rem; |
|||
font-size: 1.2rem; |
|||
font-weight: normal; |
|||
text-align: center; |
|||
color: darken($color-border, 30%); |
|||
} |
|||
|
|||
.drop-area { |
|||
& { |
|||
@include transition(border-color .4s ease); |
|||
@include border-radius; |
|||
border: 2px dashed $color-border; |
|||
font-size: 1.2rem; |
|||
font-weight: normal; |
|||
text-align: center; |
|||
padding: 2rem; |
|||
cursor: pointer; |
|||
color: darken($color-border, 30%); |
|||
} |
|||
|
|||
&:hover { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
&.drag, |
|||
&.dnd-drag-over, |
|||
&.dnd-drag-enter { |
|||
border-color: darken($color-border, 10%); |
|||
cursor: copy; |
|||
color: darken($color-border, 40%); |
|||
text-decoration: none; |
|||
} |
|||
} |
|||
|
|||
.table { |
|||
& { |
|||
margin-bottom: -.3rem; |
|||
margin-top: 1rem; |
|||
} |
|||
|
|||
&.disabled { |
|||
margin-top: 0; |
|||
} |
|||
} |
|||
@ -0,0 +1,154 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
// tslint:disable:prefer-for-of
|
|||
|
|||
import { Component, forwardRef, Input } from '@angular/core'; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
|
|||
import { |
|||
AppComponentBase, |
|||
AppsStoreService, |
|||
ContentDto, |
|||
ContentsService, |
|||
FieldDto, |
|||
ImmutableArray, |
|||
NotificationService, |
|||
SchemaDetailsDto, |
|||
SchemasService |
|||
} from 'shared'; |
|||
|
|||
const NOOP = () => { /* NOOP */ }; |
|||
|
|||
export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferencesEditorComponent), multi: true |
|||
}; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-references-editor', |
|||
styleUrls: ['./references-editor.component.scss'], |
|||
templateUrl: './references-editor.component.html', |
|||
providers: [SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class ReferencesEditorComponent extends AppComponentBase implements ControlValueAccessor { |
|||
private changeCallback: (value: any) => void = NOOP; |
|||
private touchedCallback: () => void = NOOP; |
|||
|
|||
@Input() |
|||
public schemaId: string; |
|||
|
|||
@Input() |
|||
public languageCode: string; |
|||
|
|||
public schema: SchemaDetailsDto; |
|||
|
|||
public contentItems = ImmutableArray.empty<ContentDto>(); |
|||
public contentFields: FieldDto[]; |
|||
|
|||
public columnWidth: number; |
|||
|
|||
public isDisabled = false; |
|||
public isInvalidSchema = false; |
|||
|
|||
constructor(apps: AppsStoreService, notifications: NotificationService, |
|||
private readonly contentsService: ContentsService, |
|||
private readonly schemasService: SchemasService |
|||
) { |
|||
super(notifications, apps); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.appNameOnce() |
|||
.switchMap(app => this.schemasService.getSchema(app, this.schemaId)) |
|||
.subscribe(dto => { |
|||
this.schema = dto; |
|||
|
|||
this.loadFields(); |
|||
}, error => { |
|||
this.isInvalidSchema = true; |
|||
}); |
|||
} |
|||
|
|||
public writeValue(value: any) { |
|||
this.contentItems = ImmutableArray.empty<ContentDto>(); |
|||
|
|||
if (value && value.length > 0) { |
|||
const contentIds: string[] = value; |
|||
|
|||
this.appNameOnce() |
|||
.switchMap(app => this.contentsService.getContents(app, this.schemaId, 10000, 0, undefined, contentIds)) |
|||
.subscribe(dtos => { |
|||
this.contentItems = ImmutableArray.of(dtos.items); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.changeCallback = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.touchedCallback = fn; |
|||
} |
|||
|
|||
public canDrop() { |
|||
const component = this; |
|||
|
|||
return (dragData: any) => { |
|||
return dragData.content instanceof ContentDto && dragData.schemaId === component.schemaId && !component.contentItems.find(c => c.id === dragData.content.id); |
|||
}; |
|||
} |
|||
|
|||
public onContentDropped(content: ContentDto) { |
|||
if (content) { |
|||
this.contentItems = this.contentItems.pushFront(content); |
|||
|
|||
this.updateValue(); |
|||
} |
|||
} |
|||
|
|||
public onContentRemoving(content: ContentDto) { |
|||
if (content) { |
|||
this.contentItems = this.contentItems.remove(content); |
|||
|
|||
this.updateValue(); |
|||
} |
|||
} |
|||
|
|||
public onContentsSorted(contents: ContentDto[]) { |
|||
if (contents) { |
|||
this.contentItems = ImmutableArray.of(contents); |
|||
|
|||
this.updateValue(); |
|||
} |
|||
} |
|||
|
|||
private updateValue() { |
|||
let ids: string[] | null = this.contentItems.values.map(x => x.id); |
|||
|
|||
if (ids.length === 0) { |
|||
ids = null; |
|||
} |
|||
|
|||
this.touchedCallback(); |
|||
this.changeCallback(ids); |
|||
} |
|||
|
|||
private loadFields() { |
|||
this.contentFields = this.schema.fields.filter(x => x.properties.isListField); |
|||
|
|||
this.columnWidth = 100 / this.contentFields.length; |
|||
|
|||
if (this.contentFields.length === 0 && this.schema.fields.length > 0) { |
|||
this.contentFields = [this.schema.fields[0]]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params } from '@angular/router'; |
|||
|
|||
export function allDataFromRoute(route: ActivatedRoute): Data { |
|||
return allData(route.snapshot); |
|||
} |
|||
|
|||
export function allData(route: ActivatedRouteSnapshot): Data { |
|||
let result: { [key: string]: any } = { }; |
|||
|
|||
while (route) { |
|||
for (let key in route.data) { |
|||
if (route.data.hasOwnProperty(key) && !result[key]) { |
|||
result[key] = route.data[key]; |
|||
} |
|||
} |
|||
|
|||
route = route.parent; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
export function allParametersFromRoute(route: ActivatedRoute): Params { |
|||
return allParameters(route.snapshot); |
|||
} |
|||
|
|||
export function allParameters(route: ActivatedRouteSnapshot): Params { |
|||
let result: { [key: string]: any } = { }; |
|||
|
|||
while (route) { |
|||
for (let key of route.paramMap.keys) { |
|||
if (!result[key]) { |
|||
result[key] = route.paramMap.get(key); |
|||
} |
|||
} |
|||
|
|||
route = route.parent; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
File diff suppressed because it is too large
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 58 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Loading…
Reference in new issue