mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-31 15:24:31 +03:00
Merge pull request #3387 from BookStackApp/editor_switching
Page editor switching
This commit is contained in:
@ -131,7 +131,7 @@ class AutoSuggest {
|
||||
return this.hideSuggestions();
|
||||
}
|
||||
|
||||
this.list.innerHTML = suggestions.map(value => `<li><button type="button">${escapeHtml(value)}</button></li>`).join('');
|
||||
this.list.innerHTML = suggestions.map(value => `<li><button type="button" class="text-item">${escapeHtml(value)}</button></li>`).join('');
|
||||
this.list.style.display = 'block';
|
||||
for (const button of this.list.querySelectorAll('button')) {
|
||||
button.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
|
||||
|
@ -96,7 +96,7 @@ class CodeEditor {
|
||||
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>`;
|
||||
return `<li><button type="button" data-time="${key}" class="text-item">${localTime}</button></li>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
|
52
resources/js/components/confirm-dialog.js
Normal file
52
resources/js/components/confirm-dialog.js
Normal file
@ -0,0 +1,52 @@
|
||||
import {onSelect} from "../services/dom";
|
||||
|
||||
/**
|
||||
* Custom equivalent of window.confirm() using our popup component.
|
||||
* Is promise based so can be used like so:
|
||||
* `const result = await dialog.show()`
|
||||
* @extends {Component}
|
||||
*/
|
||||
class ConfirmDialog {
|
||||
|
||||
setup() {
|
||||
this.container = this.$el;
|
||||
this.confirmButton = this.$refs.confirm;
|
||||
|
||||
this.res = null;
|
||||
|
||||
onSelect(this.confirmButton, () => {
|
||||
this.sendResult(true);
|
||||
this.getPopup().hide();
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
this.getPopup().show(null, () => {
|
||||
this.sendResult(false);
|
||||
});
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
this.res = res;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Popup}
|
||||
*/
|
||||
getPopup() {
|
||||
return this.container.components.popup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} result
|
||||
*/
|
||||
sendResult(result) {
|
||||
if (this.res) {
|
||||
this.res(result)
|
||||
this.res = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ConfirmDialog;
|
@ -10,6 +10,7 @@ import chapterToggle from "./chapter-toggle.js"
|
||||
import codeEditor from "./code-editor.js"
|
||||
import codeHighlighter from "./code-highlighter.js"
|
||||
import collapsible from "./collapsible.js"
|
||||
import confirmDialog from "./confirm-dialog"
|
||||
import customCheckbox from "./custom-checkbox.js"
|
||||
import detailsHighlighter from "./details-highlighter.js"
|
||||
import dropdown from "./dropdown.js"
|
||||
@ -26,7 +27,6 @@ import headerMobileToggle from "./header-mobile-toggle.js"
|
||||
import homepageControl from "./homepage-control.js"
|
||||
import imageManager from "./image-manager.js"
|
||||
import imagePicker from "./image-picker.js"
|
||||
import index from "./index.js"
|
||||
import listSortControl from "./list-sort-control.js"
|
||||
import markdownEditor from "./markdown-editor.js"
|
||||
import newUserPassword from "./new-user-password.js"
|
||||
@ -66,6 +66,7 @@ const componentMapping = {
|
||||
"code-editor": codeEditor,
|
||||
"code-highlighter": codeHighlighter,
|
||||
"collapsible": collapsible,
|
||||
"confirm-dialog": confirmDialog,
|
||||
"custom-checkbox": customCheckbox,
|
||||
"details-highlighter": detailsHighlighter,
|
||||
"dropdown": dropdown,
|
||||
@ -82,7 +83,6 @@ const componentMapping = {
|
||||
"homepage-control": homepageControl,
|
||||
"image-manager": imageManager,
|
||||
"image-picker": imagePicker,
|
||||
"index": index,
|
||||
"list-sort-control": listSortControl,
|
||||
"markdown-editor": markdownEditor,
|
||||
"new-user-password": newUserPassword,
|
||||
|
@ -24,6 +24,8 @@ class PageEditor {
|
||||
this.draftDisplayIcon = this.$refs.draftDisplayIcon;
|
||||
this.changelogInput = this.$refs.changelogInput;
|
||||
this.changelogDisplay = this.$refs.changelogDisplay;
|
||||
this.changeEditorButtons = this.$manyRefs.changeEditor;
|
||||
this.switchDialogContainer = this.$refs.switchDialog;
|
||||
|
||||
// Translations
|
||||
this.draftText = this.$opts.draftText;
|
||||
@ -72,6 +74,9 @@ class PageEditor {
|
||||
// Draft Controls
|
||||
onSelect(this.saveDraftButton, this.saveDraft.bind(this));
|
||||
onSelect(this.discardDraftButton, this.discardDraft.bind(this));
|
||||
|
||||
// Change editor controls
|
||||
onSelect(this.changeEditorButtons, this.changeEditor.bind(this));
|
||||
}
|
||||
|
||||
setInitialFocus() {
|
||||
@ -113,17 +118,21 @@ class PageEditor {
|
||||
data.markdown = this.editorMarkdown;
|
||||
}
|
||||
|
||||
let didSave = false;
|
||||
try {
|
||||
const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data);
|
||||
if (!this.isNewDraft) {
|
||||
this.toggleDiscardDraftVisibility(true);
|
||||
}
|
||||
|
||||
this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`);
|
||||
this.autoSave.last = Date.now();
|
||||
if (resp.data.warning && !this.shownWarningsCache.has(resp.data.warning)) {
|
||||
window.$events.emit('warning', resp.data.warning);
|
||||
this.shownWarningsCache.add(resp.data.warning);
|
||||
}
|
||||
|
||||
didSave = true;
|
||||
} catch (err) {
|
||||
// Save the editor content in LocalStorage as a last resort, just in case.
|
||||
try {
|
||||
@ -134,6 +143,7 @@ class PageEditor {
|
||||
window.$events.emit('error', this.autosaveFailText);
|
||||
}
|
||||
|
||||
return didSave;
|
||||
}
|
||||
|
||||
draftNotifyChange(text) {
|
||||
@ -185,6 +195,18 @@ class PageEditor {
|
||||
this.discardDraftWrap.classList.toggle('hidden', !show);
|
||||
}
|
||||
|
||||
async changeEditor(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const link = event.target.closest('a').href;
|
||||
const dialog = this.switchDialogContainer.components['confirm-dialog'];
|
||||
const [saved, confirmed] = await Promise.all([this.saveDraft(), dialog.show()]);
|
||||
|
||||
if (saved && confirmed) {
|
||||
window.location = link;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PageEditor;
|
@ -34,7 +34,7 @@ class Popup {
|
||||
}
|
||||
|
||||
hide(onComplete = null) {
|
||||
fadeOut(this.container, 240, onComplete);
|
||||
fadeOut(this.container, 120, onComplete);
|
||||
if (this.onkeyup) {
|
||||
window.removeEventListener('keyup', this.onkeyup);
|
||||
this.onkeyup = null;
|
||||
@ -45,7 +45,7 @@ class Popup {
|
||||
}
|
||||
|
||||
show(onComplete = null, onHide = null) {
|
||||
fadeIn(this.container, 240, onComplete);
|
||||
fadeIn(this.container, 120, onComplete);
|
||||
|
||||
this.onkeyup = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
|
Reference in New Issue
Block a user