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="row"> |
||||
<div class="col-4 drop-area-container"> |
<div class="col-4 drop-area-container" *ngIf="!isDisabled"> |
||||
<div class="drop-area" dnd-droppable (onDropSuccess)="onAssetDropped($event.dragData)" (sqxFileDrop)="addFiles($event)"> |
<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. |
Drop files or assets here to add them. |
||||
</div> |
</div> |
||||
</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