mirror of https://github.com/Squidex/squidex.git
15 changed files with 290 additions and 139 deletions
@ -1,5 +0,0 @@ |
|||
<div #container> |
|||
<div #inner [class.fullscreen]="isFullscreen"> |
|||
<textarea class="form-control" #editor></textarea> |
|||
</div> |
|||
</div> |
|||
@ -1,15 +0,0 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
$background: #fff; |
|||
|
|||
.editor { |
|||
height: 30rem; |
|||
} |
|||
|
|||
.fullscreen { |
|||
@include fixed(0, 0, 0, 0); |
|||
border: 0; |
|||
background: $background; |
|||
z-index: 100000; |
|||
} |
|||
@ -1,106 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { AfterViewInit, Component, forwardRef, ElementRef, 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 SimpleMDE: any; |
|||
|
|||
export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true |
|||
}; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-markdown-editor', |
|||
styleUrls: ['./markdown-editor.component.scss'], |
|||
templateUrl: './markdown-editor.component.html', |
|||
providers: [SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewInit { |
|||
private callChange = (v: any) => { /* NOOP */ }; |
|||
private callTouched = () => { /* NOOP */ }; |
|||
private simplemde: any; |
|||
private value: string; |
|||
private isDisabled = false; |
|||
|
|||
@ViewChild('editor') |
|||
public editor: ElementRef; |
|||
|
|||
@ViewChild('container') |
|||
public container: ElementRef; |
|||
|
|||
@ViewChild('inner') |
|||
public inner: ElementRef; |
|||
|
|||
public isFullscreen = false; |
|||
|
|||
constructor( |
|||
private readonly resourceLoader: ResourceLoaderService |
|||
) { |
|||
this.resourceLoader.loadStyle('https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css'); |
|||
} |
|||
|
|||
public writeValue(value: string) { |
|||
this.value = Types.isString(value) ? value : ''; |
|||
|
|||
if (this.simplemde) { |
|||
this.simplemde.value(this.value); |
|||
} |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
|
|||
if (this.simplemde) { |
|||
this.simplemde.codemirror.setOption('readOnly', isDisabled); |
|||
} |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.callChange = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.callTouched = fn; |
|||
} |
|||
|
|||
public ngAfterViewInit() { |
|||
this.resourceLoader.loadScript('https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js').then(() => { |
|||
this.simplemde = new SimpleMDE({ element: this.editor.nativeElement }); |
|||
this.simplemde.value(this.value || ''); |
|||
this.simplemde.codemirror.setOption('readOnly', this.isDisabled); |
|||
|
|||
this.simplemde.codemirror.on('change', () => { |
|||
const value = this.simplemde.value(); |
|||
|
|||
if (this.value !== value) { |
|||
this.value = value; |
|||
|
|||
this.callChange(value); |
|||
} |
|||
}); |
|||
|
|||
this.simplemde.codemirror.on('blur', () => { |
|||
this.callTouched(); |
|||
}); |
|||
|
|||
this.simplemde.codemirror.on('refresh', () => { |
|||
this.isFullscreen = this.simplemde.isFullscreenActive(); |
|||
|
|||
if (this.isFullscreen) { |
|||
document.body.appendChild(this.inner.nativeElement); |
|||
} else { |
|||
this.container.nativeElement.appendChild(this.inner.nativeElement); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
<div #container class="editor-container"> |
|||
<div #inner> |
|||
<textarea class="form-control" #editor></textarea> |
|||
</div> |
|||
|
|||
<div class="file-drop drag drop-area" [class.dragging]="draggedOver" dnd-droppable (onDropSuccess)="onItemDropped($event)"> |
|||
<div class="drop-text">Drop files or assets here to add them.</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,41 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
$background: #fff; |
|||
|
|||
.editor { |
|||
height: 30rem; |
|||
} |
|||
|
|||
.editor-container { |
|||
position: relative; |
|||
|
|||
.drop-area { |
|||
& { |
|||
@include absolute(80px, 30px, 66px, 30px); |
|||
@include border-radius; |
|||
z-index: 1; |
|||
align-content: center; |
|||
align-items: center; |
|||
display: none; |
|||
border: 2px dashed $color-border; |
|||
font-size: 1.2rem; |
|||
font-weight: normal; |
|||
justify-content: center; |
|||
color: darken($color-border, 30%); |
|||
} |
|||
|
|||
&.dragging { |
|||
@include flex-box; |
|||
} |
|||
|
|||
&.drag, |
|||
&.dnd-drag-over, |
|||
&.dnd-drag-enter { |
|||
border-color: darken($color-border, 10%); |
|||
cursor: copy; |
|||
color: darken($color-border, 40%); |
|||
text-decoration: none; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
|
|||
import { |
|||
AssetDto, |
|||
AssetDragged, |
|||
MessageBus, |
|||
ResourceLoaderService, |
|||
Types |
|||
} from './../declarations-base'; |
|||
|
|||
declare var SimpleMDE: any; |
|||
|
|||
export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true |
|||
}; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-markdown-editor', |
|||
styleUrls: ['./markdown-editor.component.scss'], |
|||
templateUrl: './markdown-editor.component.html', |
|||
providers: [SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewInit, OnDestroy, OnInit { |
|||
private callChange = (v: any) => { /* NOOP */ }; |
|||
private callTouched = () => { /* NOOP */ }; |
|||
private simplemde: any; |
|||
private value: string; |
|||
private isDisabled = false; |
|||
private assetDraggedSubscription: any; |
|||
|
|||
@ViewChild('editor') |
|||
public editor: ElementRef; |
|||
|
|||
@ViewChild('container') |
|||
public container: ElementRef; |
|||
|
|||
@ViewChild('inner') |
|||
public inner: ElementRef; |
|||
|
|||
@Output() |
|||
public assetPluginClicked = new EventEmitter<any>(); |
|||
|
|||
public draggedOver = false; |
|||
|
|||
constructor( |
|||
private readonly resourceLoader: ResourceLoaderService, |
|||
private readonly messageBus: MessageBus |
|||
) { |
|||
this.resourceLoader.loadStyle('https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css'); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.assetDraggedSubscription.unsubscribe(); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.assetDraggedSubscription = |
|||
this.messageBus.of(AssetDragged).subscribe(message => { |
|||
if (message.assetDto.isImage) { |
|||
if (message.dragEvent === AssetDragged.DRAG_START) { |
|||
this.draggedOver = true; |
|||
} else { |
|||
this.draggedOver = false; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public writeValue(value: string) { |
|||
this.value = Types.isString(value) ? value : ''; |
|||
|
|||
if (this.simplemde) { |
|||
this.simplemde.value(this.value); |
|||
} |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
|
|||
if (this.simplemde) { |
|||
this.simplemde.codemirror.setOption('readOnly', isDisabled); |
|||
} |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.callChange = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.callTouched = fn; |
|||
} |
|||
|
|||
public ngAfterViewInit() { |
|||
const self = this; |
|||
|
|||
this.resourceLoader.loadScript('https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js').then(() => { |
|||
this.simplemde = new SimpleMDE({ |
|||
toolbar: [ |
|||
{ |
|||
name: 'bold', |
|||
action: SimpleMDE.toggleBold, |
|||
className: 'fa fa-bold', |
|||
title: 'Bold' |
|||
}, { |
|||
name: 'italic', |
|||
action: SimpleMDE.toggleItalic, |
|||
className: 'fa fa-italic', |
|||
title: 'Italic' |
|||
}, { |
|||
name: 'heading', |
|||
action: SimpleMDE.toggleHeadingSmaller, |
|||
className: 'fa fa-header', |
|||
title: 'Heading' |
|||
}, { |
|||
name: 'quote', |
|||
action: SimpleMDE.toggleBlockquote, |
|||
className: 'fa fa-quote-left', |
|||
title: 'Quote' |
|||
}, { |
|||
name: 'unordered-list', |
|||
action: SimpleMDE.toggleUnorderedList, |
|||
className: 'fa fa-list-ul', |
|||
title: 'Generic List' |
|||
}, { |
|||
name: 'ordered-list', |
|||
action: SimpleMDE.toggleOrderedList, |
|||
className: 'fa fa-list-ol', |
|||
title: 'Numbered List' |
|||
}, |
|||
'|', |
|||
{ |
|||
name: 'link', |
|||
action: SimpleMDE.drawLink, |
|||
className: 'fa fa-link', |
|||
title: 'Create Link' |
|||
}, { |
|||
name: 'image', |
|||
action: SimpleMDE.drawImage, |
|||
className: 'fa fa-picture-o', |
|||
title: 'Insert Image' |
|||
}, |
|||
'|', |
|||
{ |
|||
name: 'preview', |
|||
action: SimpleMDE.togglePreview, |
|||
className: 'fa fa-eye no-disable', |
|||
title: 'Toggle Preview' |
|||
}, { |
|||
name: 'side-by-side', |
|||
action: SimpleMDE.toggleSideBySide, |
|||
className: 'fa fa-columns no-disable no-mobile', |
|||
title: 'Toggle Side by Side' |
|||
}, |
|||
'|', |
|||
{ |
|||
name: 'guide', |
|||
action: 'https://simplemde.com/markdown-guide', |
|||
className: 'fa fa-question-circle', |
|||
title: 'Markdown Guide' |
|||
}, |
|||
'|', |
|||
{ |
|||
name: 'assets', |
|||
action: () => { |
|||
self.assetPluginClicked.emit(); |
|||
}, |
|||
className: 'icon-assets icon-bold', |
|||
title: 'Insert Assets' |
|||
} |
|||
], |
|||
element: this.editor.nativeElement |
|||
}); |
|||
this.simplemde.value(this.value || ''); |
|||
this.simplemde.codemirror.setOption('readOnly', this.isDisabled); |
|||
|
|||
this.simplemde.codemirror.on('change', () => { |
|||
const value = this.simplemde.value(); |
|||
|
|||
if (this.value !== value) { |
|||
this.value = value; |
|||
|
|||
this.callChange(value); |
|||
} |
|||
}); |
|||
|
|||
this.simplemde.codemirror.on('blur', () => { |
|||
this.callTouched(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public onItemDropped(event: any) { |
|||
const content = event.dragData; |
|||
|
|||
if (content instanceof AssetDto) { |
|||
const img = ``; |
|||
|
|||
this.simplemde.codemirror.replaceSelection(img); |
|||
} |
|||
|
|||
this.draggedOver = false; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue