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 'mixins'; |
||||
@import 'vars'; |
@import 'vars'; |
||||
|
|
||||
.drop-container { |
|
||||
min-height: 401px; |
|
||||
} |
|
||||
|
|
||||
.editor { |
|
||||
height: 400px; |
|
||||
} |
|
||||
Loading…
Reference in new issue