0
Fork 0
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:
Kevin Ansfield 2021-11-08 15:50:05 +00:00
parent fd3f879ea8
commit 794ed83032
4 changed files with 258 additions and 0 deletions

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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'
}]
},
{

View file

@ -0,0 +1 @@
export { default } from 'koenig-editor/components/koenig-card-button';