X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/a6633642232efd164d4708967ab59e498fbff896..3b8ee3954e3f2834dae4b7a56d4fcecd5e8d03e6:/resources/js/components/code-editor.js diff --git a/resources/js/components/code-editor.js b/resources/js/components/code-editor.js index 2e3506ec7..241cdece9 100644 --- a/resources/js/components/code-editor.js +++ b/resources/js/components/code-editor.js @@ -1,27 +1,27 @@ -import Code from "../services/code"; import {onChildEvent, onEnterPress, onSelect} from "../services/dom"; +import {Component} from "./component"; -/** - * Code Editor - * @extends {Component} - */ -class CodeEditor { + +export class CodeEditor extends Component { setup() { this.container = this.$refs.container; this.popup = this.$el; this.editorInput = this.$refs.editor; - this.languageLinks = this.$manyRefs.languageLink; + this.languageButtons = this.$manyRefs.languageButton; + this.languageOptionsContainer = this.$refs.languageOptionsContainer; this.saveButton = this.$refs.saveButton; this.languageInput = this.$refs.languageInput; this.historyDropDown = this.$refs.historyDropDown; this.historyList = this.$refs.historyList; + this.favourites = new Set(this.$opts.favourites.split(',')); this.callback = null; this.editor = null; this.history = {}; this.historyKey = 'code_history'; this.setupListeners(); + this.setupFavourites(); } setupListeners() { @@ -31,13 +31,14 @@ class CodeEditor { } }); - onSelect(this.languageLinks, event => { + onSelect(this.languageButtons, event => { const language = event.target.dataset.lang; this.languageInput.value = language; - this.updateEditorMode(language); + this.languageInputChange(language); }); onEnterPress(this.languageInput, e => this.save()); + this.languageInput.addEventListener('input', e => this.languageInputChange(this.languageInput.value)); onSelect(this.saveButton, e => this.save()); onChildEvent(this.historyList, 'button', 'click', (event, elem) => { @@ -49,6 +50,58 @@ class CodeEditor { }); } + setupFavourites() { + for (const button of this.languageButtons) { + this.setupFavouritesForButton(button); + } + + this.sortLanguageList(); + } + + /** + * @param {HTMLButtonElement} button + */ + setupFavouritesForButton(button) { + const language = button.dataset.lang; + let isFavorite = this.favourites.has(language); + button.setAttribute('data-favourite', isFavorite ? 'true' : 'false'); + + onChildEvent(button.parentElement, '.lang-option-favorite-toggle', 'click', () => { + isFavorite = !isFavorite; + isFavorite ? this.favourites.add(language) : this.favourites.delete(language); + button.setAttribute('data-favourite', isFavorite ? 'true' : 'false'); + + window.$http.patch('/preferences/update-code-language-favourite', { + language: language, + active: isFavorite + }); + + this.sortLanguageList(); + if (isFavorite) { + button.scrollIntoView({block: "center", behavior: "smooth"}); + } + }); + } + + sortLanguageList() { + const sortedParents = this.languageButtons.sort((a, b) => { + const aFav = a.dataset.favourite === 'true'; + const bFav = b.dataset.favourite === 'true'; + + if (aFav && !bFav) { + return -1; + } else if (bFav && !aFav) { + return 1; + } + + return a.dataset.lang > b.dataset.lang ? 1 : -1; + }).map(button => button.parentElement); + + for (const parent of sortedParents) { + this.languageOptionsContainer.append(parent); + } + } + save() { if (this.callback) { this.callback(this.editor.getValue(), this.languageInput.value); @@ -60,16 +113,18 @@ class CodeEditor { this.languageInput.value = language; this.callback = callback; - this.show(); - this.updateEditorMode(language); - - Code.setContent(this.editor, code); + this.show() + .then(() => this.languageInputChange(language)) + .then(() => window.importVersioned('code')) + .then(Code => Code.setContent(this.editor, code)); } - show() { + async show() { + const Code = await window.importVersioned('code'); if (!this.editor) { this.editor = Code.popupEditor(this.editorInput, this.languageInput.value); } + this.loadHistory(); this.popup.components.popup.show(() => { Code.updateLayout(this.editor); @@ -84,17 +139,32 @@ class CodeEditor { this.addHistory(); } - updateEditorMode(language) { + async updateEditorMode(language) { + const Code = await window.importVersioned('code'); Code.setMode(this.editor, language, this.editor.getValue()); } + languageInputChange(language) { + this.updateEditorMode(language); + const inputLang = language.toLowerCase(); + + for (const link of this.languageButtons) { + const lang = link.dataset.lang.toLowerCase().trim(); + const isMatch = inputLang === lang; + link.classList.toggle('active', isMatch); + if (isMatch) { + link.scrollIntoView({block: "center", behavior: "smooth"}); + } + } + } + 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 `
`; + return ``; }).join(''); } @@ -112,6 +182,4 @@ class CodeEditor { window.sessionStorage.setItem(this.historyKey, historyString); } -} - -export default CodeEditor; \ No newline at end of file +} \ No newline at end of file