mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-01 10:56:08 +01:00
Implemented Preview button and states abstraction for toolbar
This commit is contained in:
@@ -750,7 +750,7 @@ class AdminController
|
||||
|
||||
$html = $page->content();
|
||||
|
||||
$this->admin->json_response = ['status' => 'success', 'message' => $html];
|
||||
$this->admin->json_response = ['status' => 'success', 'preview' => $html];
|
||||
} catch (\Exception $e) {
|
||||
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
|
||||
|
||||
|
||||
@@ -110,10 +110,16 @@ export class Toolbar {
|
||||
static templates() {
|
||||
return {
|
||||
navigation: `
|
||||
<div class="grav-editor-toolbar"></div>
|
||||
<div class="grav-editor-toolbar">
|
||||
<div class="grav-editor-actions"></div>
|
||||
<div class="grav-editor-modes"></div>
|
||||
</div>
|
||||
`,
|
||||
states: `
|
||||
<div class="grav-editor-states"></div>
|
||||
<div class="grav-editor-states">
|
||||
<div class="grav-editor-info"></div>
|
||||
<div class="grav-editor-modes"></div>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
}
|
||||
@@ -135,14 +141,35 @@ export class Toolbar {
|
||||
}
|
||||
|
||||
renderButtons() {
|
||||
this.ui.navigation.empty().append('<ul />');
|
||||
this.ui.navigation.find('.grav-editor-actions').empty().append('<ul />');
|
||||
Buttons.navigation.forEach((button) => {
|
||||
Object.keys(button).forEach((key) => {
|
||||
let obj = button[key];
|
||||
let element = $(`<li class="grav-editor-button-${key}"><a title="${obj.title}">${obj.label}</a></li>`);
|
||||
this.ui.navigation.find('ul').append(element);
|
||||
this.ui.navigation.find('.grav-editor-actions ul').append(element);
|
||||
|
||||
obj.action && obj.action.call(obj.action, this.codemirror, element, this.editor);
|
||||
obj.action && obj.action.call(obj.action, {
|
||||
codemirror: this.codemirror,
|
||||
button: element,
|
||||
textarea: this.editor,
|
||||
ui: this.ui
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.ui.navigation.find('.grav-editor-modes').empty().append('<ul />');
|
||||
Buttons.states.forEach((button) => {
|
||||
Object.keys(button).forEach((key) => {
|
||||
let obj = button[key];
|
||||
let element = $(`<li class="grav-editor-button-${key}"><a title="${obj.title}">${obj.label}</a></li>`);
|
||||
this.ui.navigation.find('.grav-editor-modes ul').append(element);
|
||||
|
||||
obj.action && obj.action.call(obj.action, {
|
||||
codemirror: this.codemirror,
|
||||
button: element,
|
||||
textarea: this.editor,
|
||||
ui: this.ui
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import { config } from 'grav-config';
|
||||
import request from '../../../utils/request';
|
||||
|
||||
let replacer = ({ name, replace, codemirror, button, mode = 'replaceSelections', runner }) => {
|
||||
button.on(`click.editor.${name}`, () => {
|
||||
@@ -71,7 +73,7 @@ export default {
|
||||
identifier: 'bold',
|
||||
title: 'Bold',
|
||||
label: '<i class="fa fa-fw fa-bold"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'bold', replace: '**$1$cur**', codemirror, button });
|
||||
}
|
||||
}
|
||||
@@ -80,7 +82,7 @@ export default {
|
||||
identifier: 'italic',
|
||||
title: 'Italic',
|
||||
label: '<i class="fa fa-fw fa-italic"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'italic', replace: '_$1$cur_', codemirror, button });
|
||||
}
|
||||
}
|
||||
@@ -89,7 +91,7 @@ export default {
|
||||
identifier: 'strike',
|
||||
title: 'Strikethrough',
|
||||
label: '<i class="fa fa-fw fa-strikethrough"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'strike', replace: '~~$1$cur~~', codemirror, button });
|
||||
}
|
||||
}
|
||||
@@ -98,7 +100,7 @@ export default {
|
||||
identifier: 'link',
|
||||
title: 'Link',
|
||||
label: '<i class="fa fa-fw fa-link"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'link', replace: '[$1](http://$cur)', codemirror, button });
|
||||
}
|
||||
}
|
||||
@@ -107,7 +109,7 @@ export default {
|
||||
identifier: 'image',
|
||||
title: 'Image',
|
||||
label: '<i class="fa fa-fw fa-picture-o"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'image', replace: '', codemirror, button });
|
||||
}
|
||||
}
|
||||
@@ -116,7 +118,7 @@ export default {
|
||||
identifier: 'blockquote',
|
||||
title: 'Blockquote',
|
||||
label: '<i class="fa fa-fw fa-quote-right"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'blockquote', replace: '> $1', codemirror, button, mode: 'replaceLine' });
|
||||
}
|
||||
}
|
||||
@@ -125,7 +127,7 @@ export default {
|
||||
identifier: 'listUl',
|
||||
title: 'Unordered List',
|
||||
label: '<i class="fa fa-fw fa-list-ul"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({ name: 'listUl', replace: '* $1', codemirror, button, mode: 'replaceLine' });
|
||||
}
|
||||
}
|
||||
@@ -134,7 +136,7 @@ export default {
|
||||
identifier: 'listOl',
|
||||
title: 'Ordered List',
|
||||
label: '<i class="fa fa-fw fa-list-ol"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
action({ codemirror, button, textarea }) {
|
||||
replacer({
|
||||
name: 'listOl',
|
||||
replace: '. $1',
|
||||
@@ -152,47 +154,109 @@ export default {
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
fullscreen: {
|
||||
identifier: 'fullscreen',
|
||||
title: 'Fullscreen',
|
||||
label: '<i class="fa fa-fw fa-expand"></i>',
|
||||
action(codemirror, button, textarea) {
|
||||
button.on('click.editor.fullscreen', () => {
|
||||
let container = textarea.closest('.grav-editor');
|
||||
let wrapper = codemirror.getWrapperElement();
|
||||
|
||||
if (!container.hasClass('grav-editor-fullscreen')) {
|
||||
textarea.data('fullScreenRestore', {
|
||||
scrollTop: window.pageYOffset,
|
||||
scrollLeft: window.pageXOffset,
|
||||
width: wrapper.style.width,
|
||||
height: wrapper.style.height
|
||||
});
|
||||
|
||||
wrapper.style.width = '';
|
||||
wrapper.style.height = textarea.parent('.grav-editor-content').height() + 'px';
|
||||
global.document.documentElement.style.overflow = 'hidden';
|
||||
} else {
|
||||
global.document.documentElement.style.overflow = '';
|
||||
let state = textarea.data('fullScreenRestore');
|
||||
|
||||
wrapper.style.width = state.width;
|
||||
wrapper.style.height = state.height;
|
||||
window.scrollTo(state.scrollLeft, state.scrollTop);
|
||||
}
|
||||
|
||||
container.toggleClass('grav-editor-fullscreen');
|
||||
|
||||
setTimeout(() => {
|
||||
codemirror.refresh();
|
||||
// this.preview.parent().css('height', this.code.height());
|
||||
$(window).trigger('resize');
|
||||
}, 5);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
states: [{}]
|
||||
states: [{
|
||||
code: {
|
||||
identifier: 'editor',
|
||||
title: 'Editor',
|
||||
label: '<i class="fa fa-fw fa-code"></i>',
|
||||
action({ codemirror, button, textarea, ui }) {
|
||||
if (textarea.data('grav-editor-mode') === 'editor') {
|
||||
button.addClass('editor-active');
|
||||
}
|
||||
|
||||
button.on('click.states.editor', () => {
|
||||
button.siblings().removeClass('editor-active');
|
||||
button.addClass('editor-active');
|
||||
textarea.data('grav-editor-mode', 'editor');
|
||||
let previewContainer = textarea.data('grav-editor-preview-container');
|
||||
let content = textarea.parent('.grav-editor-content');
|
||||
|
||||
content.css('display', 'block');
|
||||
ui.navigation.find('.grav-editor-actions').css('display', 'block');
|
||||
if (previewContainer.length) {
|
||||
previewContainer.css('display', 'none');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
preview: {
|
||||
identifier: 'preview',
|
||||
title: 'Preview',
|
||||
label: '<i class="fa fa-fw fa-eye"></i>',
|
||||
action({ codemirror, button, textarea, ui }) {
|
||||
if (textarea.data('grav-editor-mode') === 'preview') {
|
||||
button.addClass('editor-active');
|
||||
}
|
||||
button.on('click.states.preview', () => {
|
||||
let previewContainer = textarea.data('grav-editor-preview-container');
|
||||
let content = textarea.parent('.grav-editor-content');
|
||||
button.siblings().removeClass('editor-active');
|
||||
button.addClass('editor-active');
|
||||
textarea.data('grav-editor-mode', 'preview');
|
||||
|
||||
if (!previewContainer) {
|
||||
previewContainer = $('<div class="grav-editor-preview" />');
|
||||
content.after(previewContainer);
|
||||
textarea.data('grav-editor-preview-container', previewContainer);
|
||||
}
|
||||
|
||||
previewContainer.css({ height: content.height(), display: 'block' });
|
||||
content.css('display', 'none');
|
||||
ui.navigation.find('.grav-editor-actions').css('display', 'none');
|
||||
|
||||
let url = `${textarea.data('grav-urlpreview')}/task${config.param_sep}processmarkdown`;
|
||||
let params = textarea.closest('form').serializeArray();
|
||||
let body = {};
|
||||
params.map((obj) => { body[obj.name] = obj.value; });
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: params
|
||||
}, (response) => previewContainer.html(response.preview));
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
fullscreen: {
|
||||
identifier: 'fullscreen',
|
||||
title: 'Fullscreen',
|
||||
label: '<i class="fa fa-fw fa-expand"></i>',
|
||||
action({ codemirror, button, textarea }) {
|
||||
button.on('click.editor.fullscreen', () => {
|
||||
let container = textarea.closest('.grav-editor');
|
||||
let wrapper = codemirror.getWrapperElement();
|
||||
|
||||
if (!container.hasClass('grav-editor-fullscreen')) {
|
||||
textarea.data('fullScreenRestore', {
|
||||
scrollTop: window.pageYOffset,
|
||||
scrollLeft: window.pageXOffset,
|
||||
width: wrapper.style.width,
|
||||
height: wrapper.style.height
|
||||
});
|
||||
|
||||
wrapper.style.width = '';
|
||||
wrapper.style.height = textarea.parent('.grav-editor-content').height() + 'px';
|
||||
global.document.documentElement.style.overflow = 'hidden';
|
||||
} else {
|
||||
global.document.documentElement.style.overflow = '';
|
||||
let state = textarea.data('fullScreenRestore');
|
||||
|
||||
wrapper.style.width = state.width;
|
||||
wrapper.style.height = state.height;
|
||||
window.scrollTo(state.scrollLeft, state.scrollTop);
|
||||
}
|
||||
|
||||
container.toggleClass('grav-editor-fullscreen');
|
||||
|
||||
setTimeout(() => {
|
||||
codemirror.refresh();
|
||||
// this.preview.parent().css('height', this.code.height());
|
||||
$(window).trigger('resize');
|
||||
}, 5);
|
||||
});
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
2
themes/grav/css-compiled/template.css
vendored
2
themes/grav/css-compiled/template.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
20
themes/grav/js/admin.min.js
vendored
20
themes/grav/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
26
themes/grav/js/vendor.min.js
vendored
26
themes/grav/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -30,6 +30,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.grav-editor-toolbar {
|
||||
@include align-items(stretch);
|
||||
@include display(flex);
|
||||
@include flex-direction(row);
|
||||
|
||||
.grav-editor-actions {
|
||||
@include flex(80% 0 0);
|
||||
}
|
||||
.grav-editor-modes {
|
||||
@include flex(1);
|
||||
ul {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grav-editor-toolbar {
|
||||
@include clearfix;
|
||||
border: 1px solid $form-border;
|
||||
@@ -48,15 +64,6 @@
|
||||
}
|
||||
display: inline-block;
|
||||
}
|
||||
.editor-active a {
|
||||
background: white;
|
||||
cursor: auto;
|
||||
border-left: 1px solid $form-border;
|
||||
border-right: 1px solid $form-border;
|
||||
&:hover {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
@@ -75,22 +82,48 @@
|
||||
&:hover, &:focus {
|
||||
background: lighten($content-bg, 2%);
|
||||
color: darken($content-fg, 10%);
|
||||
border-top: 1px solid darken($content-bg, 20%);
|
||||
border-bottom: 1px solid darken($content-bg, 20%);
|
||||
border-top: 1px solid $form-border;
|
||||
border-bottom: 1px solid $form-border;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-active {
|
||||
a {
|
||||
background: white;
|
||||
cursor: auto;
|
||||
border-top: 1px solid $form-border;
|
||||
border-left: 1px solid $form-border;
|
||||
border-right: 1px solid $form-border;
|
||||
|
||||
&:hover {
|
||||
background: $white;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid $white;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child a, &:last-child a:hover {
|
||||
border-right: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.grav-editor-actions {
|
||||
&.left {
|
||||
float: left;
|
||||
.grav-editor-toolbar .grav-editor-modes {
|
||||
li {
|
||||
&:first-child a {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
&:last-child a {
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
a {
|
||||
&:hover {
|
||||
/*border: 1px solid transparent;
|
||||
border-bottom: 1px solid darken($content-bg, 20%);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +134,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.grav-editor-content {
|
||||
|
||||
.grav-editor-content, .grav-editor-preview {
|
||||
@include clearfix;
|
||||
cursor: text;
|
||||
border: 1px solid $form-border;
|
||||
@@ -123,7 +155,7 @@
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
background: #fbfbfb;
|
||||
background: #fff;
|
||||
border-bottom-right-radius: $form-border-radius;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<div class="grav-editor-content">
|
||||
<textarea
|
||||
data-grav-editor="{{ {'codemirror': codemirrorOptions} | json_encode|e('html_attr') }}"
|
||||
data-grav-editor-mode="editor"
|
||||
name="{{ (scope ~ field.name)|fieldName }}"
|
||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||
{% if field.showPreview %}data-grav-preview-enabled="true"{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user