mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
New editor layout (#355)
- the title is now part of the content - new ways to navigate from the title to the content - the new editor contains updated toolbar behavior - the new editor contains markdown like commands
This commit is contained in:
parent
a1daa359b0
commit
ec597f0296
7 changed files with 116 additions and 63 deletions
|
@ -16,6 +16,8 @@ import PostModel from 'ghost-admin/models/post';
|
|||
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
||||
import {isVersionMismatchError} from 'ghost-admin/services/ajax';
|
||||
import {isInvalidError} from 'ember-ajax/errors';
|
||||
import $ from 'jquery';
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
|
||||
const {resolve} = RSVP;
|
||||
|
||||
|
@ -40,6 +42,12 @@ export default Mixin.create({
|
|||
clock: injectService(),
|
||||
slugGenerator: injectService(),
|
||||
|
||||
cards: [], // for apps
|
||||
atoms: [], // for apps
|
||||
toolbar: [], // for apps
|
||||
apiRoot: ghostPaths().apiRoot,
|
||||
assetPath: ghostPaths().assetRoot,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
window.onbeforeunload = () => {
|
||||
|
@ -543,6 +551,13 @@ export default Mixin.create({
|
|||
|
||||
toggleReAuthenticateModal() {
|
||||
this.toggleProperty('showReAuthenticateModal');
|
||||
},
|
||||
|
||||
titleKeyDown(event) {
|
||||
if (event.keyCode === 13 || event.keyCode === 40) {
|
||||
// if the enter key or down key are pressed then focus on the editor
|
||||
$('.__mobiledoc-editor').focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@ import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
|||
import base from 'ghost-admin/mixins/editor-base-route';
|
||||
import isNumber from 'ghost-admin/utils/isNumber';
|
||||
import isFinite from 'ghost-admin/utils/isFinite';
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
|
||||
export default AuthenticatedRoute.extend(base, {
|
||||
titleToken: 'Editor',
|
||||
|
@ -53,13 +52,7 @@ export default AuthenticatedRoute.extend(base, {
|
|||
|
||||
setupController(controller) {
|
||||
this._super(...arguments);
|
||||
|
||||
controller.set('shouldFocusEditor', this.get('_transitionedFromNew'));
|
||||
controller.set('cards' , []);
|
||||
controller.set('atoms' , []);
|
||||
controller.set('toolbar' , []);
|
||||
controller.set('apiRoot', ghostPaths().apiRoot);
|
||||
controller.set('assetPath', ghostPaths().assetRoot);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
.gh-editor-title {
|
||||
flex-grow: 1;
|
||||
margin-bottom: 2vw;
|
||||
}
|
||||
|
||||
.gh-editor-title input {
|
||||
|
@ -16,7 +17,7 @@
|
|||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--darkgrey);
|
||||
font-size: 2.6rem;
|
||||
font-size: 3.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
@ -400,3 +401,32 @@
|
|||
.modal-markdown-help-table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
/* NEW editor
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.gh-editor-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
height: 65px;
|
||||
}
|
||||
|
||||
.gh-editor-container {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
padding: 100px 4vw 40px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-editor-inner {
|
||||
margin: 0 auto;
|
||||
max-width: 700px;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<section class="gh-view">
|
||||
<header class="view-header">
|
||||
{{#gh-view-title classNames="gh-editor-title" openMobileMenu="openMobileMenu"}}
|
||||
{{gh-trim-focus-input model.titleScratch type="text" id="entry-title" placeholder="Your Post Title" tabindex="1" shouldFocus=shouldFocusTitle focus-out="updateTitle" update=(action (perform updateTitle))}}
|
||||
{{/gh-view-title}}
|
||||
{{#if scheduleCountdown}}
|
||||
<time datetime="{{post.publishedAtUTC}}" class="gh-notification gh-notification-schedule">
|
||||
Post will be published {{scheduleCountdown}}.
|
||||
</time>
|
||||
|
||||
<header class="gh-editor-header">
|
||||
<div class="gh-editor-status">
|
||||
{{#if model.isPublished}}
|
||||
Published
|
||||
{{else if model.isScheduled}}
|
||||
Scheduled
|
||||
{{else}}
|
||||
Draft
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
<section class="view-actions">
|
||||
<button type="button" class="post-settings" title="Post Settings" {{action "openSettingsMenu"}}>
|
||||
<i class="icon-settings"></i>
|
||||
|
@ -29,15 +32,28 @@
|
|||
}}
|
||||
</section>
|
||||
</header>
|
||||
{{ghost-editor
|
||||
value=(readonly model.scratch)
|
||||
onChange=(action (mut model.scratch))
|
||||
onFirstChange=(action "autoSaveNew")
|
||||
onTeardown=(action "cancelTimers")
|
||||
shouldFocusEditor=shouldFocusEditor
|
||||
apiRoot=apiRoot
|
||||
assetPath=assetPath
|
||||
}}
|
||||
<div class="gh-editor-container">
|
||||
<div class="gh-editor-inner">
|
||||
{{#gh-view-title classNames="gh-editor-title" openMobileMenu="openMobileMenu"}}
|
||||
{{gh-trim-focus-input model.titleScratch type="text" id="entry-title" placeholder="Your Post Title" tabindex="1" shouldFocus=shouldFocusTitle focus-out="updateTitle" update=(action (perform updateTitle)) keyDown=(action "titleKeyDown")}}
|
||||
{{/gh-view-title}}
|
||||
{{#if scheduleCountdown}}
|
||||
<time datetime="{{post.publishedAtUTC}}" class="gh-notification gh-notification-schedule">
|
||||
Post will be published {{scheduleCountdown}}.
|
||||
</time>
|
||||
{{/if}}
|
||||
{{ghost-editor
|
||||
value=(readonly model.scratch)
|
||||
onChange=(action (mut model.scratch))
|
||||
onFirstChange=(action "autoSaveNew")
|
||||
onTeardown=(action "cancelTimers")
|
||||
shouldFocusEditor=shouldFocusEditor
|
||||
apiRoot=apiRoot
|
||||
assetPath=assetPath
|
||||
tabindex=2
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{#if showDeletePostModal}}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
"ember-wormhole": "0.5.0",
|
||||
"emberx-file-input": "1.1.0",
|
||||
"fs-extra": "0.30.0",
|
||||
"ghost-editor": "0.0.11",
|
||||
"ghost-editor": "0.0.14",
|
||||
"glob": "7.1.1",
|
||||
"grunt": "1.0.1",
|
||||
"grunt-bg-shell": "2.3.3",
|
||||
|
|
|
@ -110,7 +110,7 @@ describe('Acceptance: Editor', function() {
|
|||
fillIn('input[name="post-setting-date"]', '10 May 16 @ 10:00');
|
||||
triggerEvent('input[name="post-setting-date"]', 'blur');
|
||||
// saving
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('input[name="post-setting-date"]').val(), 'date after saving')
|
||||
|
@ -128,9 +128,9 @@ describe('Acceptance: Editor', function() {
|
|||
|
||||
// checking the flow of the saving button for a draft
|
||||
andThen(() => {
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
.to.be.false;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button')
|
||||
.to.equal('Save Draft');
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the default active button state for a draft')
|
||||
.to.be.true;
|
||||
|
@ -142,21 +142,21 @@ describe('Acceptance: Editor', function() {
|
|||
andThen(() => {
|
||||
expect(find('.post-save-publish').hasClass('active'), 'highlights the selected active button state')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from draft to published')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from draft to published')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button after click on \'publish now\'')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button after click on \'publish now\'')
|
||||
.to.equal('Publish Now');
|
||||
});
|
||||
|
||||
// Publish the post
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button after publishing')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button after publishing')
|
||||
.to.equal('Update Post');
|
||||
expect(find('.post-save-publish').hasClass('active'), 'highlights the default active button state for a published post')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
.to.be.false;
|
||||
});
|
||||
|
||||
|
@ -178,7 +178,7 @@ describe('Acceptance: Editor', function() {
|
|||
});
|
||||
|
||||
// saving
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('input[name="post-setting-date"]').val(), 'date value restored')
|
||||
|
@ -189,7 +189,7 @@ describe('Acceptance: Editor', function() {
|
|||
fillIn('input[name="post-setting-date"]', '10 May 16 @ 10:00');
|
||||
triggerEvent('input[name="post-setting-date"]', 'blur');
|
||||
// saving
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('input[name="post-setting-date"]').val(), 'new date after saving')
|
||||
|
@ -210,7 +210,7 @@ describe('Acceptance: Editor', function() {
|
|||
|
||||
triggerEvent('#activeTimezone', 'change');
|
||||
// save the settings
|
||||
click('.view-header .btn.btn-blue');
|
||||
click('.btn.btn-blue');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#activeTimezone option:selected').text().trim(), 'new timezone after saving')
|
||||
|
@ -242,21 +242,21 @@ describe('Acceptance: Editor', function() {
|
|||
andThen(() => {
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the active button state for a draft')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from published to draft')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from published to draft')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for post to unpublish')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for post to unpublish')
|
||||
.to.equal('Unpublish');
|
||||
});
|
||||
|
||||
// Unpublish the post
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for draft')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for draft')
|
||||
.to.equal('Save Draft');
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the default active button state for a draft')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
.to.be.false;
|
||||
});
|
||||
|
||||
|
@ -287,24 +287,24 @@ describe('Acceptance: Editor', function() {
|
|||
andThen(() => {
|
||||
expect(find('.post-save-schedule').hasClass('active'), 'highlights the active button state for a draft')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from published to draft')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button to change from published to draft')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for post to schedule')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for post to schedule')
|
||||
.to.equal('Schedule Post');
|
||||
});
|
||||
|
||||
// click on schedule post and save
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
// Dropdown menu should be 'Update Post' and 'Unschedule'
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post')
|
||||
.to.equal('Update Post');
|
||||
expect(find('.post-save-schedule').hasClass('active'), 'highlights the default active button state for a scheduled post')
|
||||
.to.be.true;
|
||||
expect(find('.post-save-draft').text().trim(), 'not active option should say \'Unschedule\'')
|
||||
.to.equal('Unschedule');
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
.to.be.false;
|
||||
// expect countdown to show warning, that post will be published in x minutes
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -315,23 +315,23 @@ describe('Acceptance: Editor', function() {
|
|||
click('.post-save-draft a');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button to unscheduled post')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button to unscheduled post')
|
||||
.to.equal('Unschedule');
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the default active button state for a scheduled post')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
// click on unschedule post and save
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for a draft')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for a draft')
|
||||
.to.equal('Save Draft');
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the default active button state for a draft post')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
.to.be.false;
|
||||
// expect no countdown notification after unscheduling
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -376,7 +376,7 @@ describe('Acceptance: Editor', function() {
|
|||
fillIn('input[name="post-setting-date"]', plusTenMin);
|
||||
triggerEvent('input[name="post-setting-date"]', 'blur');
|
||||
click('.post-save-schedule a');
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(
|
||||
|
@ -405,7 +405,7 @@ describe('Acceptance: Editor', function() {
|
|||
// Test title validation
|
||||
fillIn('input[id="entry-title"]', Array(160).join('a'));
|
||||
triggerEvent('input[id="entry-title"]', 'blur');
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(
|
||||
|
@ -435,13 +435,13 @@ describe('Acceptance: Editor', function() {
|
|||
expect(find('input[name="post-setting-date"]').val(), 'scheduled date')
|
||||
.to.equal(compareDate);
|
||||
// Dropdown menu should be 'Update Post' and 'Unschedule'
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post')
|
||||
.to.equal('Update Post');
|
||||
expect(find('.post-save-schedule').hasClass('active'), 'highlights the default active button state for a scheduled post')
|
||||
.to.be.true;
|
||||
expect(find('.post-save-draft').text().trim(), 'not active option should say \'Unschedule\'')
|
||||
.to.equal('Unschedule');
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'no red button expected')
|
||||
.to.be.false;
|
||||
// expect countdown to show warning, that post will be published in x minutes
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -461,7 +461,7 @@ describe('Acceptance: Editor', function() {
|
|||
|
||||
andThen(() => {
|
||||
// Save button should say 'Unschedule'
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post in status freeze mode')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post in status freeze mode')
|
||||
.to.equal('Unschedule');
|
||||
// expect countdown to show warning, that post will be published in x minutes
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -488,7 +488,7 @@ describe('Acceptance: Editor', function() {
|
|||
|
||||
andThen(() => {
|
||||
// Save button should say 'Unschedule'
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post in status freeze mode')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for scheduled post in status freeze mode')
|
||||
.to.equal('Unschedule');
|
||||
// expect countdown to show warning, that post will be published in x minutes
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -499,16 +499,16 @@ describe('Acceptance: Editor', function() {
|
|||
});
|
||||
|
||||
// click on Unschedule
|
||||
click('.view-header .btn.btn-sm.js-publish-button');
|
||||
click('.btn.btn-sm.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.markdown-editor').val(), 'changed text in markdown editor')
|
||||
.to.equal('Let\'s make some markdown changes');
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').text().trim(), 'text in save button for a draft')
|
||||
expect(find('.btn.btn-sm.js-publish-button').text().trim(), 'text in save button for a draft')
|
||||
.to.equal('Save Draft');
|
||||
expect(find('.post-save-draft').hasClass('active'), 'highlights the default active button state for a draft post')
|
||||
.to.be.true;
|
||||
expect(find('.view-header .btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
expect(find('.btn.btn-sm.js-publish-button').hasClass('btn-red'), 'red button expected due to status change')
|
||||
.to.be.false;
|
||||
// expect no countdown notification after unscheduling
|
||||
expect(find('.gh-notification.gh-notification-schedule').text().trim(), 'notification countdown')
|
||||
|
@ -519,4 +519,4 @@ describe('Acceptance: Editor', function() {
|
|||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue