mirror of https://github.com/Squidex/squidex.git
21 changed files with 319 additions and 89 deletions
@ -1,84 +1,100 @@ |
|||||
<form [formGroup]="annotateForm.form" (ngSubmit)="annotateAsset()"> |
<form [formGroup]="annotateForm.form" (ngSubmit)="annotateAsset()"> |
||||
<sqx-modal-dialog (close)="emitComplete()"> |
<sqx-modal-dialog (close)="emitComplete()" size="xl" fullHeight="true" [showHeader]="true" [showFooter]="false"> |
||||
<ng-container title> |
<ng-container title> |
||||
Update Asset |
Update Asset |
||||
</ng-container> |
</ng-container> |
||||
|
|
||||
|
<ng-container tabs> |
||||
|
<ul class="nav nav-tabs2"> |
||||
|
<li class="nav-item" *ngFor="let tab of selectableTabs"> |
||||
|
<a class="nav-link" [class.active]="tab === selectedTab" (click)="selectTab(tab)">{{tab}}</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
|
||||
|
<button type="submit" class="float-right btn btn-primary" [disabled]="!isEditable">Save</button> |
||||
|
</ng-container> |
||||
|
|
||||
<ng-container content> |
<ng-container content> |
||||
<sqx-form-error [error]="annotateForm.error | async"></sqx-form-error> |
<ng-container [ngSwitch]="selectedTab"> |
||||
|
<ng-container *ngSwitchCase="'Image'"> |
||||
<div class="form-group"> |
<div class="image"> |
||||
<label for="fileName">Name</label> |
<sqx-image-editor [imageUrl]="asset.contentUrl"></sqx-image-editor> |
||||
|
</div> |
||||
|
</ng-container> |
||||
|
<ng-container *ngSwitchCase="'Metadata'"> |
||||
|
<div class="metadata"> |
||||
|
<sqx-form-error [error]="annotateForm.error | async"></sqx-form-error> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label for="fileName">Name</label> |
||||
|
|
||||
|
<sqx-control-errors for="fileName" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
||||
|
|
||||
|
<input type="text" class="form-control" id="fileName" formControlName="fileName" /> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label for="slug">Slug</label> |
||||
|
|
||||
|
<sqx-control-errors for="slug" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
||||
|
|
||||
|
<input type="text" class="form-control slug" id="slug" formControlName="slug" sqxTransformInput="Slugify" /> |
||||
|
|
||||
|
<button type="button" class="btn btn-text-secondary btn-sm slug-generate" (click)="generateSlug()"> |
||||
|
Generate |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label>Tags</label> |
||||
|
|
||||
|
<sqx-control-errors for="tags" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
||||
|
|
||||
|
<sqx-tag-editor [suggestions]="allTags" [allowDuplicates]="false" [undefinedWhenEmpty]="false" formControlName="tags"></sqx-tag-editor> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label>Metadata</label> |
||||
|
|
||||
|
<div class="form-group row no-gutters" *ngFor="let form of annotateForm.metadata.controls; let i = index"> |
||||
|
<div class="col col-name pr-1"> |
||||
|
<sqx-control-errors [for]="form.get('name')" fieldName="Name" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
||||
|
|
||||
<sqx-control-errors for="fileName" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('name')" placeholder="Name" /> |
||||
|
</div> |
||||
<input type="text" class="form-control" id="fileName" formControlName="fileName" /> |
|
||||
</div> |
|
||||
|
|
||||
<div class="form-group"> |
|
||||
<label for="slug">Slug</label> |
|
||||
|
|
||||
<sqx-control-errors for="slug" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
<div class="col pr-1"> |
||||
|
<sqx-control-errors [for]="form.get('value')" fieldName="Value" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
||||
<input type="text" class="form-control slug" id="slug" formControlName="slug" sqxTransformInput="Slugify" /> |
|
||||
|
|
||||
<button type="button" class="btn btn-text-secondary btn-sm btn-generate" (click)="generateSlug()"> |
|
||||
Generate |
|
||||
</button> |
|
||||
</div> |
|
||||
|
|
||||
<div class="form-group"> |
|
||||
<label>Tags</label> |
|
||||
|
|
||||
<sqx-control-errors for="tags" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('value')" placeholder="Value" /> |
||||
|
</div> |
||||
<sqx-tag-editor [suggestions]="allTags" [allowDuplicates]="false" [undefinedWhenEmpty]="false" formControlName="tags"></sqx-tag-editor> |
|
||||
</div> |
<div class="col-auto col-options"> |
||||
|
<button type="button" class="btn btn-text-danger" |
||||
<div class="form-group"> |
[disabled]="!isEditable" |
||||
<label>Metadata</label> |
(sqxConfirmClick)="annotateForm.removeMetadata(i)" |
||||
|
confirmTitle="Remove url" |
||||
<div class="form-group row no-gutters" *ngFor="let form of annotateForm.metadata.controls; let i = index"> |
confirmText="Do you really want to remove this metadata?"> |
||||
<div class="col col-name pr-1"> |
<i class="icon-bin2"></i> |
||||
<sqx-control-errors [for]="form.get('name')" fieldName="Name" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
</button> |
||||
|
</div> |
||||
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('name')" placeholder="Name" /> |
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<button type="button" class="btn btn-success" (click)="annotateForm.addMetadata()" [disabled]="!isEditable"> |
||||
|
Add Metadata |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group form-check"> |
||||
|
<input type="checkbox" class="form-check-input" id="isProtected" formControlName="isProtected" /> |
||||
|
|
||||
|
<label class="form-check-label" for="isProtected">Protected</label> |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
</ng-container> |
||||
<div class="col pr-1"> |
</ng-container> |
||||
<sqx-control-errors [for]="form.get('value')" fieldName="Value" [submitted]="annotateForm.submitted | async"></sqx-control-errors> |
|
||||
|
|
||||
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('value')" placeholder="Value" /> |
|
||||
</div> |
|
||||
|
|
||||
<div class="col-auto col-options"> |
|
||||
<button type="button" class="btn btn-text-danger" |
|
||||
[disabled]="!isEditable" |
|
||||
(sqxConfirmClick)="annotateForm.removeMetadata(i)" |
|
||||
confirmTitle="Remove url" |
|
||||
confirmText="Do you really want to remove this metadata?"> |
|
||||
<i class="icon-bin2"></i> |
|
||||
</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="form-group"> |
|
||||
<button type="button" class="btn btn-success" (click)="annotateForm.addMetadata()" [disabled]="!isEditable"> |
|
||||
Add Metadata |
|
||||
</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="form-group form-check"> |
|
||||
<input type="checkbox" class="form-check-input" id="isProtected" formControlName="isProtected" /> |
|
||||
|
|
||||
<label class="form-check-label" for="isProtected">Protected</label> |
|
||||
</div> |
|
||||
</ng-container> |
|
||||
|
|
||||
<ng-container footer> |
|
||||
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button> |
|
||||
<button type="submit" class="float-right btn btn-primary" *ngIf="isEditable">Save</button> |
|
||||
</ng-container> |
</ng-container> |
||||
</sqx-modal-dialog> |
</sqx-modal-dialog> |
||||
</form> |
</form> |
||||
@ -0,0 +1 @@ |
|||||
|
<div #editor></div> |
||||
@ -0,0 +1,29 @@ |
|||||
|
:host { |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
:host /deep/ { |
||||
|
.tui-image-editor-header { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
* { |
||||
|
box-sizing: content-box; |
||||
|
} |
||||
|
|
||||
|
.tie-btn-delete { |
||||
|
display: none !important; |
||||
|
} |
||||
|
|
||||
|
.tie-btn-delete-all { |
||||
|
display: none !important; |
||||
|
|
||||
|
& + li { |
||||
|
display: none !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
svg { |
||||
|
vertical-align: baseline; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core'; |
||||
|
|
||||
|
import { ResourceLoaderService } from '@app/shared/internal'; |
||||
|
|
||||
|
declare var tui: any; |
||||
|
|
||||
|
const blackTheme = { |
||||
|
'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png', |
||||
|
'common.bisize.width': '251px', |
||||
|
'common.bisize.height': '21px', |
||||
|
'common.backgroundImage': 'none', |
||||
|
'common.backgroundColor': '#000', |
||||
|
'common.border': '0px', |
||||
|
|
||||
|
// header
|
||||
|
'header.backgroundImage': 'none', |
||||
|
'header.backgroundColor': 'transparent', |
||||
|
'header.border': '0px', |
||||
|
|
||||
|
// load button
|
||||
|
'loadButton.backgroundColor': '#fff', |
||||
|
'loadButton.border': '1px solid #ddd', |
||||
|
'loadButton.color': '#222', |
||||
|
'loadButton.fontFamily': '\'Noto Sans\', sans-serif', |
||||
|
'loadButton.fontSize': '12px', |
||||
|
|
||||
|
// download button
|
||||
|
'downloadButton.backgroundColor': '#fdba3b', |
||||
|
'downloadButton.border': '1px solid #fdba3b', |
||||
|
'downloadButton.color': '#fff', |
||||
|
'downloadButton.fontFamily': '\'Noto Sans\', sans-serif', |
||||
|
'downloadButton.fontSize': '12px', |
||||
|
|
||||
|
// main icons
|
||||
|
'menu.normalIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-d.svg', |
||||
|
'menu.normalIcon.name': 'icon-d', |
||||
|
'menu.activeIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-b.svg', |
||||
|
'menu.activeIcon.name': 'icon-b', |
||||
|
'menu.disabledIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-a.svg', |
||||
|
'menu.disabledIcon.name': 'icon-a', |
||||
|
'menu.hoverIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-c.svg', |
||||
|
'menu.hoverIcon.name': 'icon-c', |
||||
|
'menu.iconSize.width': '24px', |
||||
|
'menu.iconSize.height': '24px', |
||||
|
|
||||
|
// submenu primary color
|
||||
|
'submenu.backgroundColor': '#000', |
||||
|
'submenu.partition.color': '#3c3c3c', |
||||
|
|
||||
|
// submenu icons
|
||||
|
'submenu.normalIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-d.svg', |
||||
|
'submenu.normalIcon.name': 'icon-d', |
||||
|
'submenu.activeIcon.path': 'https://unpkg.com/tui-image-editor@3.7.3/dist/svg/icon-c.svg', |
||||
|
'submenu.activeIcon.name': 'icon-c', |
||||
|
'submenu.iconSize.width': '32px', |
||||
|
'submenu.iconSize.height': '32px', |
||||
|
|
||||
|
// submenu labels
|
||||
|
'submenu.normalLabel.color': '#8a8a8a', |
||||
|
'submenu.normalLabel.fontWeight': 'normal', |
||||
|
'submenu.activeLabel.color': '#fff', |
||||
|
'submenu.activeLabel.fontWeight': 'normal', |
||||
|
|
||||
|
// checkbox style
|
||||
|
'checkbox.border': '0px', |
||||
|
'checkbox.backgroundColor': '#fff', |
||||
|
|
||||
|
// range style
|
||||
|
'range.pointer.color': '#fff', |
||||
|
'range.bar.color': '#666', |
||||
|
'range.subbar.color': '#d1d1d1', |
||||
|
|
||||
|
'range.disabledPointer.color': '#414141', |
||||
|
'range.disabledBar.color': '#282828', |
||||
|
'range.disabledSubbar.color': '#414141', |
||||
|
|
||||
|
'range.value.color': '#fff', |
||||
|
'range.value.fontWeight': 'normal', |
||||
|
'range.value.fontSize': '11px', |
||||
|
'range.value.border': '1px solid #353535', |
||||
|
'range.value.backgroundColor': '#151515', |
||||
|
'range.title.color': '#fff', |
||||
|
'range.title.fontWeight': 'normal', |
||||
|
|
||||
|
// colorpicker style
|
||||
|
'colorpicker.button.border': '1px solid #1e1e1e', |
||||
|
'colorpicker.title.color': '#fff' |
||||
|
}; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-image-editor', |
||||
|
styleUrls: ['./image-editor.component.scss'], |
||||
|
templateUrl: './image-editor.component.html', |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush |
||||
|
}) |
||||
|
export class ImageEditorComponent implements AfterViewInit, OnChanges { |
||||
|
private imageEditor: any; |
||||
|
|
||||
|
@Input() |
||||
|
public imageUrl: string; |
||||
|
|
||||
|
@ViewChild('editor', { static: false }) |
||||
|
public editor: ElementRef; |
||||
|
|
||||
|
constructor( |
||||
|
private readonly resourceLoader: ResourceLoaderService |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public ngOnChanges() { |
||||
|
if (this.imageEditor && this.imageUrl) { |
||||
|
this.imageEditor.loadImageFromURL(this.imageUrl); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ngAfterViewInit() { |
||||
|
const styles = [ |
||||
|
'https://uicdn.toast.com/tui-color-picker/latest/tui-color-picker.css', |
||||
|
'https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.css' |
||||
|
]; |
||||
|
|
||||
|
const scripts = [ |
||||
|
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js', |
||||
|
'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js', |
||||
|
'https://uicdn.toast.com/tui.code-snippet/latest/tui-code-snippet.min.js', |
||||
|
'https://uicdn.toast.com/tui-color-picker/latest/tui-color-picker.js', |
||||
|
'https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.js' |
||||
|
]; |
||||
|
|
||||
|
styles.forEach(style => this.resourceLoader.loadStyle(style)); |
||||
|
|
||||
|
const s = scripts.map(script => this.resourceLoader.loadScript(script)); |
||||
|
|
||||
|
Promise.all(s).then(() => { |
||||
|
this.imageEditor = new tui.ImageEditor(this.editor.nativeElement, { |
||||
|
includeUI: { |
||||
|
loadImage: { |
||||
|
path: this.imageUrl, name: 'image' |
||||
|
}, |
||||
|
menu: [ |
||||
|
'crop', |
||||
|
'flip', |
||||
|
'rotate', |
||||
|
'mask', |
||||
|
'filter' |
||||
|
], |
||||
|
theme: blackTheme |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue