mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 20:03:12 +03:00
🎨 Adjusted post settings menu design so it can stay open whilst editing
refs https://github.com/TryGhost/Team/issues/840 We wanted to switch to a settings menu that stays open to the right of the editor rather than a popover that blocks all other interaction with the post to solve two use-cases: 1. when editing it's fairly common to select some text from the post contents when setting excerpt and meta data text, with the previous design not letting you scroll or select whilst the menu got in the way of that 2. having the menu open with meta data visible before publishing can help you see everything is set as you want and help you feel confident when publishing/sending content --- - removed `psmRedesign` labs flag - swapped labs component/css for main component and deleted labs component - cleaned up now-unused `ui.showSettingsMenu` property and related actions/classes
This commit is contained in:
parent
a07b40440d
commit
3d16b95e0f
@ -1,3 +1,3 @@
|
||||
<div class="gh-app" {{did-insert this.setBodyClass}} {{did-update this.setBodyClass @showSettingsMenu}} ...attributes>
|
||||
<div class="gh-app" ...attributes>
|
||||
{{yield}}
|
||||
</div>
|
||||
|
@ -1,9 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
export default class GhAppComponent extends Component {
|
||||
@action
|
||||
setBodyClass() {
|
||||
document.body.classList.toggle('settings-menu-expanded', this.args.showSettingsMenu);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
{{yield (hash
|
||||
headerClass=this.headerClass
|
||||
headerHeight=this.headerHeight
|
||||
isDraggedOver=this.isDraggedOver
|
||||
isFullScreen=this.isFullScreen
|
||||
|
@ -19,7 +19,6 @@ export default Component.extend({
|
||||
|
||||
// Internal attributes
|
||||
droppedFiles: null,
|
||||
headerClass: '',
|
||||
headerHeight: 0,
|
||||
imageExtensions: IMAGE_EXTENSIONS,
|
||||
imageMimeTypes: IMAGE_MIME_TYPES,
|
||||
@ -33,29 +32,10 @@ export default Component.extend({
|
||||
_onResizeHandler: null,
|
||||
_viewActionsWidth: 190,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this._onResizeHandler = (evt) => {
|
||||
debounce(this, this._setHeaderClass, evt, 100);
|
||||
};
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
window.addEventListener('resize', this._onResizeHandler);
|
||||
this._setHeaderClass();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
window.removeEventListener('resize', this._onResizeHandler);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleFullScreen(isFullScreen) {
|
||||
this.set('isFullScreen', isFullScreen);
|
||||
this.ui.set('isFullScreen', isFullScreen);
|
||||
run.scheduleOnce('afterRender', this, this._setHeaderClass);
|
||||
},
|
||||
|
||||
togglePreview(isPreview) {
|
||||
@ -64,7 +44,6 @@ export default Component.extend({
|
||||
|
||||
toggleSplitScreen(isSplitScreen) {
|
||||
this.set('isSplitScreen', isSplitScreen);
|
||||
run.scheduleOnce('afterRender', this, this._setHeaderClass);
|
||||
},
|
||||
|
||||
uploadImages(fileList, resetInput) {
|
||||
@ -85,40 +64,6 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_setHeaderClass() {
|
||||
if (this.feature.psmRedesign) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editorTitle = this.element.querySelector('.gh-editor-title, .kg-title-input');
|
||||
let smallHeaderClass = 'gh-editor-header-small';
|
||||
let newHeaderClass = '';
|
||||
|
||||
this._editorTitleElement = editorTitle;
|
||||
|
||||
if (this.isSplitScreen) {
|
||||
this.set('headerClass', smallHeaderClass);
|
||||
return;
|
||||
}
|
||||
|
||||
if (editorTitle) {
|
||||
let boundingRect = editorTitle.getBoundingClientRect();
|
||||
let maxRight = window.innerWidth - this._viewActionsWidth;
|
||||
|
||||
if (boundingRect.right >= maxRight) {
|
||||
newHeaderClass = smallHeaderClass;
|
||||
}
|
||||
}
|
||||
|
||||
if (newHeaderClass !== this.headerClass) {
|
||||
// grab height of header so that we can pass it as an offset to other
|
||||
// editor components
|
||||
run.scheduleOnce('afterRender', this, this._setHeaderHeight);
|
||||
}
|
||||
|
||||
this.set('headerClass', newHeaderClass);
|
||||
},
|
||||
|
||||
_setHeaderHeight() {
|
||||
if (this.headerClass && this._editorTitleElement) {
|
||||
let height = this._editorTitleElement.offsetHeight;
|
||||
|
@ -1,467 +0,0 @@
|
||||
<div
|
||||
class="settings-menu-container-labs {{if (and this.isViewingSubview (not (eq this.subview "email-settings"))) "settings-menu-container-labs-wide"}}"
|
||||
{{did-insert this.setSidebarWidthFromElement}}
|
||||
{{did-update this.setSidebarWidthFromElement this.isViewingSubview}}
|
||||
>
|
||||
<div id="entry-controls">
|
||||
<div class="settings-menu settings-menu-pane settings-menu-pane-main">
|
||||
<div class="settings-menu-header labs">
|
||||
<h4>{{capitalize this.post.displayName}} settings</h4>
|
||||
</div>
|
||||
<div class="settings-menu-content labs">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="url">{{capitalize this.post.displayName}} URL</label>
|
||||
{{!-- new posts don't have a preview link --}}
|
||||
{{#unless this.post.isNew}}
|
||||
{{#if this.post.isPublished}}
|
||||
<a class="post-view-link" target="_blank" href="{{this.post.url}}">
|
||||
View {{this.post.displayName}} {{svg-jar "external"}}
|
||||
</a>
|
||||
{{else if this.post.isScheduled}}
|
||||
<a class="post-view-link" target="_blank" href="{{this.post.previewUrl}}">
|
||||
Preview {{svg-jar "external"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
<div class="gh-input-icon gh-icon-link">
|
||||
{{svg-jar "link"}}
|
||||
<GhTextInput
|
||||
@class="post-setting-slug"
|
||||
@id="url"
|
||||
@name="post-setting-slug"
|
||||
@value={{readonly this.slugValue}}
|
||||
@input={{action (mut this.slugValue) value="target.value"}}
|
||||
@focus-out={{action "updateSlug" this.slugValue}}
|
||||
@stopEnterKeyDownPropagation={{true}} />
|
||||
</div>
|
||||
<GhUrlPreview @slug={{this.slugValue}} @tagName="p" @classNames="description" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{#if (or this.post.isDraft this.post.isPublished this.post.pastScheduledTime)}}
|
||||
<label>Publish date</label>
|
||||
{{else}}
|
||||
<label>Scheduled date</label>
|
||||
{{/if}}
|
||||
<GhDateTimePicker
|
||||
@date={{this.post.publishedAtBlogDate}}
|
||||
@time={{this.post.publishedAtBlogTime}}
|
||||
@setDate={{action "setPublishedAtBlogDate"}}
|
||||
@setTime={{action "setPublishedAtBlogTime"}}
|
||||
@errors={{this.post.errors}}
|
||||
@dateErrorProperty="publishedAtBlogDate"
|
||||
@timeErrorProperty="publishedAtBlogTime"
|
||||
@maxDate="now"
|
||||
@disabled={{this.post.isScheduled}}
|
||||
@isActive={{and this.showSettingsMenu (not this.isViewingSubview)}}
|
||||
/>
|
||||
{{#unless (or this.post.isDraft this.post.isPublished this.post.pastScheduledTime)}}
|
||||
<p>Use the publish menu to re-schedule</p>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{#unless this.session.user.isContributor}}
|
||||
<div class="form-group">
|
||||
<label for="tag-input">Tags</label>
|
||||
<GhPsmTagsInput @post={{this.post}} @triggerId="tag-input" />
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
|
||||
{{#if this.showVisibilityInput}}
|
||||
{{#if (feature "multipleProducts")}}
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="visibility">
|
||||
<label for="visibility-input">Post access</label>
|
||||
<div
|
||||
class="gh-radio {{if this.post.isPublic "active"}}"
|
||||
{{on "click" (action "setVisibility" "public")}}
|
||||
>
|
||||
<div class="gh-radio-button"></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Public</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gh-radio {{if (not this.post.isPublic) "active"}}"
|
||||
{{on "click" (action "setVisibility" this.post.visibilitySegment)}}
|
||||
>
|
||||
<div class="gh-radio-button"></div>
|
||||
<div class="gh-radio-content">
|
||||
<div class="gh-radio-label">Members-only</div>
|
||||
<div class="gh-radio-desc">
|
||||
<GhMembersSegmentSelect
|
||||
@hideLabels={{true}}
|
||||
@segment={{this.post.visibilitySegment}}
|
||||
@onChange={{action "setVisibility"}}
|
||||
@renderInPlace={{true}}
|
||||
@hideOptionsWhenAllSelected={{true}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="visibility" class="no-selection" data-test-error="visibility" />
|
||||
</GhFormGroup>
|
||||
{{else}}
|
||||
<div class="form-group">
|
||||
<label for="visibility-input">Post access</label>
|
||||
<GhPsmVisibilityInput @post={{this.post}} @triggerId="visibility-input" />
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="customExcerpt">
|
||||
<label for="custom-excerpt">Excerpt</label>
|
||||
<GhTextarea
|
||||
@class="post-setting-custom-excerpt"
|
||||
@id="custom-excerpt"
|
||||
@name="post-setting-custom-excerpt"
|
||||
@value={{readonly this.customExcerptScratch}}
|
||||
@input={{action (mut this.customExcerptScratch) value="target.value"}}
|
||||
@focus-out={{action "setCustomExcerpt" this.customExcerptScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
data-test-field="custom-excerpt"
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="customExcerpt" data-test-error="custom-excerpt" />
|
||||
</GhFormGroup>
|
||||
|
||||
{{#unless this.session.user.isAuthorOrContributor}}
|
||||
<GhFormGroup @class="for-select" @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="authors" data-test-input="authors">
|
||||
<label for="author-list">Authors</label>
|
||||
<GhPsmAuthorsInput @selectedAuthors={{this.post.authors}} @updateAuthors={{action "changeAuthors"}} @triggerId="author-list" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="authors" data-test-error="authors" />
|
||||
</GhFormGroup>
|
||||
{{/unless}}
|
||||
|
||||
<ul class="nav-list nav-list-block">
|
||||
<li class="nav-list-item" {{action "showSubview" "meta-data"}} data-test-button="meta-data">
|
||||
<button type="button">
|
||||
<b>Meta data</b>
|
||||
<span>Extra content for search engines</span>
|
||||
</button>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</li>
|
||||
<li class="nav-list-item" {{action "showSubview" "twitter-data"}} data-test-button="twitter-data">
|
||||
<button type="button">
|
||||
<b>Twitter card</b>
|
||||
<span>Customize structured data for Twitter</span>
|
||||
</button>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</li>
|
||||
<li class="nav-list-item" {{action "showSubview" "facebook-data"}} data-test-button="facebook-data">
|
||||
<button type="button">
|
||||
<b>Facebook card</b>
|
||||
<span>Customize Open Graph data</span>
|
||||
</button>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</li>
|
||||
{{#if (and this.post.isPost showEmailNewsletter)}}
|
||||
<li class="nav-list-item" {{action "showSubview" "email-settings"}} data-test-button="email-settings">
|
||||
<button type="button">
|
||||
<b>Email newsletter</b>
|
||||
<span>Customize email settings</span>
|
||||
</button>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="nav-list-item" {{action "showSubview" "codeinjection"}} data-test-button="codeinjection">
|
||||
<button type="button">
|
||||
<b>Code injection</b>
|
||||
<span>Add styles/scripts to the header & footer</span>
|
||||
</button>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{#unless this.session.user.isAuthorOrContributor}}
|
||||
<div class="form-group for-checkbox">
|
||||
<label class="checkbox" for="featured" {{action "toggleFeatured" bubbles="false"}}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={{this.post.featured}}
|
||||
class="gh-input post-settings-featured"
|
||||
onclick={{action (mut this.post.featured) value="target.checked"}}
|
||||
data-test-checkbox="featured"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Feature this {{this.post.displayName}}</p>
|
||||
</label>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<GhPsmTemplateSelect
|
||||
@post={{this.post}}
|
||||
@onTemplateSelect={{action (mut this.post.customTemplate)}} />
|
||||
|
||||
{{#unless this.post.isNew}}
|
||||
<button type="button" class="gh-btn gh-btn-hover-red gh-btn-icon settings-menu-delete-button" {{action "deletePost"}}><span>{{svg-jar "trash"}} Delete {{this.post.displayName}}</span></button>
|
||||
{{/unless}}
|
||||
|
||||
</form>
|
||||
</div>{{! .settings-menu-content }}
|
||||
</div>{{! .post-settings-menu }}
|
||||
|
||||
{{#if this.isViewingSubview}}
|
||||
<div class="settings-menu settings-menu-pane {{unless (eq this.subview "email-settings") "settings-menu-pane-wide"}}">
|
||||
<div class="active">
|
||||
{{#if (eq this.subview "meta-data")}}
|
||||
<div class="settings-menu-header subview labs">
|
||||
<button aria-label="Back" {{action "closeSubview"}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
<h4>Meta data</h4>
|
||||
<div style="width:23px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-menu-content labs">
|
||||
<form {{action "discardEnter" on="submit"}}>
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="metaTitle">
|
||||
<label for="meta-title">Meta title</label>
|
||||
<GhTextInput
|
||||
@class="post-setting-meta-title"
|
||||
@id="meta-title"
|
||||
@name="post-setting-meta-title"
|
||||
@placeholder={{this.seoTitle}}
|
||||
@value={{readonly this.metaTitleScratch}}
|
||||
@input={{action (mut this.metaTitleScratch) value="target.value"}}
|
||||
@focus-out={{action "setMetaTitle" this.metaTitleScratch}}
|
||||
@stopEnterKeyDownPropagation={{true}}
|
||||
data-test-field="meta-title" />
|
||||
<p>Recommended: <b>60</b> characters. You’ve used {{gh-count-down-characters this.metaTitleScratch 60}}</p>
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="meta-title" />
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="metaDescription">
|
||||
<label for="meta-description">Meta description</label>
|
||||
<GhTextarea
|
||||
@class="post-setting-meta-description"
|
||||
@id="meta-description"
|
||||
@name="post-setting-meta-description"
|
||||
@placeholder={{truncate this.seoDescription 150}}
|
||||
@value={{readonly this.metaDescriptionScratch}}
|
||||
@input={{action (mut this.metaDescriptionScratch) value="target.value"}}
|
||||
@focus-out={{action "setMetaDescription" this.metaDescriptionScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
data-test-field="meta-description" />
|
||||
<p>Recommended: <b>145</b> characters. You’ve used {{gh-count-down-characters this.metaDescriptionScratch 145}}</p>
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="meta-description" />
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="canonicalUrl">
|
||||
<label for="canonicalUrl">Canonical URL</label>
|
||||
<GhTextInput
|
||||
@class="post-setting-canonicalUrl"
|
||||
@name="post-setting-canonicalUrl"
|
||||
@value={{readonly this.canonicalUrlScratch}}
|
||||
@input={{action (mut this.canonicalUrlScratch) value="target.value"}}
|
||||
@focus-out={{action "setCanonicalUrl" this.canonicalUrlScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
data-test-field="canonicalUrl" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="canonicalUrl" />
|
||||
</GhFormGroup>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Search Engine Result Preview</label>
|
||||
<div class="gh-seo-container">
|
||||
<div class="gh-seo-preview">
|
||||
<div class="flex mb7">
|
||||
{{svg-jar "google"}}
|
||||
<div class="gh-seo-search-bar">{{svg-jar "google-search"}}</div>
|
||||
</div>
|
||||
<div class="gh-seo-preview-link">{{this.seoURL}}</div>
|
||||
<div class="gh-seo-preview-title">{{truncate this.seoTitle 60}}</div>
|
||||
<div class="gh-seo-preview-desc">{{moment-format (now) "DD MMM YYYY"}} — {{truncate this.seoDescription 149}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.subview "twitter-data")}}
|
||||
<div class="settings-menu-header subview labs">
|
||||
<button aria-label="Back" {{action "closeSubview"}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
<h4>Twitter card</h4>
|
||||
<div style="width:23px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-menu-content labs">
|
||||
|
||||
<form {{action "discardEnter" on="submit"}}>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.post.twitterImage}}
|
||||
@text="Add Twitter image"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setTwitterImage"}}
|
||||
@remove={{action "clearTwitterImage"}}
|
||||
/>
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="twitterTitle">
|
||||
<label for="twitter-title">Twitter title</label>
|
||||
<GhTextInput
|
||||
@class="post-setting-twitter-title"
|
||||
@id="twitter-title"
|
||||
@name="post-setting-twitter-title"
|
||||
@placeholder={{truncate this.twitterTitle 40}}
|
||||
@value={{readonly this.twitterTitleScratch}}
|
||||
@input={{action (mut this.twitterTitleScratch) value="target.value"}}
|
||||
@focus-out={{action "setTwitterTitle" this.twitterTitleScratch}}
|
||||
@stopEnterKeyDownPropagation={{true}}
|
||||
data-test-field="twitter-title" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="twitterTitle" data-test-error="twitter-title" />
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="twitterDescription">
|
||||
<label for="twitter-description">Twitter description</label>
|
||||
<GhTextarea
|
||||
@class="post-setting-twitter-description"
|
||||
@id="twitter-description"
|
||||
@name="post-setting-twitter-description"
|
||||
@placeholder={{truncate this.twitterDescription 150}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
@value={{readonly this.twitterDescriptionScratch}}
|
||||
@input={{action (mut this.twitterDescriptionScratch) value="target.value"}}
|
||||
@focus-out={{action "setTwitterDescription" this.twitterDescriptionScratch}}
|
||||
data-test-field="twitter-description" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="twitterDescription" data-test-error="twitter-description" />
|
||||
</GhFormGroup>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Twitter preview</label>
|
||||
<div class="gh-social-twitter-post-preview">
|
||||
{{#if this.twitterImage}}
|
||||
<div class="gh-social-twitter-preview-image" style={{background-image-style this.twitterImage}}></div>
|
||||
{{/if}}
|
||||
<div class="gh-social-twitter-preview-content">
|
||||
<div class="gh-social-twitter-preview-title">{{this.twitterTitle}}</div>
|
||||
<div class="gh-social-twitter-preview-desc">{{truncate this.twitterDescription}}</div>
|
||||
<div class="gh-social-twitter-preview-meta">
|
||||
{{svg-jar "twitter-link"}}
|
||||
{{this.config.blogDomain}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.subview "email-settings")}}
|
||||
<GhPostSettingsMenu::Email
|
||||
@post={{this.post}}
|
||||
@savePostTask={{this.savePostTask}}
|
||||
@toggleEmailPreviewModal={{this.toggleEmailPreviewModal}}
|
||||
@close={{action "closeSubview"}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.subview "facebook-data")}}
|
||||
<div class="settings-menu-header subview labs">
|
||||
<button aria-label="Back" {{action "closeSubview"}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
<h4>Facebook card</h4>
|
||||
<div style="width:23px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-menu-content labs">
|
||||
<form {{action "discardEnter" on="submit"}}>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.post.ogImage}}
|
||||
@text="Add Facebook image"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setOgImage"}}
|
||||
@remove={{action "clearOgImage"}}
|
||||
/>
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="ogTitle">
|
||||
<label for="og-title">Facebook title</label>
|
||||
<GhTextInput
|
||||
@class="post-setting-og-title"
|
||||
@id="og-title"
|
||||
@name="post-setting-og-title"
|
||||
@placeholder={{truncate this.facebookTitle 40}}
|
||||
@value={{readonly this.ogTitleScratch}}
|
||||
@input={{action (mut this.ogTitleScratch) value="target.value"}}
|
||||
@focus-out={{action "setOgTitle" this.ogTitleScratch}}
|
||||
@stopEnterKeyDownPropagation={{true}}
|
||||
data-test-field="og-title" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="ogTitle" data-test-error="og-title" />
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="ogDescription">
|
||||
<label for="og-description">Facebook description</label>
|
||||
<GhTextarea
|
||||
@class="post-setting-og-description"
|
||||
@id="og-description" @name="post-setting-og-description"
|
||||
@placeholder={{truncate this.facebookDescription 150}}
|
||||
@value={{readonly this.ogDescriptionScratch}}
|
||||
@input={{action (mut this.ogDescriptionScratch) value="target.value"}}
|
||||
@focus-out={{action "setOgDescription" this.ogDescriptionScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
data-test-field="og-description" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="ogDescription" data-test-error="og-description" />
|
||||
</GhFormGroup>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Facebook preview</label>
|
||||
<div class="gh-social-og-preview no-container">
|
||||
{{#if this.facebookImage}}
|
||||
<div class="gh-social-og-preview-image" style={{background-image-style this.facebookImage}}></div>
|
||||
{{/if}}
|
||||
<div class="gh-social-og-preview-bookmark">
|
||||
{{!-- Ensures description is hidden if title exceeds one line --}}
|
||||
<div class="gh-social-og-preview-content">
|
||||
<div class="gh-social-og-preview-meta">
|
||||
{{this.config.blogDomain}}
|
||||
</div>
|
||||
<div class="gh-social-og-preview-title">{{truncate this.facebookTitle}}</div>
|
||||
<div class="gh-social-og-preview-desc">{{truncate this.facebookDescription}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.subview "codeinjection")}}
|
||||
<div class="settings-menu-header subview labs">
|
||||
<button aria-label="Back" {{action "closeSubview"}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
<h4>Code injection</h4>
|
||||
<div style="width:23px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-menu-content labs settings-menu-content-codeinjection">
|
||||
<form {{action "discardEnter" on="submit"}}>
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="codeinjectionHead">
|
||||
<label for="codeinjection-head">{{capitalize this.post.displayName}} header <code>\{{ghost_head}}</code></label>
|
||||
<GhCmEditor @value={{this.codeinjectionHeadScratch}}
|
||||
@id="post-setting-codeinjection-head"
|
||||
@class="post-setting-codeinjection"
|
||||
@name="post-setting-codeinjection-head"
|
||||
@focusOut={{action "setHeaderInjection" this.codeinjectionHeadScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
@update={{action (mut this.codeinjectionHeadScratch)}}
|
||||
data-test-field="codeinjection-head" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="codeinjectionHead" data-test-error="codeinjection-head" />
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.post.errors}} @hasValidated={{this.post.hasValidated}} @property="codeinjectionFoot">
|
||||
<label for="codeinjection-foot">{{capitalize this.post.displayName}} footer <code>\{{ghost_foot}}</code></label>
|
||||
<GhCmEditor @value={{this.codeinjectionFootScratch}}
|
||||
@id="post-setting-codeinjection-foot"
|
||||
@class="post-setting-codeinjection"
|
||||
@name="post-setting-codeinjection-foot"
|
||||
@focusOut={{action "setFooterInjection" this.codeinjectionFootScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
@update={{action (mut this.codeinjectionFootScratch)}}
|
||||
data-test-field="codeinjection-foot" />
|
||||
<GhErrorMessage @errors={{this.post.errors}} @property="codeinjectionFoot" data-test-error="codeinjection-foot" />
|
||||
</GhFormGroup>
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
@ -1,515 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
||||
import moment from 'moment';
|
||||
import {action} from '@ember/object';
|
||||
import {alias, or} from '@ember/object/computed';
|
||||
import {computed} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default Component.extend({
|
||||
feature: service(),
|
||||
store: service(),
|
||||
config: service(),
|
||||
ajax: service(),
|
||||
ghostPaths: service(),
|
||||
notifications: service(),
|
||||
slugGenerator: service(),
|
||||
session: service(),
|
||||
settings: service(),
|
||||
ui: service(),
|
||||
|
||||
tagName: '',
|
||||
|
||||
post: null,
|
||||
|
||||
showSettingsMenu: false,
|
||||
_showSettingsMenu: false,
|
||||
|
||||
canonicalUrlScratch: alias('post.canonicalUrlScratch'),
|
||||
customExcerptScratch: alias('post.customExcerptScratch'),
|
||||
codeinjectionFootScratch: alias('post.codeinjectionFootScratch'),
|
||||
codeinjectionHeadScratch: alias('post.codeinjectionHeadScratch'),
|
||||
metaDescriptionScratch: alias('post.metaDescriptionScratch'),
|
||||
metaTitleScratch: alias('post.metaTitleScratch'),
|
||||
ogDescriptionScratch: alias('post.ogDescriptionScratch'),
|
||||
ogTitleScratch: alias('post.ogTitleScratch'),
|
||||
twitterDescriptionScratch: alias('post.twitterDescriptionScratch'),
|
||||
twitterTitleScratch: alias('post.twitterTitleScratch'),
|
||||
slugValue: boundOneWay('post.slug'),
|
||||
|
||||
seoDescription: or('metaDescriptionScratch', 'customExcerptScratch', 'post.excerpt'),
|
||||
facebookDescription: or('ogDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
|
||||
facebookImage: or('post.ogImage', 'post.featureImage', 'settings.ogImage', 'settings.coverImage'),
|
||||
facebookTitle: or('ogTitleScratch', 'seoTitle'),
|
||||
twitterDescription: or('twitterDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
|
||||
twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'),
|
||||
twitterTitle: or('twitterTitleScratch', 'seoTitle'),
|
||||
|
||||
showVisibilityInput: or('session.user.isOwner', 'session.user.isAdmin', 'session.user.isEditor'),
|
||||
showEmailNewsletter: or('session.user.isOwner', 'session.user.isAdmin', 'session.user.isEditor'),
|
||||
|
||||
seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () {
|
||||
return this.metaTitleScratch || this.post.titleScratch || '(Untitled)';
|
||||
}),
|
||||
|
||||
seoURL: computed('post.{slug,canonicalUrl}', 'config.blogUrl', function () {
|
||||
const urlParts = [];
|
||||
|
||||
if (this.post.canonicalUrl) {
|
||||
const canonicalUrl = new URL(this.post.canonicalUrl);
|
||||
urlParts.push(canonicalUrl.host);
|
||||
urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p));
|
||||
} else {
|
||||
const blogUrl = new URL(this.config.get('blogUrl'));
|
||||
urlParts.push(blogUrl.host);
|
||||
urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
|
||||
urlParts.push(this.post.slug);
|
||||
}
|
||||
|
||||
return urlParts.join(' > ');
|
||||
}),
|
||||
|
||||
isViewingSubview: computed('showSettingsMenu', {
|
||||
get() {
|
||||
return false;
|
||||
},
|
||||
set(key, value) {
|
||||
// Not viewing a subview if we can't even see the PSM
|
||||
if (!this.showSettingsMenu) {
|
||||
return false;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
// fired when menu is closed
|
||||
if (!this.showSettingsMenu && this._showSettingsMenu) {
|
||||
let post = this.post;
|
||||
let errors = post.get('errors');
|
||||
|
||||
// reset the publish date if it has an error
|
||||
if (errors.has('publishedAtBlogDate') || errors.has('publishedAtBlogTime')) {
|
||||
post.set('publishedAtBlogTZ', post.get('publishedAtUTC'));
|
||||
post.validate({attribute: 'publishedAtBlog'});
|
||||
}
|
||||
}
|
||||
|
||||
this._showSettingsMenu = this.showSettingsMenu;
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this.setSidebarWidthVariable(0);
|
||||
},
|
||||
|
||||
actions: {
|
||||
showSubview(subview) {
|
||||
this.set('isViewingSubview', true);
|
||||
this.set('subview', subview);
|
||||
},
|
||||
|
||||
closeSubview() {
|
||||
this.set('isViewingSubview', false);
|
||||
this.set('subview', null);
|
||||
},
|
||||
|
||||
discardEnter() {
|
||||
return false;
|
||||
},
|
||||
|
||||
toggleFeatured() {
|
||||
this.toggleProperty('post.featured');
|
||||
|
||||
// If this is a new post. Don't save the post. Defer the save
|
||||
// to the user pressing the save button
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* triggered by user manually changing slug
|
||||
*/
|
||||
updateSlug(newSlug) {
|
||||
return this.updateSlugTask
|
||||
.perform(newSlug)
|
||||
.catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
async setVisibility(segment) {
|
||||
this.post.set('visibility', segment);
|
||||
try {
|
||||
await this.post.validate({property: 'visibility'});
|
||||
if (this.post.changedAttributes().visibility) {
|
||||
await this.savePostTask.perform();
|
||||
}
|
||||
} catch (e) {
|
||||
if (!e) {
|
||||
// validation error
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
setPublishedAtBlogDate(date) {
|
||||
let post = this.post;
|
||||
let dateString = moment(date).format('YYYY-MM-DD');
|
||||
|
||||
post.get('errors').remove('publishedAtBlogDate');
|
||||
|
||||
if (post.get('isNew') || date === post.get('publishedAtBlogDate')) {
|
||||
post.validate({property: 'publishedAtBlog'});
|
||||
} else {
|
||||
post.set('publishedAtBlogDate', dateString);
|
||||
return this.savePostTask.perform();
|
||||
}
|
||||
},
|
||||
|
||||
setPublishedAtBlogTime(time) {
|
||||
let post = this.post;
|
||||
|
||||
post.get('errors').remove('publishedAtBlogDate');
|
||||
|
||||
if (post.get('isNew') || time === post.get('publishedAtBlogTime')) {
|
||||
post.validate({property: 'publishedAtBlog'});
|
||||
} else {
|
||||
post.set('publishedAtBlogTime', time);
|
||||
return this.savePostTask.perform();
|
||||
}
|
||||
},
|
||||
|
||||
setCustomExcerpt(excerpt) {
|
||||
let post = this.post;
|
||||
let currentExcerpt = post.get('customExcerpt');
|
||||
|
||||
if (excerpt === currentExcerpt) {
|
||||
return;
|
||||
}
|
||||
|
||||
post.set('customExcerpt', excerpt);
|
||||
|
||||
return post.validate({property: 'customExcerpt'}).then(() => this.savePostTask.perform());
|
||||
},
|
||||
|
||||
setHeaderInjection(code) {
|
||||
let post = this.post;
|
||||
let currentCode = post.get('codeinjectionHead');
|
||||
|
||||
if (code === currentCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
post.set('codeinjectionHead', code);
|
||||
|
||||
return post.validate({property: 'codeinjectionHead'}).then(() => this.savePostTask.perform());
|
||||
},
|
||||
|
||||
setFooterInjection(code) {
|
||||
let post = this.post;
|
||||
let currentCode = post.get('codeinjectionFoot');
|
||||
|
||||
if (code === currentCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
post.set('codeinjectionFoot', code);
|
||||
|
||||
return post.validate({property: 'codeinjectionFoot'}).then(() => this.savePostTask.perform());
|
||||
},
|
||||
|
||||
setMetaTitle(metaTitle) {
|
||||
// Grab the post and current stored meta title
|
||||
let post = this.post;
|
||||
let currentTitle = post.get('metaTitle');
|
||||
|
||||
// If the title entered matches the stored meta title, do nothing
|
||||
if (currentTitle === metaTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the title entered is different, set it as the new meta title
|
||||
post.set('metaTitle', metaTitle);
|
||||
|
||||
// Make sure the meta title is valid and if so, save it into the post
|
||||
return post.validate({property: 'metaTitle'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setMetaDescription(metaDescription) {
|
||||
// Grab the post and current stored meta description
|
||||
let post = this.post;
|
||||
let currentDescription = post.get('metaDescription');
|
||||
|
||||
// If the title entered matches the stored meta title, do nothing
|
||||
if (currentDescription === metaDescription) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the title entered is different, set it as the new meta title
|
||||
post.set('metaDescription', metaDescription);
|
||||
|
||||
// Make sure the meta title is valid and if so, save it into the post
|
||||
return post.validate({property: 'metaDescription'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setCanonicalUrl(value) {
|
||||
// Grab the post and current stored meta description
|
||||
let post = this.post;
|
||||
let currentCanonicalUrl = post.canonicalUrl;
|
||||
|
||||
// If the value entered matches the stored value, do nothing
|
||||
if (currentCanonicalUrl === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value supplied is different, set it as the new value
|
||||
post.set('canonicalUrl', value);
|
||||
|
||||
// Make sure the value is valid and if so, save it into the post
|
||||
return post.validate({property: 'canonicalUrl'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setOgTitle(ogTitle) {
|
||||
// Grab the post and current stored facebook title
|
||||
let post = this.post;
|
||||
let currentTitle = post.get('ogTitle');
|
||||
|
||||
// If the title entered matches the stored facebook title, do nothing
|
||||
if (currentTitle === ogTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the title entered is different, set it as the new facebook title
|
||||
post.set('ogTitle', ogTitle);
|
||||
|
||||
// Make sure the facebook title is valid and if so, save it into the post
|
||||
return post.validate({property: 'ogTitle'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setOgDescription(ogDescription) {
|
||||
// Grab the post and current stored facebook description
|
||||
let post = this.post;
|
||||
let currentDescription = post.get('ogDescription');
|
||||
|
||||
// If the title entered matches the stored facebook description, do nothing
|
||||
if (currentDescription === ogDescription) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the description entered is different, set it as the new facebook description
|
||||
post.set('ogDescription', ogDescription);
|
||||
|
||||
// Make sure the facebook description is valid and if so, save it into the post
|
||||
return post.validate({property: 'ogDescription'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setTwitterTitle(twitterTitle) {
|
||||
// Grab the post and current stored twitter title
|
||||
let post = this.post;
|
||||
let currentTitle = post.get('twitterTitle');
|
||||
|
||||
// If the title entered matches the stored twitter title, do nothing
|
||||
if (currentTitle === twitterTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the title entered is different, set it as the new twitter title
|
||||
post.set('twitterTitle', twitterTitle);
|
||||
|
||||
// Make sure the twitter title is valid and if so, save it into the post
|
||||
return post.validate({property: 'twitterTitle'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setTwitterDescription(twitterDescription) {
|
||||
// Grab the post and current stored twitter description
|
||||
let post = this.post;
|
||||
let currentDescription = post.get('twitterDescription');
|
||||
|
||||
// If the description entered matches the stored twitter description, do nothing
|
||||
if (currentDescription === twitterDescription) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the description entered is different, set it as the new twitter description
|
||||
post.set('twitterDescription', twitterDescription);
|
||||
|
||||
// Make sure the twitter description is valid and if so, save it into the post
|
||||
return post.validate({property: 'twitterDescription'}).then(() => {
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.savePostTask.perform();
|
||||
});
|
||||
},
|
||||
|
||||
setCoverImage(image) {
|
||||
this.set('post.featureImage', image);
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
clearCoverImage() {
|
||||
this.set('post.featureImage', '');
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
setOgImage(image) {
|
||||
this.set('post.ogImage', image);
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
clearOgImage() {
|
||||
this.set('post.ogImage', '');
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
setTwitterImage(image) {
|
||||
this.set('post.twitterImage', image);
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
clearTwitterImage() {
|
||||
this.set('post.twitterImage', '');
|
||||
|
||||
if (this.get('post.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
changeAuthors(newAuthors) {
|
||||
let post = this.post;
|
||||
|
||||
// return if nothing changed
|
||||
if (newAuthors.mapBy('id').join() === post.get('authors').mapBy('id').join()) {
|
||||
return;
|
||||
}
|
||||
|
||||
post.set('authors', newAuthors);
|
||||
post.validate({property: 'authors'});
|
||||
|
||||
// if this is a new post (never been saved before), don't try to save it
|
||||
if (post.get('isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savePostTask.perform().catch((error) => {
|
||||
this.showError(error);
|
||||
post.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
deletePost() {
|
||||
if (this.deletePost) {
|
||||
this.deletePost();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showError(error) {
|
||||
// TODO: remove null check once ValidationEngine has been removed
|
||||
if (error) {
|
||||
this.notifications.showAPIError(error);
|
||||
}
|
||||
},
|
||||
|
||||
setSidebarWidthFromElement: action(function (element) {
|
||||
const width = element.getBoundingClientRect().width;
|
||||
this.setSidebarWidthVariable(width);
|
||||
}),
|
||||
|
||||
setSidebarWidthVariable(width) {
|
||||
document.documentElement.style.setProperty('--editor-sidebar-width', `${width}px`);
|
||||
}
|
||||
});
|
@ -1,11 +1,12 @@
|
||||
<div class="settings-menu-container {{if (and this.isViewingSubview (not (eq this.subview "email-settings"))) "settings-menu-container-wide"}}">
|
||||
<div
|
||||
class="settings-menu-container {{if (and this.isViewingSubview (not (eq this.subview "email-settings"))) "settings-menu-container-wide"}}"
|
||||
{{did-insert this.setSidebarWidthFromElement}}
|
||||
{{did-update this.setSidebarWidthFromElement this.isViewingSubview}}
|
||||
>
|
||||
<div id="entry-controls">
|
||||
<div class="{{if this.isViewingSubview 'settings-menu-pane-out-left' 'settings-menu-pane-in'}} settings-menu settings-menu-pane">
|
||||
<div class="settings-menu settings-menu-pane settings-menu-pane-main">
|
||||
<div class="settings-menu-header">
|
||||
<h4>{{capitalize this.post.displayName}} settings</h4>
|
||||
<button aria-label="Close" class="close settings-menu-header-action" {{action "closeMenus" target=this.ui}} data-test-close-settings-menu>
|
||||
{{svg-jar "close"}}<span class="hidden">Close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="settings-menu-content">
|
||||
<form>
|
||||
@ -17,7 +18,7 @@
|
||||
<a class="post-view-link" target="_blank" href="{{this.post.url}}">
|
||||
View {{this.post.displayName}} {{svg-jar "external"}}
|
||||
</a>
|
||||
{{else}}
|
||||
{{else if this.post.isScheduled}}
|
||||
<a class="post-view-link" target="_blank" href="{{this.post.previewUrl}}">
|
||||
Preview {{svg-jar "external"}}
|
||||
</a>
|
||||
@ -202,9 +203,9 @@
|
||||
</div>{{! .settings-menu-content }}
|
||||
</div>{{! .post-settings-menu }}
|
||||
|
||||
<div class="{{if this.isViewingSubview 'settings-menu-pane-in' 'settings-menu-pane-out-right'}} settings-menu settings-menu-pane {{unless (eq this.subview "email-settings") "settings-menu-pane-wide"}}">
|
||||
<div class="active">
|
||||
{{#if this.isViewingSubview}}
|
||||
<div class="settings-menu settings-menu-pane {{unless (eq this.subview "email-settings") "settings-menu-pane-wide"}}">
|
||||
<div class="active">
|
||||
{{#if (eq this.subview "meta-data")}}
|
||||
<div class="settings-menu-header subview">
|
||||
<button aria-label="Back" {{action "closeSubview"}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
@ -459,8 +460,8 @@
|
||||
</form>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Component from '@ember/component';
|
||||
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
||||
import moment from 'moment';
|
||||
import {action} from '@ember/object';
|
||||
import {alias, or} from '@ember/object/computed';
|
||||
import {computed} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
@ -17,6 +18,8 @@ export default Component.extend({
|
||||
settings: service(),
|
||||
ui: service(),
|
||||
|
||||
tagName: '',
|
||||
|
||||
post: null,
|
||||
|
||||
showSettingsMenu: false,
|
||||
@ -97,6 +100,10 @@ export default Component.extend({
|
||||
this._showSettingsMenu = this.showSettingsMenu;
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this.setSidebarWidthVariable(0);
|
||||
},
|
||||
|
||||
actions: {
|
||||
showSubview(subview) {
|
||||
this.set('isViewingSubview', true);
|
||||
@ -495,5 +502,14 @@ export default Component.extend({
|
||||
if (error) {
|
||||
this.notifications.showAPIError(error);
|
||||
}
|
||||
},
|
||||
|
||||
setSidebarWidthFromElement: action(function (element) {
|
||||
const width = element.getBoundingClientRect().width;
|
||||
this.setSidebarWidthVariable(width);
|
||||
}),
|
||||
|
||||
setSidebarWidthVariable(width) {
|
||||
document.documentElement.style.setProperty('--editor-sidebar-width', `${width}px`);
|
||||
}
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class="settings-menu-header subview {{if (feature "psmRedesign") "labs"}}">
|
||||
<div class="settings-menu-header subview">
|
||||
<button {{on "click" this.close}} class="back settings-menu-header-action" data-test-button="close-psm-subview">{{svg-jar "arrow-left"}}<span class="hidden">Back</span></button>
|
||||
<h4>Email newsletter</h4>
|
||||
<div style="width:23px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-menu-content settings-menu-email {{if (feature "psmRedesign") "labs"}}">
|
||||
<div class="settings-menu-content settings-menu-email">
|
||||
{{#if this.post.email.isSuccess}}
|
||||
{{!-- Mail has already been sent --}}
|
||||
<div class="ba b--whitegrey bg-white br3">
|
||||
|
@ -55,7 +55,6 @@ export default Service.extend({
|
||||
launchComplete: feature('launchComplete', {user: true}),
|
||||
matchHelper: feature('matchHelper'),
|
||||
multipleProducts: feature('multipleProducts', {developer: true}),
|
||||
psmRedesign: feature('psmRedesign', {developer: true}),
|
||||
emailCardSegments: feature('emailCardSegments', {developer: true}),
|
||||
|
||||
_user: null,
|
||||
|
@ -43,7 +43,6 @@ export default class UiService extends Service {
|
||||
@tracked isFullScreen = false;
|
||||
@tracked mainClass = '';
|
||||
@tracked showMobileMenu = false;
|
||||
@tracked showSettingsMenu = false;
|
||||
|
||||
get isMobile() {
|
||||
return this.mediaQueries.isMobile;
|
||||
@ -73,7 +72,6 @@ export default class UiService extends Service {
|
||||
@action
|
||||
closeMenus() {
|
||||
this.dropdown.closeDropdowns();
|
||||
this.showSettingsMenu = false;
|
||||
this.showMobileMenu = false;
|
||||
}
|
||||
|
||||
@ -87,11 +85,6 @@ export default class UiService extends Service {
|
||||
this.showMobileMenu = true;
|
||||
}
|
||||
|
||||
@action
|
||||
openSettingsMenu() {
|
||||
this.showSettingsMenu = true;
|
||||
}
|
||||
|
||||
@action
|
||||
setMainClass(mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
|
@ -435,20 +435,6 @@ input:focus,
|
||||
color: var(--midgrey);
|
||||
}
|
||||
|
||||
|
||||
/* delete when psmRedesign flag is removed */
|
||||
.gh-editor-header-small {
|
||||
background: var(--dark-main-bg-color);
|
||||
}
|
||||
/* END delete */
|
||||
|
||||
/* psmRedesign labs feature */
|
||||
@media (max-width: 1024px) {
|
||||
.gh-editor-header.labs {
|
||||
background-color: var(--dark-main-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.gh-markdown-editor .CodeMirror-cursor {
|
||||
border-color: #fff;
|
||||
}
|
||||
@ -505,7 +491,6 @@ input:focus,
|
||||
background: var(--lightgrey);
|
||||
}
|
||||
|
||||
.settings-menu-expanded .content-cover,
|
||||
.mobile-menu-expanded .content-cover {
|
||||
background: transparent;
|
||||
}
|
||||
@ -711,6 +696,12 @@ input:focus,
|
||||
}
|
||||
|
||||
/* Editor */
|
||||
@media (max-width: 1024px) {
|
||||
.gh-editor-header {
|
||||
background-color: var(--dark-main-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.gh-editor-back-button {
|
||||
background: var(--dark-main-bg-color);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.gh-publishmenu-dropdown {
|
||||
|
@ -1,27 +1,69 @@
|
||||
/* Settings Menu
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
/* Toggle button
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.settings-menu-toggle {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
right: 24px;
|
||||
z-index: 9999;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.settings-menu-toggle svg {
|
||||
fill: none !important;
|
||||
}
|
||||
|
||||
.settings-menu-toggle .settings-menu-open svg path {
|
||||
stroke: var(--black);
|
||||
}
|
||||
|
||||
@media (min-width: 500px) and (max-width: 1024px) {
|
||||
.settings-menu-toggle {
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.settings-menu-toggle {
|
||||
top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-toggle-spacer {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-toggle-spacer {
|
||||
width: 68px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Container
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.settings-menu-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: calc(380px - 500px);
|
||||
bottom: 0;
|
||||
z-index: 500;
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--white);
|
||||
transition: all 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transform: translate3d(510px, 0px, 0px);
|
||||
box-shadow: none;
|
||||
z-index: 999;
|
||||
height: 100vh;
|
||||
min-width: 380px;
|
||||
overflow-x: visible;
|
||||
overflow-y: auto;
|
||||
border-left: 1px solid var(--whitegrey-d1);
|
||||
}
|
||||
|
||||
.settings-menu-expanded .settings-menu-container {
|
||||
transform: translate3d(0, 0px, 0px);
|
||||
.settings-menu-container-wide {
|
||||
width: 501px;
|
||||
min-width: 501px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-container {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
box-shadow:
|
||||
-4.5px 0 3.6px rgba(0, 0, 0, 0.007),
|
||||
-12.5px 0 10px rgba(0, 0, 0, 0.008),
|
||||
@ -29,56 +71,64 @@
|
||||
-100px 0 80px rgba(0, 0, 0, 0.02)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.settings-menu-container {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-container .settings-menu-pane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
overflow: auto;
|
||||
background-color: white;
|
||||
opacity: 1;
|
||||
width: 380px;
|
||||
transform: translate3d(0, 0px, 0px);
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.settings-menu-container .settings-menu-pane-wide {
|
||||
.settings-menu-container-wide .settings-menu-pane {
|
||||
width: 500px;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.settings-menu-container-wide {
|
||||
right: 0;
|
||||
.settings-menu-container .settings-menu-pane-main {
|
||||
position: relative;
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@media (min-width: 901px) {
|
||||
.settings-menu-container .settings-menu-pane {
|
||||
transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-container .settings-menu-pane.settings-menu-pane-out-left {
|
||||
transform: translate3d(-100%, 0px, 0px);
|
||||
}
|
||||
|
||||
.settings-menu-container .settings-menu-pane.settings-menu-pane-out-right {
|
||||
transform: translate3d(100%, 0px, 0px);
|
||||
}
|
||||
|
||||
.settings-menu-container .settings-menu-pane.settings-menu-pane-in {
|
||||
transform: translate3d(0, 0px, 0px);
|
||||
}
|
||||
|
||||
|
||||
/* Header
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
.settings-menu-header {
|
||||
position: relative;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 364px;
|
||||
padding: 36px 24px 24px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 24px;
|
||||
z-index: 1;
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
@media (min-width: 500px) and (max-width: 1024px) {
|
||||
.settings-menu-header {
|
||||
padding-top: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.settings-menu-header {
|
||||
padding-top: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-header h4 {
|
||||
@ -101,19 +151,26 @@
|
||||
fill: var(--darkgrey);
|
||||
}
|
||||
|
||||
.settings-menu-header.subview {
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 484px;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview h4 {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview .back {
|
||||
margin-left: -15px;
|
||||
padding: 10px 15px;
|
||||
padding: 2px 15px 0;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview .back svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview .back svg path {
|
||||
@ -138,7 +195,13 @@
|
||||
}
|
||||
|
||||
.settings-menu-content {
|
||||
padding: 0 24px 24px;
|
||||
padding: 92px 24px 33px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-content {
|
||||
padding-top: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-content label code {
|
||||
@ -333,7 +396,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-expanded .content-cover,
|
||||
.mobile-menu-expanded .content-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -348,10 +410,6 @@
|
||||
animation: coverFadeIn 0.3s ease 0.1s 1 forwards;
|
||||
}
|
||||
|
||||
.settings-menu-expanded .content-cover {
|
||||
transform: translate3d(-350px, 0px, 0px);
|
||||
}
|
||||
|
||||
.mobile-menu-expanded .content-cover {
|
||||
transform: translate3d(205px, 0px, 0px);
|
||||
}
|
||||
@ -376,169 +434,3 @@
|
||||
.settings-menu-email .disabled input {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
/* LABS ----------------------------------------------------------------------*/
|
||||
|
||||
.settings-menu-container-labs {
|
||||
z-index: 999;
|
||||
height: 100vh;
|
||||
min-width: 380px;
|
||||
overflow-x: visible;
|
||||
overflow-y: auto;
|
||||
border-left: 1px solid var(--whitegrey-d1);
|
||||
}
|
||||
|
||||
.settings-menu-container-labs-wide {
|
||||
width: 501px;
|
||||
min-width: 501px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-container-labs {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
box-shadow:
|
||||
-4.5px 0 3.6px rgba(0, 0, 0, 0.007),
|
||||
-12.5px 0 10px rgba(0, 0, 0, 0.008),
|
||||
-30.1px 0 24.1px rgba(0, 0, 0, 0.01),
|
||||
-100px 0 80px rgba(0, 0, 0, 0.02)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.settings-menu-container-labs {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-toggle-labs {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
right: 24px;
|
||||
z-index: 9999;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.settings-menu-toggle-labs svg {
|
||||
fill: none !important;
|
||||
}
|
||||
|
||||
.settings-menu-toggle-labs .settings-menu-open svg path {
|
||||
stroke: var(--black);
|
||||
}
|
||||
|
||||
@media (min-width: 500px) and (max-width: 1024px) {
|
||||
.settings-menu-toggle-labs {
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.settings-menu-toggle-labs {
|
||||
top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-toggle-spacer-labs {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-toggle-spacer-labs {
|
||||
width: 68px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-container-labs .settings-menu-pane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
overflow: auto;
|
||||
background-color: white;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settings-menu-container-labs-wide .settings-menu-pane {
|
||||
width: 500px;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.settings-menu-container-labs .settings-menu-pane-main {
|
||||
position: relative;
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.settings-menu-header.labs {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
max-width: 364px;
|
||||
padding: 36px 24px 24px;
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
@media (min-width: 500px) and (max-width: 1024px) {
|
||||
.settings-menu-header.labs {
|
||||
padding-top: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.settings-menu-header.labs {
|
||||
padding-top: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-content.labs {
|
||||
padding: 92px 24px 33px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.settings-menu-content.labs {
|
||||
padding-top: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-menu-header.subview.labs {
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 484px;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview.labs .back {
|
||||
padding: 2px 15px 0 !important;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview.labs .back svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.settings-menu-header.subview.labs h4 {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.gh-publishmenu-labs {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.gh-editor-header.labs {
|
||||
z-index: 100;
|
||||
height: 64px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 15px;
|
||||
background-color: var(--white);
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
@ -318,6 +318,18 @@
|
||||
z-index: 799;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.gh-editor-header {
|
||||
z-index: 100;
|
||||
height: 64px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 15px;
|
||||
background-color: var(--white);
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gh-btn-editor {
|
||||
background: var(--white) !important;
|
||||
}
|
||||
@ -344,41 +356,6 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* delete when psmRedesign flag is removed */
|
||||
@media (max-width: 750px) {
|
||||
.gh-editor-header {
|
||||
padding: 0 4vw;
|
||||
}
|
||||
}
|
||||
|
||||
.gh-editor-header-small {
|
||||
z-index: 100;
|
||||
height: 48px;
|
||||
padding: 0;
|
||||
padding-left: 15px;
|
||||
background-color: #fff;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.gh-editor-header-small .gh-publishmenu {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.gh-editor-header-small .post-settings {
|
||||
padding: 13px 15px;
|
||||
}
|
||||
|
||||
.gh-editor-header-small .gh-koenig-info {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
padding: 0 8px;
|
||||
}
|
||||
/* END delete when psmRedesign flag is removed */
|
||||
|
||||
.gh-editor-status {
|
||||
color: var(--midgrey);
|
||||
font-size: 1.3rem;
|
||||
|
@ -1,9 +1,9 @@
|
||||
<GhApp @showSettingsMenu={{this.ui.showSettingsMenu}}>
|
||||
<GhApp>
|
||||
<GhSkipLink @anchor=".gh-main">Skip to main content</GhSkipLink>
|
||||
|
||||
<GhAlerts />
|
||||
|
||||
<div class="gh-viewport {{if this.ui.showSettingsMenu 'settings-menu-expanded'}} {{if this.ui.showMobileMenu 'mobile-menu-expanded'}}">
|
||||
<div class="gh-viewport {{if this.ui.showMobileMenu 'mobile-menu-expanded'}}">
|
||||
{{#if this.showNavMenu}}
|
||||
<GhNavMenu />
|
||||
{{/if}}
|
||||
|
@ -5,7 +5,7 @@
|
||||
@class="gh-editor gh-view relative"
|
||||
as |editor|
|
||||
>
|
||||
<header class="gh-editor-header br2 pe-none {{editor.headerClass}} {{if (feature "psmRedesign") "labs"}}">
|
||||
<header class="gh-editor-header br2 pe-none">
|
||||
<div class="flex items-center pe-auto">
|
||||
{{#if this.ui.isFullScreen}}
|
||||
<div class="ml3 flex items-center">
|
||||
@ -43,41 +43,19 @@
|
||||
@runningText="Saving"
|
||||
@class="gh-btn gh-btn-blue gh-btn-icon contributor-save-button"
|
||||
data-test-contributor-save=true />
|
||||
{{else if (feature "psmRedesign")}}
|
||||
<div class="gh-publishmenu-labs">
|
||||
<GhPublishmenu
|
||||
@post={{this.post}}
|
||||
@postStatus={{this.post.status}}
|
||||
@saveTask={{this.saveTask}}
|
||||
@setSaveType={{action "setSaveType"}}
|
||||
@onOpen={{action "cancelAutosave"}} />
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
<GhPublishmenu
|
||||
@post={{this.post}}
|
||||
@postStatus={{this.post.status}}
|
||||
@saveTask={{this.saveTask}}
|
||||
@setSaveType={{action "setSaveType"}}
|
||||
@onOpen={{action "cancelAutosave"}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (feature "psmRedesign")}}
|
||||
{{#unless this.showSettingsMenu}}
|
||||
<div class="settings-menu-toggle-spacer-labs"></div>
|
||||
<div class="settings-menu-toggle-spacer"></div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
<div>
|
||||
{{#unless (feature "psmRedesign")}}
|
||||
{{!-- TODO: clean up everything related to ui.showSettingsMenu when flag is removed --}}
|
||||
<button type="button" class="gh-btn gh-btn-editor gh-btn-icon only-has-icon gh-actions-cog ml3" title="Settings" data-test-button="psm-toggle" {{action "openSettingsMenu" target=this.ui}} data-test-psm-trigger>
|
||||
<span>{{svg-jar "settings"}}</span>
|
||||
</button>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</section>
|
||||
</header>
|
||||
|
||||
@ -96,7 +74,6 @@
|
||||
@onBodyChange={{action "updateScratch"}}
|
||||
@headerOffset={{editor.headerHeight}}
|
||||
@scrollContainerSelector=".gh-koenig-editor"
|
||||
@scrollOffsetTopSelector=".gh-editor-header-small"
|
||||
@scrollOffsetBottomSelector=".gh-mobile-nav-bar"
|
||||
@onEditorCreated={{action "setKoenigEditor"}}
|
||||
@onWordCountChange={{action "updateWordCount"}}
|
||||
@ -112,18 +89,17 @@
|
||||
@clearFeatureImage={{action "clearFeatureImage"}}
|
||||
/>
|
||||
|
||||
<div class="gh-editor-wordcount-container {{if editor.headerClass "small"}}">
|
||||
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw4">
|
||||
<div class="gh-editor-wordcount-container">
|
||||
<div class="midgrey-l2 f8 pl4 pr3 fw4">
|
||||
{{gh-pluralize this.wordCount.wordCount "word"}}
|
||||
</div>
|
||||
<a href="https://ghost.org/help/using-the-editor/" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
|
||||
<a href="https://ghost.org/help/using-the-editor/" class="flex pa3" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
|
||||
</div>
|
||||
|
||||
</GhEditor>
|
||||
|
||||
{{#if (feature "psmRedesign")}}
|
||||
{{#if this.showSettingsMenu}}
|
||||
<GhPostSettingsMenuLabs
|
||||
<GhPostSettingsMenu
|
||||
@post={{this.post}}
|
||||
@showSettingsMenu={{this.showSettingsMenu}}
|
||||
@toggleEmailPreviewModal={{action "toggleEmailPreviewModal"}}
|
||||
@ -132,19 +108,15 @@
|
||||
@savePostTask={{this.savePostTask}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if (feature "psmRedesign")}}
|
||||
<button type="button" class="settings-menu-toggle-labs gh-btn gh-btn-editor gh-btn-icon only-has-icon gh-actions-cog" title="Settings" data-test-button="psm-toggle" {{on "click" this.toggleSettingsMenu}} data-test-psm-trigger>
|
||||
<button type="button" class="settings-menu-toggle gh-btn gh-btn-editor gh-btn-icon only-has-icon gh-actions-cog" title="Settings" data-test-button="psm-toggle" {{on "click" this.toggleSettingsMenu}} data-test-psm-trigger>
|
||||
{{#if this.showSettingsMenu}}
|
||||
{{!-- TODO: switch to close state --}}
|
||||
<span class="settings-menu-open">{{svg-jar "sidemenu-open"}}</span>
|
||||
{{else}}
|
||||
<span>{{svg-jar "sidemenu"}}</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showDeletePostModal}}
|
||||
<GhFullscreenModal @modal="delete-post"
|
||||
@ -206,19 +178,6 @@
|
||||
@modifier="action wide"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#unless (feature "psmRedesign")}}
|
||||
<LiquidWormhole>
|
||||
<GhPostSettingsMenu
|
||||
@post={{this.post}}
|
||||
@showSettingsMenu={{this.ui.showSettingsMenu}}
|
||||
@toggleEmailPreviewModal={{action "toggleEmailPreviewModal"}}
|
||||
@deletePost={{action "toggleDeletePostModal"}}
|
||||
@updateSlugTask={{this.updateSlugTask}}
|
||||
@savePostTask={{this.savePostTask}}
|
||||
/>
|
||||
</LiquidWormhole>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{outlet}}
|
||||
|
@ -267,19 +267,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-expandable-block">
|
||||
<div class="gh-expandable-header">
|
||||
<div>
|
||||
<h4 class="gh-expandable-title">Post settings menu behaviour change</h4>
|
||||
<p class="gh-expandable-description">
|
||||
Switches post settings menu behaviour from a popover to a collapsible sidebar.
|
||||
</p>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<GhFeatureFlag @flag="psmRedesign" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-expandable-block">
|
||||
<div class="gh-expandable-header">
|
||||
<div>
|
||||
|
Loading…
Reference in New Issue
Block a user