mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-01 13:54:35 +03:00
Alpha version of multiple products
- added Products grid component to signup page with static data - separate signup design between single vs. multiple products - alpha!
This commit is contained in:
parent
859043e22e
commit
5516e348af
@ -12,6 +12,7 @@ import {AccountPlanPageStyles} from './pages/AccountPlanPage';
|
||||
import {InputFieldStyles} from './common/InputField';
|
||||
import {SignupPageStyles} from './pages/SignupPage';
|
||||
import {PlanSectionStyles} from './common/PlansSection';
|
||||
import {ProductsSectionStyles} from './common/ProductsSection';
|
||||
import {AvatarStyles} from './common/MemberGravatar';
|
||||
import {MagicLinkStyles} from './pages/MagicLinkPage';
|
||||
import {LinkPageStyles} from './pages/LinkPage';
|
||||
@ -233,6 +234,10 @@ const FrameStyles = `
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gh-portal-popup-wrapper.fullscreen {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container {
|
||||
outline: none;
|
||||
position: relative;
|
||||
@ -254,6 +259,16 @@ const FrameStyles = `
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen {
|
||||
align-items: center;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border-radius: 0px;
|
||||
box-shadow: none !important;
|
||||
overflow-y: scroll;
|
||||
padding-bottom: 6vmin;
|
||||
}
|
||||
|
||||
@keyframes popup {
|
||||
0% {
|
||||
transform: scale(0.9) translateY(20px);
|
||||
@ -303,6 +318,10 @@ const FrameStyles = `
|
||||
margin: 0 6px 0 0;
|
||||
}
|
||||
|
||||
.gh-portal-popup-wrapper.fullscreen .gh-portal-powered {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.gh-portal-container-wide {
|
||||
width: 440px;
|
||||
}
|
||||
@ -350,6 +369,11 @@ const FrameStyles = `
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-content {
|
||||
overflow-y: visible;
|
||||
max-height: unset !important;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container footer {
|
||||
padding: 0 32px 32px;
|
||||
height: 72px;
|
||||
@ -371,6 +395,11 @@ const FrameStyles = `
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-closeicon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.gh-portal-closeicon:hover {
|
||||
color: var(--grey5);
|
||||
}
|
||||
@ -675,6 +704,10 @@ const MobileStyles = `
|
||||
.gh-portal-popup-wrapper.account-home .gh-portal-powered a {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 414px) {
|
||||
@ -703,7 +736,7 @@ const MobileStyles = `
|
||||
border-bottom: 1px solid var(--grey10);
|
||||
}
|
||||
|
||||
.gh-portal-plan-checkbox {
|
||||
div:not(.gh-portal-product-card-header) > .gh-portal-plan-checkbox {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 3;
|
||||
margin: 0 12px;
|
||||
@ -801,6 +834,7 @@ const FrameStyle =
|
||||
AccountPlanPageStyles +
|
||||
InputFieldStyles +
|
||||
PlanSectionStyles +
|
||||
ProductsSectionStyles +
|
||||
SwitchStyles +
|
||||
ActionButtonStyles +
|
||||
BackButtonStyles +
|
||||
|
@ -4,7 +4,7 @@ import AppContext from '../AppContext';
|
||||
import FrameStyle from './Frame.styles';
|
||||
import Pages, {getActivePage} from '../pages';
|
||||
import PopupNotification from './common/PopupNotification';
|
||||
import {isCookiesDisabled, getSitePrices, isInviteOnlySite} from '../utils/helpers';
|
||||
import {hasMultipleProducts, isCookiesDisabled, getSitePrices, isInviteOnlySite} from '../utils/helpers';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
@ -165,6 +165,11 @@ class PopupContent extends React.Component {
|
||||
pageClass = page;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasMultipleProducts({site}) && (page === 'signup' || page === 'signin')) {
|
||||
pageClass += ' fullscreen';
|
||||
}
|
||||
|
||||
const className = (hasMode(['preview', 'dev'], {customSiteUrl}) && !site.disableBackground) ? 'gh-portal-popup-container preview' : 'gh-portal-popup-container';
|
||||
const containerClassName = `${className} ${popupWidthStyle} ${pageClass}`;
|
||||
return (
|
||||
|
@ -454,7 +454,7 @@ function PlansSection({plans, showLabel = true, selectedPlan, onPlanSelect, chan
|
||||
}
|
||||
const className = getPlanClassNames({cookiesDisabled, changePlan, plans});
|
||||
return (
|
||||
<section>
|
||||
<section className="gh-portal-plans">
|
||||
<PlanLabel showLabel={showLabel} />
|
||||
<div className={className}>
|
||||
<PlanOptions plans={plans} onPlanSelect={onPlanSelect} selectedPlan={selectedPlan} changePlan={changePlan} />
|
||||
|
337
ghost/portal/src/components/common/ProductsSection.js
Normal file
337
ghost/portal/src/components/common/ProductsSection.js
Normal file
@ -0,0 +1,337 @@
|
||||
import React from 'react';
|
||||
import Switch from '../common/Switch';
|
||||
import {isCookiesDisabled} from '../../utils/helpers';
|
||||
|
||||
export const ProductsSectionStyles = `
|
||||
.gh-portal-products {
|
||||
background: var(--grey13);
|
||||
margin: 40px -32px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.gh-portal-priceoption-label {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.3px;
|
||||
text-transform: uppercase;
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch label, .gh-portal-for-switch .container {
|
||||
width: 43px !important;
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch .input-toggle-component,
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch label:hover input:not(:checked) + .input-toggle-component,
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch .container:hover input:not(:checked) + .input-toggle-component {
|
||||
background: var(--grey1);
|
||||
border-color: var(--grey1);
|
||||
box-shadow: none;
|
||||
width: 43px !important;
|
||||
height: 24px !important;
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch .input-toggle-component:before {
|
||||
height: 18px !important;
|
||||
width: 18px !important;
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch input:checked + .input-toggle-component {
|
||||
background: var(--grey1);
|
||||
}
|
||||
|
||||
.gh-portal-products-priceswitch .gh-portal-for-switch input:checked + .input-toggle-component:before {
|
||||
transform: translateX(19px);
|
||||
}
|
||||
|
||||
.gh-portal-products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(${productColumns()}, minmax(0, 1fr));
|
||||
grid-gap: 32px;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 5vw;
|
||||
}
|
||||
|
||||
|
||||
.gh-portal-product-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: white;
|
||||
padding: 24px 24px 18px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
||||
min-height: 240px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gh-portal-product-card.checked::before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
content: "";
|
||||
z-index: 999;
|
||||
border: 2px solid var(--brandcolor);
|
||||
pointer-events: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.gh-portal-product-card.checked {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.gh-portal-product-card-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-portal-product-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.0em;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 7px;
|
||||
text-align: center;
|
||||
min-height: 24px;
|
||||
word-break: break-word;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--grey12);
|
||||
padding: 8px 0 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gh-portal-product-description {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.5em;
|
||||
text-align: center;
|
||||
color: var(--grey5);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.gh-portal-product-price {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .currency-sign {
|
||||
align-self: flex-start;
|
||||
font-size: 2.0rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .amount {
|
||||
font-size: 3.3rem;
|
||||
font-weight: 500;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .billing-period {
|
||||
align-self: flex-end;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.6em;
|
||||
color: var(--grey7);
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.gh-portal-product-alternative-price {
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.6em;
|
||||
color: var(--grey7);
|
||||
text-align: center;
|
||||
margin-top: 4px;
|
||||
letter-spacing: 0.2px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.gh-portal-products {
|
||||
margin: 0 -32px;
|
||||
}
|
||||
|
||||
.gh-portal-products-grid {
|
||||
grid-template-columns: unset;
|
||||
grid-gap: 20px;
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
.gh-portal-product-card {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-gap: 12px;
|
||||
align-items: start;
|
||||
min-height: unset;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.gh-portal-product-card-header {
|
||||
grid-row: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 20px auto;
|
||||
}
|
||||
|
||||
.gh-portal-product-name {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
border-bottom: none;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.gh-portal-product-description {
|
||||
grid-column: 2 / 3;
|
||||
margin-bottom: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.gh-portal-product-price {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .currency-sign {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .amount {
|
||||
font-size: 2.6rem;
|
||||
}
|
||||
|
||||
.gh-portal-product-price .billing-period {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 24px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.gh-portal-product-card-footer {
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen footer.gh-portal-signup-footer,
|
||||
.gh-portal-popup-container.fullscreen footer.gh-portal-signin-footer {
|
||||
padding: 0 32px !important;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.gh-portal-product-alternative-price {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function productColumns() {
|
||||
const noOfProducts = 4;
|
||||
return noOfProducts > 5 ? 5 : noOfProducts;
|
||||
}
|
||||
|
||||
function Checkbox({name, id, onPlanSelect, isChecked, disabled = false}) {
|
||||
if (isCookiesDisabled()) {
|
||||
disabled = true;
|
||||
}
|
||||
return (
|
||||
<div className='gh-portal-plan-checkbox'>
|
||||
<input
|
||||
name={name}
|
||||
key={id}
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
aria-label={name}
|
||||
onChange={e => onPlanSelect(e, id)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<span className='checkmark'></span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ProductsSection() {
|
||||
return (
|
||||
<section className="gh-portal-products">
|
||||
|
||||
<div className="gh-portal-products-priceswitch">
|
||||
<span className="gh-portal-priceoption-label">Monthly</span>
|
||||
<Switch onToggle='' checked={false} />
|
||||
<span className="gh-portal-priceoption-label">Yearly</span>
|
||||
</div>
|
||||
|
||||
<div className="gh-portal-products-grid">
|
||||
<div className="gh-portal-product-card">
|
||||
<div className="gh-portal-product-card-header">
|
||||
<Checkbox name='x' id='x' isChecked={false} onPlanSelect='' />
|
||||
<h4 className="gh-portal-product-name">Free</h4>
|
||||
<div className="gh-portal-product-description">Free preview</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card-footer">
|
||||
<div className="gh-portal-product-price">
|
||||
<span className="currency-sign">$</span>
|
||||
<span className="amount">0</span>
|
||||
</div>
|
||||
<div className="gh-portal-product-alternative-price"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card checked">
|
||||
<div className="gh-portal-product-card-header">
|
||||
<Checkbox name='x' id='x' isChecked={true} onPlanSelect='' />
|
||||
<h4 className="gh-portal-product-name">Bronze</h4>
|
||||
<div className="gh-portal-product-description">Access to all members articles</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card-footer">
|
||||
<div className="gh-portal-product-price">
|
||||
<span className="currency-sign">$</span>
|
||||
<span className="amount">70</span>
|
||||
<span className="billing-period">/year</span>
|
||||
</div>
|
||||
<div className="gh-portal-product-alternative-price">$7/month</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card">
|
||||
<div className="gh-portal-product-card-header">
|
||||
<Checkbox name='x' id='x' isChecked={false} onPlanSelect='' />
|
||||
<h4 className="gh-portal-product-name">Silver</h4>
|
||||
<div className="gh-portal-product-description">Access to all members articles and weekly podcast</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card-footer">
|
||||
<div className="gh-portal-product-price">
|
||||
<span className="currency-sign">$</span>
|
||||
<span className="amount">120</span>
|
||||
<span className="billing-period">/year</span>
|
||||
</div>
|
||||
<div className="gh-portal-product-alternative-price">$12/month</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card">
|
||||
<div className="gh-portal-product-card-header">
|
||||
<Checkbox name='x' id='x' isChecked={false} onPlanSelect='' />
|
||||
<h4 className="gh-portal-product-name">Gold</h4>
|
||||
<div className="gh-portal-product-description">Access to all members articles, weekly podcast and exclusive interviews</div>
|
||||
</div>
|
||||
<div className="gh-portal-product-card-footer">
|
||||
<div className="gh-portal-product-price">
|
||||
<span className="currency-sign">$</span>
|
||||
<span className="amount">1000</span>
|
||||
<span className="billing-period">/year</span>
|
||||
</div>
|
||||
<div className="gh-portal-product-alternative-price">$12/month</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProductsSection;
|
@ -93,10 +93,6 @@ export const AccountHomePageStyles = `
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.gh-portal-products:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gh-portal-product-icon {
|
||||
width: 52px;
|
||||
margin-right: 12px;
|
||||
|
@ -2,6 +2,7 @@ import ActionButton from '../common/ActionButton';
|
||||
import CloseButton from '../common/CloseButton';
|
||||
import AppContext from '../../AppContext';
|
||||
import PlansSection from '../common/PlansSection';
|
||||
import ProductsSection from '../common/ProductsSection';
|
||||
import InputForm from '../common/InputForm';
|
||||
import {ValidateInputForm} from '../../utils/form';
|
||||
import {getSitePrices, hasMultipleProducts, hasOnlyFreePlan, isInviteOnlySite} from '../../utils/helpers';
|
||||
@ -51,6 +52,14 @@ export const SignupPageStyles = `
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-signup-header {
|
||||
margin-top: 6vmin;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-signin-header {
|
||||
margin-top: 22vmin;
|
||||
}
|
||||
|
||||
.gh-portal-signup-message {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -83,6 +92,16 @@ export const SignupPageStyles = `
|
||||
background-attachment: local,local,scroll,scroll;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-content.signup,
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-content.signin {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen .gh-portal-input-section {
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.gh-portal-content.signup.invite-only {
|
||||
background: none;
|
||||
}
|
||||
@ -93,6 +112,17 @@ export const SignupPageStyles = `
|
||||
height: 132px;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen footer.gh-portal-signup-footer,
|
||||
.gh-portal-popup-container.fullscreen footer.gh-portal-signin-footer {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.gh-portal-popup-container.fullscreen footer.gh-portal-signin-footer {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.gh-portal-content.signup,
|
||||
.gh-portal-content.signin {
|
||||
max-height: calc(100vh - 12vw - 140px);
|
||||
@ -350,6 +380,14 @@ class SignupPage extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderProducts() {
|
||||
return (
|
||||
<>
|
||||
<ProductsSection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoginMessage() {
|
||||
const {brandColor, onAction} = this.context;
|
||||
return (
|
||||
@ -379,18 +417,34 @@ class SignupPage extends React.Component {
|
||||
</section>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
<InputForm
|
||||
fields={fields}
|
||||
onChange={(e, field) => this.handleInputChange(e, field)}
|
||||
onKeyDown={(e, field) => this.onKeyDown(e, field)}
|
||||
/>
|
||||
{this.renderPlans()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
if (hasMultipleProducts({site})) {
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
<InputForm
|
||||
fields={fields}
|
||||
onChange={(e, field) => this.handleInputChange(e, field)}
|
||||
onKeyDown={(e, field) => this.onKeyDown(e, field)}
|
||||
/>
|
||||
{this.renderProducts()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
<InputForm
|
||||
fields={fields}
|
||||
onChange={(e, field) => this.handleInputChange(e, field)}
|
||||
onKeyDown={(e, field) => this.onKeyDown(e, field)}
|
||||
/>
|
||||
{this.renderPlans()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderSiteLogo() {
|
||||
|
@ -85,8 +85,8 @@ const products = [
|
||||
export const site = {
|
||||
title: 'A Ghost site',
|
||||
description: 'Thoughts, stories and ideas.',
|
||||
logo: 'https://pbs.twimg.com/profile_images/1111773508231667713/mf2N0uqc_400x400.png',
|
||||
icon: 'https://pbs.twimg.com/profile_images/1111773508231667713/mf2N0uqc_400x400.png',
|
||||
logo: 'https://static.ghost.org/v4.0.0/images/ghost-orb-1.png',
|
||||
icon: 'https://static.ghost.org/v4.0.0/images/ghost-orb-1.png',
|
||||
accent_color: '#45C32E',
|
||||
url: 'http://localhost:2368/',
|
||||
plans: {
|
||||
@ -96,7 +96,7 @@ export const site = {
|
||||
},
|
||||
products,
|
||||
prices: prices,
|
||||
allow_self_signup: true,
|
||||
allow_self_signup: false,
|
||||
members_signup_access: 'all',
|
||||
free_price_name: 'Free',
|
||||
free_price_description: 'Free preview',
|
||||
|
Loading…
Reference in New Issue
Block a user