mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
37 changed files with 5340 additions and 1278 deletions
@ -0,0 +1 @@ |
|||
.remirror-theme{width:100%}.remirror-theme *{box-sizing:border-box}.remirror-editor{min-height:300px!important;max-height:500px}.menu{border:1px solid #dedfe3;border-bottom:0}.MuiStack-root{padding:5px}.MuiStack-root>.MuiBox-root{margin-right:5px}.MuiStack-root>.MuiBox-root>button.MuiButtonBase-root{margin-left:-1px}.MuiBox-root>.MuiButtonBase-root{border-color:#dedfe3!important;border-radius:0}.MuiButtonBase-root.Mui-selected:hover{background-color:#3284f4!important}.remirror-editor img{max-width:300px;max-height:300px}.remirror-editor-wrapper{padding-top:0!important}.custom-icon path{fill:#0000008a}.remirror-theme div.ProseMirror{border:1px solid #dedfe3!important;border-radius:0!important;box-shadow:none!important}.MuiTooltip-popper>div{background-color:#1a2129;border-radius:0;font-size:85%;font-weight:400;padding:.5rem}.squidex-editor-disabled{pointer-events:none}.squidex-editor-floating{border:1px solid #dedfe3}.squidex-editor-floating .MuiBox-root{margin-right:0!important}.squidex-editor-floating input{border:1px solid #dedfe3;border-radius:0;box-sizing:border-box;height:30px;margin-left:0;margin-right:5px;outline:none;padding:6px 12px}.squidex-editor-floating input:active{border-color:#3389ff}.squidex-editor-floating .MuiButtonBase-root{height:30px}.squidex-editor-counter{border:1px solid #dedfe3;border-top:0;font-size:85%;font-weight:400;opacity:.8;padding:4px 10px 4px 4px;text-align:right} |
|||
File diff suppressed because one or more lines are too long
@ -1,68 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script> |
|||
|
|||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script> |
|||
|
|||
<style> |
|||
.editor { |
|||
border: 1px solid #eee; |
|||
border-radius: 4px; |
|||
min-height: 50px; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="editor"> |
|||
<div id="editorjs"></div> |
|||
</div> |
|||
|
|||
<script> |
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var field = new SquidexFormField(); |
|||
|
|||
var editor = new EditorJS({ |
|||
holder: 'editorjs', |
|||
|
|||
tools: { |
|||
header: { |
|||
class: Header, |
|||
inlineToolbar : true |
|||
} |
|||
}, |
|||
|
|||
minHeight: 50, |
|||
|
|||
data: {}, |
|||
|
|||
onReady: function () { |
|||
field.onValueChanged(function (value) { |
|||
if (value) { |
|||
editor.render(value); |
|||
} |
|||
}); |
|||
|
|||
field.onDisabled(function (disabled) { |
|||
editor.readOnly.toggle(disabled); |
|||
}); |
|||
}, |
|||
onChange: function () { |
|||
editor.save().then(function (data) { |
|||
// Notify the UI that the value has been changed. Will be used to trigger validation. |
|||
field.valueChanged(data); |
|||
}); |
|||
} |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,88 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script> |
|||
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> |
|||
<script src="https://unpkg.com/@rjsf/core/dist/react-jsonschema-form.js"></script> |
|||
|
|||
<link rel="stylesheet" type="text/css" href="https://cloud.squidex.io/styles.css"> |
|||
|
|||
<style> |
|||
body { |
|||
background: none; |
|||
} |
|||
|
|||
#root__title { |
|||
display: none; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="editor"></div> |
|||
|
|||
<script> |
|||
const Form = JSONSchemaForm.default; |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
const field = new SquidexFormField(); |
|||
|
|||
function Editor() { |
|||
var stateDisabled = React.useState(false); // Use ES5 language features only. |
|||
var stateValue = React.useState(undefined); |
|||
var stateSchema = React.useState(undefined); |
|||
|
|||
React.useEffect(function () { |
|||
field.onDisabled(function (disabled) { |
|||
stateDisabled[1](disabled); |
|||
}); |
|||
|
|||
field.onValueChanged(function (value) { |
|||
stateValue[1](value); |
|||
}); |
|||
|
|||
if (window.location.hash && window.location.hash.length > 1) { |
|||
fetch(window.location.hash.substr(1)) |
|||
.then(x => x.json()) |
|||
.then(x => stateSchema[1](x)); |
|||
} |
|||
}, []); |
|||
|
|||
const doBlur = React.useCallback(function () { |
|||
field.touched(); |
|||
}, []); |
|||
|
|||
const doChange = React.useCallback(function (editor) { |
|||
if (editor.errors.length === 0) { |
|||
field.valueChanged(editor.formData); |
|||
} |
|||
}, []); |
|||
|
|||
if (!stateSchema[0]) { |
|||
return null; |
|||
} |
|||
|
|||
return ( |
|||
React.createElement(Form, { |
|||
formData: stateValue[0], |
|||
disabled: stateDisabled[0], |
|||
onChange: doChange, |
|||
onBlur: doBlur, |
|||
schema: stateSchema[0], |
|||
children: [] // Hide submit button |
|||
}) |
|||
); |
|||
} |
|||
|
|||
ReactDOM.render(React.createElement(Editor), document.getElementById("editor")); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,77 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
<script type="text/javascript" src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script> |
|||
|
|||
<style> |
|||
.editor { |
|||
border: 1px solid #eee; |
|||
border-radius: 4px; |
|||
height: 500px; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="editor" id="editor"></div> |
|||
|
|||
<script> |
|||
require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@latest/min/vs' }}); |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var field = new SquidexFormField(); |
|||
|
|||
// Before loading vs/editor/editor.main, define a global MonacoEnvironment that overwrites |
|||
// the default worker url location (used when creating WebWorkers). The problem here is that |
|||
// HTML5 does not allow cross-domain web workers, so we need to proxy the instantiation of |
|||
// a web worker through a same-domain script |
|||
window.MonacoEnvironment = { |
|||
getWorkerUrl: function(workerId, label) { |
|||
return `data:text/javascript;charset=utf-8,${encodeURIComponent(` |
|||
self.MonacoEnvironment = { |
|||
baseUrl: 'https://unpkg.com/monaco-editor@latest/min/' |
|||
}; |
|||
importScripts('https://unpkg.com/monaco-editor@latest/min/vs/base/worker/workerMain.js');` |
|||
)}`; |
|||
} |
|||
}; |
|||
|
|||
require(["vs/editor/editor.main"], function () { |
|||
var editor = monaco.editor.create(document.getElementById('editor'), { |
|||
value: `function x() { |
|||
console.log("Hello world!"); |
|||
}`, |
|||
language: 'html', |
|||
theme: 'vs-dark' |
|||
}); |
|||
|
|||
field.onValueChanged(function (value) { |
|||
editor.setValue(value || ''); |
|||
}); |
|||
|
|||
field.onDisabled(function (disabled) { |
|||
editor.updateOptions({ readOnly: disabled }); |
|||
}); |
|||
|
|||
editor.getModel().onDidChangeContent((event) => { |
|||
var data = editor.getValue(); |
|||
|
|||
// Notify the UI that the value has been changed. Will be used to trigger validation. |
|||
field.valueChanged(data); |
|||
}); |
|||
|
|||
window.addEventListener('resize', function () { |
|||
editor.layout(); |
|||
}); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,104 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
<script src="https://cdn.ckeditor.com/ckeditor5/10.0.0/classic/ckeditor.js"></script> |
|||
|
|||
<style> |
|||
.ck-editor__editable { |
|||
min-height: 250px; |
|||
} |
|||
|
|||
.fullscreen { |
|||
height: 100vh; |
|||
} |
|||
|
|||
.fullscreen .ck-editor { |
|||
position: fixed !important; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
z-index: 1000; |
|||
} |
|||
|
|||
.fullscreen .ck-editor .ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline, |
|||
.fullscreen .ck-editor .ck.ck-editor__main { |
|||
height: 100%; |
|||
} |
|||
|
|||
#fullscreenButton { |
|||
position: absolute; |
|||
z-index: 10000; |
|||
right: 10px; |
|||
top: 10px; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<a href="#" id="fullscreenButton">Toggle fullscreen</a> |
|||
|
|||
<textarea name="content" id="editor"></textarea> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
ClassicEditor |
|||
.create(element) |
|||
.catch(error => { |
|||
console.error(error); |
|||
}) |
|||
.then(editor => { |
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var field = new SquidexFormField(); |
|||
|
|||
// Handle the value change event and set the text to the editor. |
|||
field.onValueChanged(function (value) { |
|||
if (value) { |
|||
editor.setData(value); |
|||
} |
|||
}); |
|||
|
|||
// Disable the editor when it should be disabled. |
|||
field.onDisabled(function (disabled) { |
|||
editor.set('isReadOnly', disabled); |
|||
}); |
|||
|
|||
field.onFullscreen(function (fullscreen) { |
|||
if (fullscreen) { |
|||
document.body.classList.add('fullscreen'); |
|||
} else { |
|||
document.body.classList.remove('fullscreen'); |
|||
} |
|||
}); |
|||
|
|||
editor.model.document.on('change', function () { |
|||
var data = editor.getData(); |
|||
|
|||
// Notify the UI that the value has been changed. Will be used to trigger validation. |
|||
field.valueChanged(data); |
|||
}); |
|||
|
|||
editor.ui.focusTracker.on('change:isFocused', function (event, name, isFocused) { |
|||
if (!isFocused) { |
|||
// Notify the UI that the value has been touched. |
|||
field.touched(); |
|||
} |
|||
}); |
|||
|
|||
document.getElementById('fullscreenButton') |
|||
.addEventListener('click', function () { |
|||
field.toggleFullscreen(); |
|||
}); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,116 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js --> |
|||
<script src="editor-sdk.js"></script> |
|||
|
|||
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.0.0/dist/algoliasearch-lite.umd.js" integrity="sha256-MfeKq2Aw9VAkaE9Caes2NOxQf6vUa8Av0JqcUXUGkd0=" crossorigin="anonymous"></script> |
|||
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.0.0/dist/instantsearch.production.min.js" integrity="sha256-6S7q0JJs/Kx4kb/fv0oMjS855QTz5Rc2hh9AkIUjUsk=" crossorigin="anonymous"></script> |
|||
|
|||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.3.1/themes/algolia-min.css" integrity="sha256-HB49n/BZjuqiCtQQf49OdZn63XuKFaxcIHWf0HNKte8=" crossorigin="anonymous"> |
|||
|
|||
<style> |
|||
.container { |
|||
min-height: 400px; |
|||
} |
|||
|
|||
.ais-Hits { |
|||
margin-top: 1rem; |
|||
} |
|||
|
|||
.ais-Hits-item { |
|||
margin-right: 0; |
|||
margin-top: 5px; |
|||
width: 100%; |
|||
} |
|||
|
|||
.button-click { |
|||
margin-top: 5px; |
|||
color: #3389ff; |
|||
cursor: pointer; |
|||
display: inline-block; |
|||
} |
|||
|
|||
.button-click:hover { |
|||
text-decoration: underline; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<script> |
|||
function grow(element) { |
|||
element.style.height = "5px"; |
|||
element.style.height = (element.scrollHeight)+"px"; |
|||
} |
|||
</script> |
|||
|
|||
<div class="container"> |
|||
<div id="searchbox"></div> |
|||
|
|||
<div id="hits"></div> |
|||
</div> |
|||
|
|||
<script> |
|||
var element = document.getElementById('editor'); |
|||
|
|||
// When the field is instantiated it notifies the UI that it has been loaded. |
|||
// |
|||
// Furthermore it sends the current size to the parent. |
|||
var plugin = new SquidexPlugin(); |
|||
|
|||
// Init is called once with a context that contains the app name, schema name and authentication information. |
|||
plugin.onInit(function (context) { |
|||
var searchClient = algoliasearch('CFNTEE51PJ', 'afa3a7605277b85348c6fa160fb5cecc'); |
|||
|
|||
var search = instantsearch({ indexName: 'test', searchClient }); |
|||
|
|||
document.addEventListener('click', event => { |
|||
if (event.target.matches('.button-click')) { |
|||
// We cannot directly navigate to in the iframe because it would only change the URL of the iframe. |
|||
plugin.navigate(`/app/${context.appName}/content/${context.schemaName}`); |
|||
// plugin.navigate(`/app/${context.appName}/content/${context.schemaName}/${id}`); |
|||
} |
|||
}); |
|||
|
|||
search.addWidgets([ |
|||
instantsearch.widgets.searchBox({ |
|||
container: '#searchbox' |
|||
}), |
|||
|
|||
instantsearch.widgets.hits({ |
|||
container: '#hits', |
|||
templates: { |
|||
item(hit, bindEvent) { |
|||
return ` |
|||
<div> |
|||
<div class="hit-name"> |
|||
${instantsearch.highlight({ |
|||
attribute: 'firstname', |
|||
hit |
|||
})} |
|||
${instantsearch.highlight({ |
|||
attribute: 'lastname', |
|||
hit |
|||
})} |
|||
|
|||
<div> |
|||
<a data-object-id="${hit.objectID}" class="button-click">EDIT</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
`; |
|||
} |
|||
} |
|||
}) |
|||
]); |
|||
|
|||
search.start(); |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1,18 +0,0 @@ |
|||
<div #container class="drop-container" |
|||
(sqxDropFile)="insertFiles($event)" |
|||
sqxDropOnlyImages="true"> |
|||
<div #inner [class.fullscreen]="snapshot.isFullscreen"> |
|||
<textarea class="form-control editor" #editor></textarea> |
|||
</div> |
|||
</div> |
|||
|
|||
<sqx-asset-selector *sqxModal="assetsDialog" |
|||
(select)="insertAssets($event)"> |
|||
</sqx-asset-selector> |
|||
|
|||
<sqx-content-selector *sqxModal="contentsDialog" |
|||
[language]="language" |
|||
[languages]="languages" |
|||
[schemaIds]="schemaIds" |
|||
(select)="insertContents($event)"> |
|||
</sqx-content-selector> |
|||
@ -1,18 +0,0 @@ |
|||
@import 'mixins'; |
|||
@import 'vars'; |
|||
|
|||
$background: #fff; |
|||
|
|||
:host ::ng-deep { |
|||
/* stylelint-disable-next-line selector-class-pattern */ |
|||
.CodeMirror { |
|||
height: 300px; |
|||
padding-bottom: 10px; |
|||
padding-top: 10px; |
|||
} |
|||
} |
|||
|
|||
.fullscreen { |
|||
@include fixed(0, 0, 0, 0); |
|||
z-index: 1040; |
|||
} |
|||
@ -1,363 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, Renderer2, ViewChild } from '@angular/core'; |
|||
import { NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
import { marked } from 'marked'; |
|||
import { ApiUrlConfig, AssetDto, AssetUploaderState, ContentDto, DialogModel, getContentValue, LanguageDto, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal'; |
|||
|
|||
declare const SimpleMDE: any; |
|||
|
|||
export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true, |
|||
}; |
|||
|
|||
interface State { |
|||
// True, when the editor is shown as fullscreen.
|
|||
isFullscreen: boolean; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'sqx-markdown-editor', |
|||
styleUrls: ['./markdown-editor.component.scss'], |
|||
templateUrl: './markdown-editor.component.html', |
|||
providers: [ |
|||
SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR, |
|||
], |
|||
changeDetection: ChangeDetectionStrategy.OnPush, |
|||
}) |
|||
export class MarkdownEditorComponent extends StatefulControlComponent<State, string> implements AfterViewInit { |
|||
private simplemde: any; |
|||
private value?: string; |
|||
|
|||
@Input() |
|||
public schemaIds?: ReadonlyArray<string>; |
|||
|
|||
@Input() |
|||
public language!: LanguageDto; |
|||
|
|||
@Input() |
|||
public languages!: ReadonlyArray<LanguageDto>; |
|||
|
|||
@Input() |
|||
public folderId?: string; |
|||
|
|||
@Input({ transform: booleanAttribute }) |
|||
public set disabled(value: boolean | undefined | null) { |
|||
this.setDisabledState(value === true); |
|||
} |
|||
|
|||
@ViewChild('editor', { static: false }) |
|||
public editor!: ElementRef; |
|||
|
|||
@ViewChild('container', { static: false }) |
|||
public container!: ElementRef; |
|||
|
|||
@ViewChild('inner', { static: false }) |
|||
public inner!: ElementRef; |
|||
|
|||
public assetsDialog = new DialogModel(); |
|||
|
|||
public contentsDialog = new DialogModel(); |
|||
|
|||
constructor( |
|||
private readonly apiUrl: ApiUrlConfig, |
|||
private readonly assetUploader: AssetUploaderState, |
|||
private readonly renderer: Renderer2, |
|||
private readonly resourceLoader: ResourceLoaderService, |
|||
) { |
|||
super({ isFullscreen: false }); |
|||
} |
|||
|
|||
public writeValue(obj: any) { |
|||
const newValue = Types.isString(obj) ? obj : ''; |
|||
|
|||
if (newValue === this.value) { |
|||
return; |
|||
} |
|||
|
|||
this.value = newValue; |
|||
|
|||
if (this.simplemde) { |
|||
this.simplemde.value(this.value); |
|||
} |
|||
} |
|||
|
|||
public onDisabled(isDisabled: boolean) { |
|||
if (this.simplemde) { |
|||
this.simplemde.codemirror.setOption('readOnly', isDisabled); |
|||
} |
|||
} |
|||
|
|||
private showAssetSelector = () => { |
|||
if (this.snapshot.isDisabled) { |
|||
return; |
|||
} |
|||
|
|||
this.assetsDialog.show(); |
|||
}; |
|||
|
|||
private showContentsSelector = () => { |
|||
if (this.snapshot.isDisabled) { |
|||
return; |
|||
} |
|||
|
|||
this.contentsDialog.show(); |
|||
}; |
|||
|
|||
public ngAfterViewInit() { |
|||
Promise.all([ |
|||
this.resourceLoader.loadLocalStyle('dependencies/simplemde/simplemde.min.css'), |
|||
this.resourceLoader.loadLocalStyle('dependencies/font-awesome/css/font-awesome.min.css'), |
|||
this.resourceLoader.loadLocalScript('dependencies/simplemde/simplemde.min.js'), |
|||
]).then(() => { |
|||
const options = { |
|||
previewRender: (text: string) => { |
|||
return marked(text, { pedantic: true, mangle: false, headerIds: false }); |
|||
}, |
|||
autoDownloadFontAwesome: true, |
|||
spellChecker: false, |
|||
status: ['lines', 'words', 'cursor'], |
|||
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: 'fullscreen', |
|||
action: SimpleMDE.toggleFullScreen, |
|||
className: 'fa fa-arrows-alt no-disable no-mobile', |
|||
title: 'Toggle Fullscreen', |
|||
}, { |
|||
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: this.showAssetSelector, |
|||
className: 'icon-assets icon-bold', |
|||
title: 'Insert Assets', |
|||
}, |
|||
], |
|||
element: this.editor.nativeElement, |
|||
}; |
|||
|
|||
if (this.schemaIds && this.schemaIds.length > 0) { |
|||
options.toolbar.push({ |
|||
name: 'contents', |
|||
action: this.showContentsSelector, |
|||
className: 'icon-contents icon-bold', |
|||
title: 'Insert Contents', |
|||
}); |
|||
} |
|||
|
|||
this.simplemde = new SimpleMDE(options); |
|||
this.simplemde.value(this.value || ''); |
|||
this.simplemde.codemirror.setOption('readOnly', this.snapshot.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('refresh', () => { |
|||
const isFullscreen = this.simplemde.isFullscreenActive(); |
|||
|
|||
this.toggleFullscreen(isFullscreen); |
|||
}); |
|||
|
|||
this.simplemde.codemirror.on('blur', () => { |
|||
this.callTouched(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public insertAssets(assets: ReadonlyArray<AssetDto>) { |
|||
const content = this.buildAssetsMarkup(assets); |
|||
|
|||
if (content.length > 0) { |
|||
this.simplemde.codemirror.replaceSelection(content); |
|||
} |
|||
|
|||
this.assetsDialog.hide(); |
|||
} |
|||
|
|||
public insertContents(contents: ReadonlyArray<ContentDto>) { |
|||
const content = this.buildContentsMarkup(contents); |
|||
|
|||
if (content.length > 0) { |
|||
this.simplemde.codemirror.replaceSelection(content); |
|||
} |
|||
|
|||
this.contentsDialog.hide(); |
|||
} |
|||
|
|||
public insertFiles(files: ReadonlyArray<File>) { |
|||
const doc = this.simplemde.codemirror.getDoc(); |
|||
|
|||
for (const file of files) { |
|||
this.uploadFile(doc, file); |
|||
} |
|||
} |
|||
|
|||
private uploadFile(doc: any, file: File) { |
|||
if (this.snapshot.isDisabled) { |
|||
return; |
|||
} |
|||
|
|||
const uploadCursor = doc.getCursor(); |
|||
const uploadText = `![Uploading file...${new Date()}]()`; |
|||
|
|||
doc.replaceSelection(uploadText); |
|||
|
|||
const replaceText = (replacement: string) => { |
|||
const cursor = doc.getCursor(); |
|||
|
|||
const text = doc.getValue().replace(uploadText, replacement); |
|||
|
|||
doc.setValue(text); |
|||
|
|||
if (uploadCursor && uploadCursor.line === cursor.line) { |
|||
const offset = replacement.length - uploadText.length; |
|||
|
|||
doc.setCursor({ line: cursor.line, ch: cursor.ch + offset }); |
|||
} else { |
|||
doc.setCursor(cursor); |
|||
} |
|||
}; |
|||
|
|||
this.assetUploader.uploadFile(file, this.folderId) |
|||
.subscribe({ |
|||
next: asset => { |
|||
if (Types.is(asset, AssetDto)) { |
|||
replaceText(this.buildAssetMarkup(asset)); |
|||
} |
|||
}, |
|||
error: error => { |
|||
if (!Types.is(error, UploadCanceled)) { |
|||
replaceText('FAILED'); |
|||
} |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
private toggleFullscreen(isFullscreen: boolean) { |
|||
let target = this.container.nativeElement; |
|||
|
|||
if (isFullscreen) { |
|||
target = document.body; |
|||
} |
|||
|
|||
this.renderer.appendChild(target, this.inner.nativeElement); |
|||
|
|||
this.next({ isFullscreen }); |
|||
} |
|||
|
|||
private buildAssetsMarkup(assets: ReadonlyArray<AssetDto>) { |
|||
let markup = ''; |
|||
|
|||
for (const asset of assets) { |
|||
markup += this.buildAssetMarkup(asset); |
|||
} |
|||
|
|||
return markup; |
|||
} |
|||
|
|||
private buildContentsMarkup(contents: ReadonlyArray<ContentDto>) { |
|||
let markup = ''; |
|||
|
|||
for (const content of contents) { |
|||
markup += this.buildContentMarkup(content); |
|||
} |
|||
|
|||
return markup; |
|||
} |
|||
|
|||
private buildContentMarkup(content: ContentDto) { |
|||
const name = |
|||
content.referenceFields |
|||
.map(f => getContentValue(content, this.language, f, false)) |
|||
.map(v => v.formatted) |
|||
.defined() |
|||
.join(', ') |
|||
|| 'content'; |
|||
|
|||
return `[${name}](${this.apiUrl.buildUrl(content._links['self'].href)})`; |
|||
} |
|||
|
|||
private buildAssetMarkup(asset: AssetDto) { |
|||
const name = asset.fileNameWithoutExtension; |
|||
|
|||
if (asset.type === 'Image' || asset.mimeType === 'image/svg+xml' || asset.fileName.endsWith('.svg')) { |
|||
return `} '${name}')`; |
|||
} else if (asset.type === 'Video') { |
|||
return `[${name}](${asset.fullUrl(this.apiUrl)})`; |
|||
} else { |
|||
return `[${name}](${asset.fullUrl(this.apiUrl)})`; |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +1,2 @@ |
|||
@import 'mixins'; |
|||
@import 'vars'; |
|||
|
|||
.drop-container { |
|||
min-height: 401px; |
|||
} |
|||
|
|||
.editor { |
|||
height: 400px; |
|||
} |
|||
@import 'vars'; |
|||
Loading…
Reference in new issue