mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
Added first pass of button card
no issue - adds card that contains a single button with alignment options and configurable text and url
This commit is contained in:
parent
fd3f879ea8
commit
794ed83032
4 changed files with 258 additions and 0 deletions
|
@ -0,0 +1,102 @@
|
|||
<KoenigCard
|
||||
@icon={{"koenig/card-indicator-email"}}
|
||||
@class={{concat (kg-style "container-card") " kg-email-card kg-email-cta-card mih10 miw-100 relative"}}
|
||||
@headerOffset={{@headerOffset}}
|
||||
@toolbar={{this.toolbar}}
|
||||
@payload={{@payload}}
|
||||
@isSelected={{@isSelected}}
|
||||
@isEditing={{@isEditing}}
|
||||
@selectCard={{@selectCard}}
|
||||
@deselectCard={{@deselectCard}}
|
||||
@editCard={{@editCard}}
|
||||
@hasEditMode={{true}}
|
||||
@saveCard={{@saveCard}}
|
||||
@saveAsSnippet={{@saveAsSnippet}}
|
||||
@onLeaveEdit={{this.leaveEditMode}}
|
||||
@addParagraphAfterCard={{@addParagraphAfterCard}}
|
||||
@moveCursorToPrevSection={{@moveCursorToPrevSection}}
|
||||
@moveCursorToNextSection={{@moveCursorToNextSection}}
|
||||
@editor={{@editor}}
|
||||
{{did-insert this.registerElement}}
|
||||
as |card|
|
||||
>
|
||||
{{#if @isEditing}}
|
||||
|
||||
{{!-- edit mode view --}}
|
||||
|
||||
<div class="{{if (eq @payload.alignment "center") "tc"}}">
|
||||
|
||||
<button type="button" class="gh-btn gh-btn-accent" disabled={{not @payload.buttonText}}>
|
||||
{{#if @payload.buttonText}}
|
||||
<span>{{@payload.buttonText}}</span>
|
||||
{{else}}
|
||||
<span>Add button text</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<KoenigSettingsPanel>
|
||||
<div class="kg-settings-panel-control kg-settings-panel-control-horizontal">
|
||||
<div class="kg-settings-panel-control-label">Content alignment</div>
|
||||
<div class="kg-settings-panel-control-input">
|
||||
<div class="gh-btn-group icons">
|
||||
<button type="button" title="Left-align content" class="gh-btn gh-btn-icon {{if (eq @payload.alignment "left") "gh-btn-group-selected"}}" {{on "click" (fn this.setAlignment "left")}}><span>{{svg-jar "align-left"}}</span></button>
|
||||
<button type="button" title="Center-align content" class="gh-btn gh-btn-icon {{if (eq @payload.alignment "center") "gh-btn-group-selected"}}" {{on "click" (fn this.setAlignment "center")}}><span>{{svg-jar "align-center"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="kg-settings-panel-divider">
|
||||
|
||||
<div class="kg-settings-panel-control">
|
||||
<label class="kg-settings-panel-control-label" for="button-text-input">Button title</label>
|
||||
<div class="kg-settings-panel-control-input">
|
||||
<input
|
||||
type="text"
|
||||
class="gh-input"
|
||||
id="button-text-input"
|
||||
name="button-text"
|
||||
value={{@payload.buttonText}}
|
||||
placeholder="Add button text"
|
||||
{{on "input" this.setButtonText}}
|
||||
{{on-key "Enter" (fn this.focusElement "#button-url-input")}}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kg-settings-panel-control">
|
||||
<label class="kg-settings-panel-control-label" for="button-url-input">Button URL</label>
|
||||
<div class="kg-settings-panel-control-input">
|
||||
<GhInputWithSelect
|
||||
@value={{@payload.buttonUrl}}
|
||||
@options={{this.suggestedUrls}}
|
||||
@valueField="url"
|
||||
@searchField="url"
|
||||
@placeholder="https://yoursite.com/#/portal/signup/"
|
||||
@searchMessage={{null}}
|
||||
@onInput={{this.setButtonUrl}}
|
||||
@openOnFocus={{true}}
|
||||
@closeWhenEmpty={{true}}
|
||||
@triggerClass="email-cta-button-url-input"
|
||||
@triggerId="button-url-input"
|
||||
@renderInPlace={{false}} {{!-- avoid dropdown inheriting editor styles --}}
|
||||
as |suggestion|
|
||||
>
|
||||
<span class="db b">{{suggestion.name}}</span>
|
||||
{{suggestion.url}}
|
||||
</GhInputWithSelect>
|
||||
</div>
|
||||
</div>
|
||||
</KoenigSettingsPanel>
|
||||
|
||||
{{else}}
|
||||
|
||||
{{!-- rendered (non-edit) mode view --}}
|
||||
|
||||
<div class="{{if (eq @payload.alignment "center") "tc"}}">
|
||||
<a class="gh-btn gh-btn-accent" href="javascript:void(0)" data-tooltip={{@payload.buttonUrl}}><span>{{@payload.buttonText}}</span></a>
|
||||
</div>
|
||||
|
||||
{{/if}}
|
||||
</KoenigCard>
|
|
@ -0,0 +1,141 @@
|
|||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {isBlank} from '@ember/utils';
|
||||
import {run} from '@ember/runloop';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {set} from '@ember/object';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class KoenigCardButtonComponent extends Component {
|
||||
@service config;
|
||||
@service feature;
|
||||
@service store;
|
||||
@service membersUtils;
|
||||
@service ui;
|
||||
|
||||
@tracked buttonFocused = false;
|
||||
@tracked contentFocused = false;
|
||||
@tracked offers = null;
|
||||
|
||||
get toolbar() {
|
||||
if (this.args.isEditing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
items: [{
|
||||
buttonClass: 'fw4 flex items-center white',
|
||||
icon: 'koenig/kg-edit',
|
||||
iconClass: 'fill-white',
|
||||
title: 'Edit',
|
||||
text: '',
|
||||
action: run.bind(this, this.args.editCard)
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
get suggestedUrls() {
|
||||
const urls = [];
|
||||
|
||||
urls.push(...[{
|
||||
name: `Link to ${this.config.get('blogTitle')}`,
|
||||
url: this.config.getSiteUrl('/')
|
||||
}, {
|
||||
name: 'Free email signup',
|
||||
url: this.config.getSiteUrl('/#/portal/signup/free')
|
||||
}]);
|
||||
|
||||
if (this.membersUtils.isStripeEnabled) {
|
||||
urls.push(...[{
|
||||
name: 'Paid subscription',
|
||||
url: this.config.getSiteUrl('/#/portal/signup')
|
||||
}, {
|
||||
name: 'Upgrade or change plan',
|
||||
url: this.config.getSiteUrl('/#/portal/account/plans')
|
||||
}]);
|
||||
}
|
||||
|
||||
if (this.offers) {
|
||||
this.offers.forEach((offer) => {
|
||||
urls.push(...[{
|
||||
name: `Offer - ${offer.name}`,
|
||||
url: this.config.getSiteUrl(offer.code)
|
||||
}]);
|
||||
});
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.args.registerComponent(this);
|
||||
|
||||
const payloadDefaults = {
|
||||
alignment: 'left'
|
||||
};
|
||||
|
||||
Object.entries(payloadDefaults).forEach(([key, value]) => {
|
||||
if (this.args.payload[key] === undefined) {
|
||||
this._updatePayloadAttr(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
this.fetchOffersTask.perform();
|
||||
}
|
||||
|
||||
// required for snippet rects to be calculated - editor reaches in to component,
|
||||
// expecting a non-Glimmer component with a .element property
|
||||
@action
|
||||
registerElement(element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@action
|
||||
setButtonText(event) {
|
||||
this._updatePayloadAttr('buttonText', event.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
setButtonUrl(url) {
|
||||
this._updatePayloadAttr('buttonUrl', url);
|
||||
}
|
||||
|
||||
@action
|
||||
setAlignment(alignment, event) {
|
||||
event.preventDefault();
|
||||
this._updatePayloadAttr('alignment', alignment);
|
||||
}
|
||||
|
||||
@action
|
||||
leaveEditMode() {
|
||||
const {html, buttonText, buttonUrl} = this.args.payload;
|
||||
|
||||
if (isBlank(html) && isBlank(buttonText) && isBlank(buttonUrl)) {
|
||||
// afterRender is required to avoid double modification of `isSelected`
|
||||
// TODO: see if there's a way to avoid afterRender
|
||||
run.scheduleOnce('afterRender', this, this.args.deleteCard);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
focusElement(selector, event) {
|
||||
event.preventDefault();
|
||||
document.querySelector(selector)?.focus();
|
||||
}
|
||||
|
||||
@task({restartable: true})
|
||||
*fetchOffersTask() {
|
||||
this.offers = yield this.store.query('offer', {limit: 'all', filter: 'status:active'});
|
||||
}
|
||||
|
||||
_updatePayloadAttr(attr, value) {
|
||||
let payload = this.args.payload;
|
||||
|
||||
set(payload, attr, value);
|
||||
|
||||
// update the mobiledoc and stay in edit mode
|
||||
this.args.saveCard?.(payload, false);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ export const CARD_COMPONENT_MAP = {
|
|||
bookmark: 'koenig-card-bookmark',
|
||||
gallery: 'koenig-card-gallery',
|
||||
email: 'koenig-card-email',
|
||||
button: 'koenig-card-button',
|
||||
'email-cta': 'koenig-card-email-cta',
|
||||
paywall: 'koenig-card-paywall'
|
||||
};
|
||||
|
@ -28,6 +29,7 @@ export const CARD_ICON_MAP = {
|
|||
bookmark: 'koenig/kg-card-type-bookmark',
|
||||
gallery: 'koenig/kg-card-type-gallery',
|
||||
email: 'koenig/kg-card-type-gen-embed',
|
||||
button: 'koenig/kg-card-type-gen-embed',
|
||||
'email-cta': 'koenig/kg-card-type-gen-embed',
|
||||
paywall: 'koenig/kg-card-type-divider'
|
||||
};
|
||||
|
@ -50,6 +52,9 @@ export default [
|
|||
createComponentCard('email-cta', {deleteIfEmpty(card) {
|
||||
return !card.payload.html && !card.payload.buttonText && !card.payload.buttonUrl;
|
||||
}}),
|
||||
createComponentCard('button', {deleteIfEmpty(card) {
|
||||
return !card.payload.buttonText && !card.payload.buttonUrl;
|
||||
}}),
|
||||
createComponentCard('paywall', {hasEditMode: false, selectAfterInsert: false})
|
||||
];
|
||||
|
||||
|
@ -140,6 +145,15 @@ export const CARD_MENU = [
|
|||
matches: ['public preview', 'preview', 'paywall'],
|
||||
type: 'card',
|
||||
replaceArg: 'paywall'
|
||||
},
|
||||
{
|
||||
label: 'Button',
|
||||
icon: 'koenig/kg-card-type-paywall',
|
||||
desc: 'Clicky thing',
|
||||
matches: ['button'],
|
||||
type: 'card',
|
||||
replaceArg: 'button',
|
||||
feature: 'buttonCard'
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { default } from 'koenig-editor/components/koenig-card-button';
|
Loading…
Add table
Reference in a new issue