mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-25 00:00:00 +08:00
Moved overlay component, migrated code-editor & added features
- Moved Code-editor from vue to component. - Updated popup code so it background click only hides if the click originated on the same background. Clicks within the popup will no longer cause it to hide. - Added session-level history tracking to code editor.
This commit is contained in:
117
resources/js/components/code-editor.js
Normal file
117
resources/js/components/code-editor.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import Code from "../services/code";
|
||||||
|
import {onChildEvent, onEnterPress, onSelect} from "../services/dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code Editor
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
class CodeEditor {
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.container = this.$refs.container;
|
||||||
|
this.popup = this.$el;
|
||||||
|
this.editorInput = this.$refs.editor;
|
||||||
|
this.languageLinks = this.$manyRefs.languageLink;
|
||||||
|
this.saveButton = this.$refs.saveButton;
|
||||||
|
this.languageInput = this.$refs.languageInput;
|
||||||
|
this.historyDropDown = this.$refs.historyDropDown;
|
||||||
|
this.historyList = this.$refs.historyList;
|
||||||
|
|
||||||
|
this.callback = null;
|
||||||
|
this.editor = null;
|
||||||
|
this.history = {};
|
||||||
|
this.historyKey = 'code_history';
|
||||||
|
this.setupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupListeners() {
|
||||||
|
this.container.addEventListener('keydown', event => {
|
||||||
|
if (event.ctrlKey && event.key === 'Enter') {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onSelect(this.languageLinks, event => {
|
||||||
|
const language = event.target.dataset.lang;
|
||||||
|
this.languageInput.value = language;
|
||||||
|
this.updateEditorMode(language);
|
||||||
|
});
|
||||||
|
|
||||||
|
onEnterPress(this.languageInput, e => this.save());
|
||||||
|
onSelect(this.saveButton, e => this.save());
|
||||||
|
|
||||||
|
onChildEvent(this.historyList, 'button', 'click', (event, elem) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const historyTime = elem.dataset.time;
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setValue(this.history[historyTime]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
if (this.callback) {
|
||||||
|
this.callback(this.editor.getValue(), this.languageInput.value);
|
||||||
|
}
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
open(code, language, callback) {
|
||||||
|
this.languageInput.value = language;
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
this.updateEditorMode(language);
|
||||||
|
|
||||||
|
Code.setContent(this.editor, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
if (!this.editor) {
|
||||||
|
this.editor = Code.popupEditor(this.editorInput, this.languageInput.value);
|
||||||
|
}
|
||||||
|
this.loadHistory();
|
||||||
|
this.popup.components.popup.show(() => {
|
||||||
|
Code.updateLayout(this.editor);
|
||||||
|
this.editor.focus();
|
||||||
|
}, () => {
|
||||||
|
this.addHistory()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.popup.components.popup.hide();
|
||||||
|
this.addHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEditorMode(language) {
|
||||||
|
Code.setMode(this.editor, language, this.editor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
loadHistory() {
|
||||||
|
this.history = JSON.parse(window.sessionStorage.getItem(this.historyKey) || '{}');
|
||||||
|
const historyKeys = Object.keys(this.history).reverse();
|
||||||
|
this.historyDropDown.classList.toggle('hidden', historyKeys.length === 0);
|
||||||
|
this.historyList.innerHTML = historyKeys.map(key => {
|
||||||
|
const localTime = (new Date(parseInt(key))).toLocaleTimeString();
|
||||||
|
return `<li><button type="button" data-time="${key}">${localTime}</button></li>`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
addHistory() {
|
||||||
|
if (!this.editor) return;
|
||||||
|
const code = this.editor.getValue();
|
||||||
|
if (!code) return;
|
||||||
|
|
||||||
|
// Stop if we'd be storing the same as the last item
|
||||||
|
const lastHistoryKey = Object.keys(this.history).pop();
|
||||||
|
if (this.history[lastHistoryKey] === code) return;
|
||||||
|
|
||||||
|
this.history[String(Date.now())] = code;
|
||||||
|
const historyString = JSON.stringify(this.history);
|
||||||
|
window.sessionStorage.setItem(this.historyKey, historyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CodeEditor;
|
@ -1,27 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Entity Selector Popup
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
class EntitySelectorPopup {
|
class EntitySelectorPopup {
|
||||||
|
|
||||||
constructor(elem) {
|
setup() {
|
||||||
this.elem = elem;
|
this.elem = this.$el;
|
||||||
|
this.selectButton = this.$refs.select;
|
||||||
window.EntitySelectorPopup = this;
|
window.EntitySelectorPopup = this;
|
||||||
|
|
||||||
this.callback = null;
|
this.callback = null;
|
||||||
this.selection = null;
|
this.selection = null;
|
||||||
|
|
||||||
this.selectButton = elem.querySelector('.entity-link-selector-confirm');
|
|
||||||
this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
|
this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
|
||||||
|
|
||||||
window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
|
window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
|
||||||
window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
|
window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
show(callback) {
|
show(callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.elem.components.overlay.show();
|
this.elem.components.popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.elem.components.overlay.hide();
|
this.elem.components.popup.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectButtonClick() {
|
onSelectButtonClick() {
|
||||||
|
@ -36,7 +36,9 @@ function initComponent(name, element) {
|
|||||||
try {
|
try {
|
||||||
instance = new componentModel(element);
|
instance = new componentModel(element);
|
||||||
instance.$el = element;
|
instance.$el = element;
|
||||||
instance.$refs = parseRefs(name, element);
|
const allRefs = parseRefs(name, element);
|
||||||
|
instance.$refs = allRefs.refs;
|
||||||
|
instance.$manyRefs = allRefs.manyRefs;
|
||||||
instance.$opts = parseOpts(name, element);
|
instance.$opts = parseOpts(name, element);
|
||||||
if (typeof instance.setup === 'function') {
|
if (typeof instance.setup === 'function') {
|
||||||
instance.setup();
|
instance.setup();
|
||||||
@ -67,6 +69,7 @@ function initComponent(name, element) {
|
|||||||
*/
|
*/
|
||||||
function parseRefs(name, element) {
|
function parseRefs(name, element) {
|
||||||
const refs = {};
|
const refs = {};
|
||||||
|
const manyRefs = {};
|
||||||
const prefix = `${name}@`
|
const prefix = `${name}@`
|
||||||
const refElems = element.querySelectorAll(`[refs*="${prefix}"]`);
|
const refElems = element.querySelectorAll(`[refs*="${prefix}"]`);
|
||||||
for (const el of refElems) {
|
for (const el of refElems) {
|
||||||
@ -76,9 +79,13 @@ function parseRefs(name, element) {
|
|||||||
.map(str => str.replace(prefix, ''));
|
.map(str => str.replace(prefix, ''));
|
||||||
for (const ref of refNames) {
|
for (const ref of refNames) {
|
||||||
refs[ref] = el;
|
refs[ref] = el;
|
||||||
|
if (typeof manyRefs[ref] === 'undefined') {
|
||||||
|
manyRefs[ref] = [];
|
||||||
|
}
|
||||||
|
manyRefs[ref].push(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return refs;
|
return {refs, manyRefs};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,6 +141,7 @@ function initAll(parentElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.components.init = initAll;
|
window.components.init = initAll;
|
||||||
|
window.components.first = (name) => (window.components[name] || [null])[0];
|
||||||
|
|
||||||
export default initAll;
|
export default initAll;
|
||||||
|
|
||||||
@ -141,5 +149,6 @@ export default initAll;
|
|||||||
* @typedef Component
|
* @typedef Component
|
||||||
* @property {HTMLElement} $el
|
* @property {HTMLElement} $el
|
||||||
* @property {Object<String, HTMLElement>} $refs
|
* @property {Object<String, HTMLElement>} $refs
|
||||||
|
* @property {Object<String, HTMLElement[]>} $manyRefs
|
||||||
* @property {Object<String, String>} $opts
|
* @property {Object<String, String>} $opts
|
||||||
*/
|
*/
|
@ -1,43 +0,0 @@
|
|||||||
import {fadeIn, fadeOut} from "../services/animations";
|
|
||||||
|
|
||||||
class Overlay {
|
|
||||||
|
|
||||||
constructor(elem) {
|
|
||||||
this.container = elem;
|
|
||||||
elem.addEventListener('click', event => {
|
|
||||||
if (event.target === elem) return this.hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('keyup', event => {
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let closeButtons = elem.querySelectorAll('.popup-header-close');
|
|
||||||
for (let i=0; i < closeButtons.length; i++) {
|
|
||||||
closeButtons[i].addEventListener('click', this.hide.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hide(onComplete = null) { this.toggle(false, onComplete); }
|
|
||||||
show(onComplete = null) { this.toggle(true, onComplete); }
|
|
||||||
|
|
||||||
toggle(show = true, onComplete) {
|
|
||||||
if (show) {
|
|
||||||
fadeIn(this.container, 240, onComplete);
|
|
||||||
} else {
|
|
||||||
fadeOut(this.container, 240, onComplete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
focusOnBody() {
|
|
||||||
const body = this.container.querySelector('.popup-body');
|
|
||||||
if (body) {
|
|
||||||
body.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Overlay;
|
|
61
resources/js/components/popup.js
Normal file
61
resources/js/components/popup.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {fadeIn, fadeOut} from "../services/animations";
|
||||||
|
import {onSelect} from "../services/dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup window that will contain other content.
|
||||||
|
* This component provides the show/hide functionality
|
||||||
|
* with the ability for popup@hide child references to close this.
|
||||||
|
* @extends {Component}
|
||||||
|
*/
|
||||||
|
class Popup {
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.container = this.$el;
|
||||||
|
this.hideButtons = this.$manyRefs.hide || [];
|
||||||
|
|
||||||
|
this.onkeyup = null;
|
||||||
|
this.onHide = null;
|
||||||
|
this.setupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupListeners() {
|
||||||
|
let lastMouseDownTarget = null;
|
||||||
|
this.container.addEventListener('mousedown', event => {
|
||||||
|
lastMouseDownTarget = event.target;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.addEventListener('click', event => {
|
||||||
|
if (event.target === this.container && lastMouseDownTarget === this.container) {
|
||||||
|
return this.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onSelect(this.hideButtons, e => this.hide());
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(onComplete = null) {
|
||||||
|
fadeOut(this.container, 240, onComplete);
|
||||||
|
if (this.onkeyup) {
|
||||||
|
window.removeEventListener('keyup', this.onkeyup);
|
||||||
|
this.onkeyup = null;
|
||||||
|
}
|
||||||
|
if (this.onHide) {
|
||||||
|
this.onHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(onComplete = null, onHide = null) {
|
||||||
|
fadeIn(this.container, 240, onComplete);
|
||||||
|
|
||||||
|
this.onkeyup = (event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('keyup', this.onkeyup);
|
||||||
|
this.onHide = onHide;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Popup;
|
@ -137,7 +137,7 @@ function codePlugin() {
|
|||||||
|
|
||||||
if (!elemIsCodeBlock(selectedNode)) {
|
if (!elemIsCodeBlock(selectedNode)) {
|
||||||
const providedCode = editor.selection.getNode().textContent;
|
const providedCode = editor.selection.getNode().textContent;
|
||||||
window.vues['code-editor'].open(providedCode, '', (code, lang) => {
|
window.components.first('code-editor').open(providedCode, '', (code, lang) => {
|
||||||
const wrap = document.createElement('div');
|
const wrap = document.createElement('div');
|
||||||
wrap.innerHTML = `<pre><code class="language-${lang}"></code></pre>`;
|
wrap.innerHTML = `<pre><code class="language-${lang}"></code></pre>`;
|
||||||
wrap.querySelector('code').innerText = code;
|
wrap.querySelector('code').innerText = code;
|
||||||
@ -155,7 +155,7 @@ function codePlugin() {
|
|||||||
let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
|
let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
|
||||||
let currentCode = selectedNode.querySelector('textarea').textContent;
|
let currentCode = selectedNode.querySelector('textarea').textContent;
|
||||||
|
|
||||||
window.vues['code-editor'].open(currentCode, lang, (code, lang) => {
|
window.components.first('code-editor').open(currentCode, lang, (code, lang) => {
|
||||||
const editorElem = selectedNode.querySelector('.CodeMirror');
|
const editorElem = selectedNode.querySelector('.CodeMirror');
|
||||||
const cmInstance = editorElem.CodeMirror;
|
const cmInstance = editorElem.CodeMirror;
|
||||||
if (cmInstance) {
|
if (cmInstance) {
|
||||||
|
@ -25,10 +25,15 @@ export function onEvents(listenerElement, events, callback) {
|
|||||||
/**
|
/**
|
||||||
* Helper to run an action when an element is selected.
|
* Helper to run an action when an element is selected.
|
||||||
* A "select" is made to be accessible, So can be a click, space-press or enter-press.
|
* A "select" is made to be accessible, So can be a click, space-press or enter-press.
|
||||||
* @param listenerElement
|
* @param {HTMLElement|Array} elements
|
||||||
* @param callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
export function onSelect(listenerElement, callback) {
|
export function onSelect(elements, callback) {
|
||||||
|
if (!Array.isArray(elements)) {
|
||||||
|
elements = [elements];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const listenerElement of elements) {
|
||||||
listenerElement.addEventListener('click', callback);
|
listenerElement.addEventListener('click', callback);
|
||||||
listenerElement.addEventListener('keydown', (event) => {
|
listenerElement.addEventListener('keydown', (event) => {
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
@ -36,6 +41,18 @@ export function onSelect(listenerElement, callback) {
|
|||||||
callback(event);
|
callback(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to enter press on the given element(s).
|
||||||
|
* @param {HTMLElement|Array} elements
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function onEnterPress(elements, callback) {
|
||||||
|
if (!Array.isArray(elements)) {
|
||||||
|
elements = [elements];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import codeLib from "../services/code";
|
|
||||||
|
|
||||||
const methods = {
|
|
||||||
show() {
|
|
||||||
if (!this.editor) this.editor = codeLib.popupEditor(this.$refs.editor, this.language);
|
|
||||||
this.$refs.overlay.components.overlay.show(() => {
|
|
||||||
codeLib.updateLayout(this.editor);
|
|
||||||
this.editor.focus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
hide() {
|
|
||||||
this.$refs.overlay.components.overlay.hide();
|
|
||||||
},
|
|
||||||
updateEditorMode(language) {
|
|
||||||
codeLib.setMode(this.editor, language, this.editor.getValue());
|
|
||||||
},
|
|
||||||
updateLanguage(lang) {
|
|
||||||
this.language = lang;
|
|
||||||
this.updateEditorMode(lang);
|
|
||||||
},
|
|
||||||
open(code, language, callback) {
|
|
||||||
this.show();
|
|
||||||
this.updateEditorMode(language);
|
|
||||||
this.language = language;
|
|
||||||
codeLib.setContent(this.editor, code);
|
|
||||||
this.code = code;
|
|
||||||
this.callback = callback;
|
|
||||||
},
|
|
||||||
save() {
|
|
||||||
if (!this.callback) return;
|
|
||||||
this.callback(this.editor.getValue(), this.language);
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
editor: null,
|
|
||||||
language: '',
|
|
||||||
code: '',
|
|
||||||
callback: null
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
methods,
|
|
||||||
data
|
|
||||||
};
|
|
@ -35,7 +35,7 @@ const methods = {
|
|||||||
show(providedCallback, imageType = null) {
|
show(providedCallback, imageType = null) {
|
||||||
callback = providedCallback;
|
callback = providedCallback;
|
||||||
this.showing = true;
|
this.showing = true;
|
||||||
this.$el.children[0].components.overlay.show();
|
this.$el.children[0].components.popup.show();
|
||||||
|
|
||||||
// Get initial images if they have not yet been loaded in.
|
// Get initial images if they have not yet been loaded in.
|
||||||
if (dataLoaded && imageType === this.imageType) return;
|
if (dataLoaded && imageType === this.imageType) return;
|
||||||
@ -53,7 +53,7 @@ const methods = {
|
|||||||
}
|
}
|
||||||
this.showing = false;
|
this.showing = false;
|
||||||
this.selectedImage = false;
|
this.selectedImage = false;
|
||||||
this.$el.children[0].components.overlay.hide();
|
this.$el.children[0].components.popup.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
|
@ -5,7 +5,6 @@ function exists(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import entityDashboard from "./entity-dashboard";
|
import entityDashboard from "./entity-dashboard";
|
||||||
import codeEditor from "./code-editor";
|
|
||||||
import imageManager from "./image-manager";
|
import imageManager from "./image-manager";
|
||||||
import tagManager from "./tag-manager";
|
import tagManager from "./tag-manager";
|
||||||
import attachmentManager from "./attachment-manager";
|
import attachmentManager from "./attachment-manager";
|
||||||
@ -13,7 +12,6 @@ import pageEditor from "./page-editor";
|
|||||||
|
|
||||||
let vueMapping = {
|
let vueMapping = {
|
||||||
'entity-dashboard': entityDashboard,
|
'entity-dashboard': entityDashboard,
|
||||||
'code-editor': codeEditor,
|
|
||||||
'image-manager': imageManager,
|
'image-manager': imageManager,
|
||||||
'tag-manager': tagManager,
|
'tag-manager': tagManager,
|
||||||
'attachment-manager': attachmentManager,
|
'attachment-manager': attachmentManager,
|
||||||
|
@ -29,5 +29,6 @@ return [
|
|||||||
'code_editor' => 'Edit Code',
|
'code_editor' => 'Edit Code',
|
||||||
'code_language' => 'Code Language',
|
'code_language' => 'Code Language',
|
||||||
'code_content' => 'Code Content',
|
'code_content' => 'Code Content',
|
||||||
|
'code_session_history' => 'Session History',
|
||||||
'code_save' => 'Save Code',
|
'code_save' => 'Save Code',
|
||||||
];
|
];
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[overlay] {
|
[overlay], .popup-background {
|
||||||
@include lightDark(background-color, rgba(0, 0, 0, 0.333), rgba(0, 0, 0, 0.6));
|
@include lightDark(background-color, rgba(0, 0, 0, 0.333), rgba(0, 0, 0, 0.6));
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 95536;
|
z-index: 95536;
|
||||||
@ -611,11 +611,11 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#code-editor .CodeMirror {
|
.code-editor .CodeMirror {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#code-editor .lang-options {
|
.code-editor .lang-options {
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
margin-bottom: $-s;
|
margin-bottom: $-s;
|
||||||
a {
|
a {
|
||||||
@ -625,10 +625,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include smaller-than($m) {
|
@include smaller-than($m) {
|
||||||
#code-editor .lang-options {
|
.code-editor .lang-options {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
#code-editor .CodeMirror {
|
.code-editor .CodeMirror {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,9 @@
|
|||||||
&.v-center {
|
&.v-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
&.v-end {
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
&.no-gap {
|
&.no-gap {
|
||||||
grid-row-gap: 0;
|
grid-row-gap: 0;
|
||||||
grid-column-gap: 0;
|
grid-column-gap: 0;
|
||||||
|
@ -569,6 +569,8 @@ ul.pagination {
|
|||||||
@include lightDark(color, #555, #eee);
|
@include lightDark(color, #555, #eee);
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
text-align: start !important;
|
text-align: start !important;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
&.wide {
|
&.wide {
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div id="code-editor">
|
<div>
|
||||||
<div overlay ref="overlay" v-cloak @click="hide()">
|
<div components="popup code-editor" class="popup-background code-editor">
|
||||||
<div class="popup-body" tabindex="-1" @click.stop @keydown.enter.ctrl="save">
|
<div refs="code-editor@container" class="popup-body" tabindex="-1">
|
||||||
|
|
||||||
<div class="popup-header primary-background">
|
<div class="popup-header primary-background">
|
||||||
<div class="popup-title">{{ trans('components.code_editor') }}</div>
|
<div class="popup-title">{{ trans('components.code_editor') }}</div>
|
||||||
<button class="popup-header-close" @click="hide()">x</button>
|
<button class="popup-header-close" refs="popup@hide">x</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-l popup-content">
|
<div class="p-l popup-content">
|
||||||
@ -12,42 +12,54 @@
|
|||||||
<label for="code-editor-language">{{ trans('components.code_language') }}</label>
|
<label for="code-editor-language">{{ trans('components.code_language') }}</label>
|
||||||
<div class="lang-options">
|
<div class="lang-options">
|
||||||
<small>
|
<small>
|
||||||
<a @click="updateLanguage('CSS')">CSS</a>
|
<a refs="code-editor@languageLink" data-lang="CSS">CSS</a>
|
||||||
<a @click="updateLanguage('C')">C</a>
|
<a refs="code-editor@languageLink" data-lang="C">C</a>
|
||||||
<a @click="updateLanguage('C++')">C++</a>
|
<a refs="code-editor@languageLink" data-lang="C++">C++</a>
|
||||||
<a @click="updateLanguage('C#')">C#</a>
|
<a refs="code-editor@languageLink" data-lang="C#">C#</a>
|
||||||
<a @click="updateLanguage('Fortran')">Fortran</a>
|
<a refs="code-editor@languageLink" data-lang="Fortran">Fortran</a>
|
||||||
<a @click="updateLanguage('Go')">Go</a>
|
<a refs="code-editor@languageLink" data-lang="Go">Go</a>
|
||||||
<a @click="updateLanguage('HTML')">HTML</a>
|
<a refs="code-editor@languageLink" data-lang="HTML">HTML</a>
|
||||||
<a @click="updateLanguage('INI')">INI</a>
|
<a refs="code-editor@languageLink" data-lang="INI">INI</a>
|
||||||
<a @click="updateLanguage('Java')">Java</a>
|
<a refs="code-editor@languageLink" data-lang="Java">Java</a>
|
||||||
<a @click="updateLanguage('JavaScript')">JavaScript</a>
|
<a refs="code-editor@languageLink" data-lang="JavaScript">JavaScript</a>
|
||||||
<a @click="updateLanguage('JSON')">JSON</a>
|
<a refs="code-editor@languageLink" data-lang="JSON">JSON</a>
|
||||||
<a @click="updateLanguage('Lua')">Lua</a>
|
<a refs="code-editor@languageLink" data-lang="Lua">Lua</a>
|
||||||
<a @click="updateLanguage('MarkDown')">MarkDown</a>
|
<a refs="code-editor@languageLink" data-lang="MarkDown">MarkDown</a>
|
||||||
<a @click="updateLanguage('Nginx')">Nginx</a>
|
<a refs="code-editor@languageLink" data-lang="Nginx">Nginx</a>
|
||||||
<a @click="updateLanguage('PASCAL')">Pascal</a>
|
<a refs="code-editor@languageLink" data-lang="PASCAL">Pascal</a>
|
||||||
<a @click="updateLanguage('Perl')">Perl</a>
|
<a refs="code-editor@languageLink" data-lang="Perl">Perl</a>
|
||||||
<a @click="updateLanguage('PHP')">PHP</a>
|
<a refs="code-editor@languageLink" data-lang="PHP">PHP</a>
|
||||||
<a @click="updateLanguage('Powershell')">Powershell</a>
|
<a refs="code-editor@languageLink" data-lang="Powershell">Powershell</a>
|
||||||
<a @click="updateLanguage('Python')">Python</a>
|
<a refs="code-editor@languageLink" data-lang="Python">Python</a>
|
||||||
<a @click="updateLanguage('Ruby')">Ruby</a>
|
<a refs="code-editor@languageLink" data-lang="Ruby">Ruby</a>
|
||||||
<a @click="updateLanguage('shell')">Shell/Bash</a>
|
<a refs="code-editor@languageLink" data-lang="shell">Shell/Bash</a>
|
||||||
<a @click="updateLanguage('SQL')">SQL</a>
|
<a refs="code-editor@languageLink" data-lang="SQL">SQL</a>
|
||||||
<a @click="updateLanguage('XML')">XML</a>
|
<a refs="code-editor@languageLink" data-lang="XML">XML</a>
|
||||||
<a @click="updateLanguage('YAML')">YAML</a>
|
<a refs="code-editor@languageLink" data-lang="YAML">YAML</a>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<input @keypress.enter="save()" id="code-editor-language" type="text" @input="updateEditorMode(language)" v-model="language">
|
<input refs="code-editor@languageInput" id="code-editor-language" type="text">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div class="grid half no-break v-end mb-xs">
|
||||||
|
<div>
|
||||||
<label for="code-editor-content">{{ trans('components.code_content') }}</label>
|
<label for="code-editor-content">{{ trans('components.code_content') }}</label>
|
||||||
<textarea ref="editor" v-model="code"></textarea>
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div component="dropdown" refs="code-editor@historyDropDown" class="inline block">
|
||||||
|
<button refs="dropdown@toggle" class="text-button text-small">@icon('history') {{ trans('components.code_session_history') }}</button>
|
||||||
|
<ul refs="dropdown@menu code-editor@historyList" class="dropdown-menu"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<textarea refs="code-editor@editor"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="button" class="button" @click="save()">{{ trans('components.code_save') }}</button>
|
<button refs="code-editor@saveButton" type="button" class="button">{{ trans('components.code_save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<div id="entity-selector-wrap">
|
<div id="entity-selector-wrap">
|
||||||
<div overlay entity-selector-popup>
|
<div components="popup entity-selector-popup" class="popup-background">
|
||||||
<div class="popup-body small" tabindex="-1">
|
<div class="popup-body small" tabindex="-1">
|
||||||
<div class="popup-header primary-background">
|
<div class="popup-header primary-background">
|
||||||
<div class="popup-title">{{ trans('entities.entity_select') }}</div>
|
<div class="popup-title">{{ trans('entities.entity_select') }}</div>
|
||||||
<button type="button" class="popup-header-close">x</button>
|
<button refs="popup@hide" type="button" class="popup-header-close">x</button>
|
||||||
</div>
|
</div>
|
||||||
@include('components.entity-selector', ['name' => 'entity-selector'])
|
@include('components.entity-selector', ['name' => 'entity-selector'])
|
||||||
<div class="popup-footer">
|
<div class="popup-footer">
|
||||||
<button type="button" disabled="true" class="button entity-link-selector-confirm corner-button">{{ trans('common.select') }}</button>
|
<button refs="entity-selector-popup@select" type="button" disabled="true" class="button corner-button">{{ trans('common.select') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
'components.file_upload_timeout',
|
'components.file_upload_timeout',
|
||||||
])
|
])
|
||||||
|
|
||||||
<div overlay v-cloak @click="hide">
|
<div component="popup" class="popup-background" v-cloak @click="hide">
|
||||||
<div class="popup-body" tabindex="-1" @click.stop>
|
<div class="popup-body" tabindex="-1" @click.stop>
|
||||||
|
|
||||||
<div class="popup-header primary-background">
|
<div class="popup-header primary-background">
|
||||||
|
Reference in New Issue
Block a user