diff --git a/src/Squidex/app/framework/angular/rich-editor.component.html b/src/Squidex/app/framework/angular/rich-editor.component.html deleted file mode 100644 index bb84a4048..000000000 --- a/src/Squidex/app/framework/angular/rich-editor.component.html +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/src/Squidex/app/framework/angular/rich-editor.component.scss b/src/Squidex/app/framework/angular/rich-editor.component.scss deleted file mode 100644 index 07b2c640b..000000000 --- a/src/Squidex/app/framework/angular/rich-editor.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '_mixins'; -@import '_vars'; - -.editor { - background: $color-dark-foreground; - border: 1px solid $color-input; - height: 30rem; -} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/rich-editor.component.ts b/src/Squidex/app/framework/angular/rich-editor.component.ts deleted file mode 100644 index 4e95a8152..000000000 --- a/src/Squidex/app/framework/angular/rich-editor.component.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Sebastian Stehle. All rights reserved - */ - -import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; - -import { Types } from './../utils/types'; - -import { ResourceLoaderService } from './../services/resource-loader.service'; - -declare var tinymce: any; - -export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { - provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true -}; - -@Component({ - selector: 'sqx-rich-editor', - styleUrls: ['./rich-editor.component.scss'], - templateUrl: './rich-editor.component.html', - providers: [SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR] -}) -export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, OnDestroy { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; - private tinyEditor: any; - private tinyInitTimer: any; - private value: string; - private isDisabled = false; - - @ViewChild('editor') - public editor: ElementRef; - - constructor( - private readonly resourceLoader: ResourceLoaderService - ) { - } - - public ngOnDestroy() { - clearTimeout(this.tinyInitTimer); - - tinymce.remove(this.editor); - } - - public ngAfterViewInit() { - const self = this; - - this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.5.4/tinymce.min.js').then(() => { - tinymce.init({ - setup: (editor: any) => { - self.tinyEditor = editor; - self.tinyEditor.setMode(this.isDisabled ? 'readonly' : 'design'); - - self.tinyEditor.on('change', () => { - const value = editor.getContent(); - - if (this.value !== value) { - this.value = value; - - self.callChange(value); - } - }); - - self.tinyEditor.on('blur', () => { - self.callTouched(); - }); - - this.tinyInitTimer = - setTimeout(() => { - self.tinyEditor.setContent(this.value || ''); - }, 500); - }, - removed_menuitems: 'newdocument', plugins: 'code', target: this.editor.nativeElement - }); - }); - } - - public writeValue(value: string) { - this.value = Types.isString(value) ? value : ''; - - if (this.tinyEditor) { - this.tinyEditor.setContent(this.value); - } - } - - public setDisabledState(isDisabled: boolean): void { - this.isDisabled = isDisabled; - - if (this.tinyEditor) { - this.tinyEditor.setMode(isDisabled ? 'readonly' : 'design'); - } - } - - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } -} \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index 40c8b1d87..7ba82733d 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -36,7 +36,6 @@ export * from './angular/panel-container.directive'; export * from './angular/parent-link.directive'; export * from './angular/popup-link.directive'; export * from './angular/progress-bar.component'; -export * from './angular/rich-editor.component'; export * from './angular/root-view.directive'; export * from './angular/router-utils'; export * from './angular/scroll-active.directive'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index 973635904..7a99b4dbc 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -54,7 +54,6 @@ import { PopupLinkDirective, ProgressBarComponent, ResourceLoaderService, - RichEditorComponent, RootViewDirective, RootViewService, ScrollActiveDirective, @@ -115,7 +114,6 @@ import { ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, - RichEditorComponent, RootViewDirective, ScrollActiveDirective, ShortcutComponent, @@ -164,7 +162,6 @@ import { ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, - RichEditorComponent, RootViewDirective, ScrollActiveDirective, ShortcutComponent, diff --git a/src/Squidex/app/shared/components/asset.component.html b/src/Squidex/app/shared/components/asset.component.html index 88fc97799..96e1f7633 100644 --- a/src/Squidex/app/shared/components/asset.component.html +++ b/src/Squidex/app/shared/components/asset.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Squidex/app/shared/components/asset.component.ts b/src/Squidex/app/shared/components/asset.component.ts index 85540c6cf..d559efba6 100644 --- a/src/Squidex/app/shared/components/asset.component.ts +++ b/src/Squidex/app/shared/components/asset.component.ts @@ -56,6 +56,9 @@ export class AssetComponent extends AppComponentBase implements OnInit { @Output() public deleting = new EventEmitter(); + @Output() + public clicked = new EventEmitter(); + @Output() public failed = new EventEmitter(); diff --git a/src/Squidex/app/shared/components/rich-editor.component.html b/src/Squidex/app/shared/components/rich-editor.component.html new file mode 100644 index 000000000..413c8e708 --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.html @@ -0,0 +1,40 @@ +
+ + \ No newline at end of file diff --git a/src/Squidex/app/shared/components/rich-editor.component.scss b/src/Squidex/app/shared/components/rich-editor.component.scss new file mode 100644 index 000000000..33c03a1e5 --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.scss @@ -0,0 +1,36 @@ +@import '_mixins'; +@import '_vars'; + +.editor { + background: $color-dark-foreground; + border: 1px solid $color-input; + height: 30rem; +} +.asset-selector { + z-index: 65560; + + .modal-header { + background: transparent; + border-bottom: 1px solid #c5c5c5; + + .modal-title { + text-decoration: none; + color: #333; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + text-shadow: none; + } + } + + .modal-content { + width: 100%; + border-radius: 0; + -webkit-border-radius: 0; + } + .modal-dialog { + max-width: 900px; + } + + .btn { + border-radius: 0; + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/components/rich-editor.component.ts b/src/Squidex/app/shared/components/rich-editor.component.ts new file mode 100644 index 000000000..b88b56988 --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.ts @@ -0,0 +1,165 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder } from '@angular/forms'; + +import { AppComponentBase } from './app.component-base'; +import { AssetUrlPipe } from './pipes'; +import { ApiUrlConfig, ModalView, AppsStoreService, AssetDto, AssetsService, ImmutableArray, DialogService, AuthService, Pager, Types, ResourceLoaderService } from './../declarations-base'; + +declare var tinymce: any; + +export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true +}; + +@Component({ + selector: 'sqx-rich-editor', + styleUrls: ['./rich-editor.component.scss'], + templateUrl: './rich-editor.component.html', + providers: [SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR] +}) +export class RichEditorComponent extends AppComponentBase implements ControlValueAccessor, AfterViewInit, OnDestroy { + private callChange = (v: any) => { /* NOOP */ }; + private callTouched = () => { /* NOOP */ }; + private tinyEditor: any; + private tinyInitTimer: any; + private value: string; + private isDisabled = false; + private assetSelectorClickHandler: any = null; + public assetsItems: ImmutableArray; + public assetsPager = new Pager(0, 0, 12); + private assetUrlGenerator: AssetUrlPipe; + + @ViewChild('editor') + public editor: ElementRef; + + public assetsDialog = new ModalView(); + public assetsForm = this.formBuilder.group({ + name: [''] + }); + + constructor(dialogs: DialogService, apps: AppsStoreService, authService: AuthService, + private readonly resourceLoader: ResourceLoaderService, + private readonly formBuilder: FormBuilder, + private readonly assetsService: AssetsService, + private readonly apiUrlConfig: ApiUrlConfig + ) { + super(dialogs, apps, authService); + this.assetUrlGenerator = new AssetUrlPipe(this.apiUrlConfig); + } + + private load() { + this.appNameOnce() + .switchMap(app => this.assetsService.getAssets(app, this.assetsPager.pageSize, this.assetsPager.skip)) + .subscribe(dtos => { + this.assetsItems = ImmutableArray.of(dtos.items); + this.assetsPager = this.assetsPager.setCount(dtos.total); + }, error => { + this.notifyError(error); + }); + } + + public goNext() { + this.assetsPager = this.assetsPager.goNext(); + + this.load(); + } + + public goPrev() { + this.assetsPager = this.assetsPager.goPrev(); + + this.load(); + } + + public ngOnDestroy() { + clearTimeout(this.tinyInitTimer); + + tinymce.remove(this.editor); + } + + public ngAfterViewInit() { + const self = this; + + this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.5.4/tinymce.min.js').then(() => { + tinymce.init({ + setup: (editor: any) => { + self.tinyEditor = editor; + self.tinyEditor.setMode(this.isDisabled ? 'readonly' : 'design'); + + self.tinyEditor.on('change', () => { + const value = editor.getContent(); + + if (this.value !== value) { + this.value = value; + + self.callChange(value); + } + }); + + self.tinyEditor.on('blur', () => { + self.callTouched(); + }); + + this.tinyInitTimer = + setTimeout(() => { + self.tinyEditor.setContent(this.value || ''); + }, 500); + }, + removed_menuitems: 'newdocument', plugins: 'code,image', target: this.editor.nativeElement, file_picker_types: 'image', file_picker_callback: (cb: any, value: any, meta: any) => { + self.load(); + self.assetsDialog.show(); + self.assetSelectorClickHandler = { + cb: cb, + meta: meta + }; + } + }); + }); + } + + public writeValue(value: string) { + this.value = Types.isString(value) ? value : ''; + + if (this.tinyEditor) { + this.tinyEditor.setContent(this.value); + } + } + + public setDisabledState(isDisabled: boolean): void { + this.isDisabled = isDisabled; + + if (this.tinyEditor) { + this.tinyEditor.setMode(isDisabled ? 'readonly' : 'design'); + } + } + + public registerOnChange(fn: any) { + this.callChange = fn; + } + + public registerOnTouched(fn: any) { + this.callTouched = fn; + } + + public closeAssetDialog() { + this.assetsDialog.hide(); + this.assetSelectorClickHandler = null; + } + + public onAssetClicked(asset: AssetDto) { + if (this.assetSelectorClickHandler != null) { + this.assetSelectorClickHandler.cb(this.assetUrlGenerator.transform(asset), { + description: asset.fileName, + width: asset.pixelWidth, + height: asset.pixelHeight + }); + this.closeAssetDialog(); + } + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/declarations.ts b/src/Squidex/app/shared/declarations.ts index 3e4313916..5fc29bd25 100644 --- a/src/Squidex/app/shared/declarations.ts +++ b/src/Squidex/app/shared/declarations.ts @@ -13,5 +13,6 @@ export * from './components/help.component'; export * from './components/history.component'; export * from './components/language-selector.component'; export * from './components/pipes'; +export * from './components/rich-editor.component'; export * from './declarations-base'; \ No newline at end of file diff --git a/src/Squidex/app/shared/module.ts b/src/Squidex/app/shared/module.ts index f67368770..6f229df77 100644 --- a/src/Squidex/app/shared/module.ts +++ b/src/Squidex/app/shared/module.ts @@ -57,7 +57,8 @@ import { UserManagementService, UsersProviderService, UsersService, - WebhooksService + WebhooksService, + RichEditorComponent } from './declarations'; @NgModule({ @@ -81,7 +82,8 @@ import { UserNamePipe, UserNameRefPipe, UserPicturePipe, - UserPictureRefPipe + UserPictureRefPipe, + RichEditorComponent ], exports: [ AppFormComponent, @@ -99,7 +101,8 @@ import { UserNamePipe, UserNameRefPipe, UserPicturePipe, - UserPictureRefPipe + UserPictureRefPipe, + RichEditorComponent ] }) export class SqxSharedModule {