Refactor the Pricing widget

This commit is contained in:
widgeter
2023-08-09 19:30:55 +02:00
parent 951f270726
commit 1db237dd7a
4 changed files with 118 additions and 142 deletions

View File

@ -1,48 +1,33 @@
--- ---
import { Icon } from "astro-icon/components"; import { Icon } from 'astro-icon/components';
import CTA from "~/components/ui/CTA.astro"; import CTA from '~/components/ui/CTA.astro';
import Headline from "~/components/ui/Headline.astro"; import Headline from '~/components/ui/Headline.astro';
import WidgetWrapper from "~/components/ui/WidgetWrapper.astro"; import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
import type { Pricing } from "~/types"; import type { Pricing } from '~/types';
const { const {
title = "", title = '',
subtitle = "", subtitle = '',
tagline = "", tagline = '',
prices = [], prices = [],
id, id,
isDark = false, isDark = false,
classes = {}, classes = {},
bg = await Astro.slots.render("bg"), bg = await Astro.slots.render('bg'),
} = Astro.props as Pricing; } = Astro.props as Pricing;
--- ---
<WidgetWrapper <WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-screen-xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
id={id}
isDark={isDark}
containerClass={`max-w-6xl mx-auto ${classes?.container ?? ""}`}
bg={bg}
>
<Headline title={title} subtitle={subtitle} tagline={tagline} /> <Headline title={title} subtitle={subtitle} tagline={tagline} />
<div class="flex items-stretch justify-center"> <div class="flex items-stretch justify-center">
<div <div class="grid grid-cols-3 gap-4 dark:text-white sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3">
class="grid grid-cols-3 gap-3 dark:text-white sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3"
>
{ {
prices && prices &&
prices.map( prices.map(({ title, subtitle, price, period, items, callToAction, hasRibbon = false, ribbonTitle }) => (
({ <div class="col-span-3 mx-auto flex w-full sm:col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-1">
title, {price && period && (
price, <div class="rounded-lg backdrop-blur border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900 shadow px-6 py-8 flex w-full max-w-sm flex-col justify-between text-center">
period,
items,
callToAction,
hasRibbon = false,
ribbonTitle,
}) => (
<div class="col-span-3 mx-auto flex w-full sm:col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-1">
<div class="rounded-md border-gray-200 bg-white px-6 pt-10 pb-8 shadow-xl transition duration-300 ease-in-out dark:border dark:border-slate-800 dark:bg-slate-900 relative flex w-full max-w-sm flex-col justify-between text-center">
{hasRibbon && ribbonTitle && ( {hasRibbon && ribbonTitle && (
<div class="absolute right-[-5px] top-[-5px] z-[1] h-[100px] w-[100px] overflow-hidden text-right"> <div class="absolute right-[-5px] top-[-5px] z-[1] h-[100px] w-[100px] overflow-hidden text-right">
<span class="absolute top-[19px] right-[-21px] block w-full rotate-45 bg-green-700 text-center text-[10px] font-bold uppercase leading-5 text-white shadow-[0_3px_10px_-5px_rgba(0,0,0,0.3)] before:absolute before:left-0 before:top-full before:z-[-1] before:border-[3px] before:border-r-transparent before:border-b-transparent before:border-l-green-800 before:border-t-green-800 before:content-[''] after:absolute after:right-0 after:top-full after:z-[-1] after:border-[3px] after:border-l-transparent after:border-b-transparent after:border-r-green-800 after:border-t-green-800 after:content-['']"> <span class="absolute top-[19px] right-[-21px] block w-full rotate-45 bg-green-700 text-center text-[10px] font-bold uppercase leading-5 text-white shadow-[0_3px_10px_-5px_rgba(0,0,0,0.3)] before:absolute before:left-0 before:top-full before:z-[-1] before:border-[3px] before:border-r-transparent before:border-b-transparent before:border-l-green-800 before:border-t-green-800 before:content-[''] after:absolute after:right-0 after:top-full after:z-[-1] after:border-[3px] after:border-l-transparent after:border-b-transparent after:border-r-green-800 after:border-t-green-800 after:content-['']">
@ -50,48 +35,47 @@ const {
</span> </span>
</div> </div>
)} )}
<ul class="px-2 py-0"> <div class="px-2 py-0">
<li class="text-center text-xl font-medium uppercase leading-6 tracking-wider text-gray-800 dark:text-slate-400"> {title && (
{title} <h3 class="text-center text-xl font-semibold uppercase leading-6 tracking-wider mb-2">{title}</h3>
</li> )}
<li class="mt-5 mb-2.5 flex items-center justify-center text-center font-semibold"> {subtitle && <p class="font-light sm:text-lg text-gray-600 dark:text-slate-400">{subtitle}</p>}
<span class="text-5xl">$</span> <div class="my-8">
<span class="text-6xl">{price}</span> <div class="flex items-center justify-center text-center mb-1">
</li> <span class="text-5xl">$</span>
<li class="mb-7 text-center text-base font-medium capitalize leading-6 text-gray-600 dark:text-slate-400"> <span class="text-6xl font-extrabold">{price}</span>
{period} </div>
</li> <span class="text-base leading-6 lowercase text-gray-600 dark:text-slate-400">{period}</span>
{items && </div>
items.map(({ description, icon }) => ( {items && (
<li class="mb-1.5 flex items-start text-left text-base leading-7"> <ul role="list" class="my-8 md:my-10 space-y-2 text-left">
<div class="mt-1.5 mr-1.5 flex h-4 w-4 items-center justify-center rounded-full border-2 border-primary bg-primary"> {items.map(
{icon && ( ({ description, icon }) =>
<Icon description && (
name={icon} <li class="mb-1.5 flex items-start space-x-3 leading-7">
class="w-5 h-5 font-bold text-white" <div class="rounded-full bg-primary mt-1">
/> <Icon name={icon ? icon : 'tabler:check'} class="w-5 h-5 font-bold p-1 text-white" />
)} </div>
</div> <span>{description}</span>
{description} </li>
</li> )
))} )}
</ul> </ul>
)}
</div>
{callToAction && ( {callToAction && (
<div class="flex justify-center"> <div class={`flex justify-center btn ${hasRibbon ? 'btn-primary' : ''}`}>
<CTA {typeof callToAction === 'string' ? (
callToAction={callToAction} <Fragment set:html={callToAction} />
class={`mt-8 ${ ) : (
hasRibbon callToAction && callToAction.text && callToAction.href && <CTA callToAction={callToAction} />
? "border-primary-600 bg-primary-600 font-semibold text-white hover:border-primary-800 hover:bg-primary-800 hover:text-white dark:border-primary-700 dark:bg-primary-700 dark:text-white dark:hover:border-primary-900 dark:hover:bg-primary-900" )}
: ""
}`}
/>
</div> </div>
)} )}
</div> </div>
</div> )}
) </div>
) ))
} }
</div> </div>
</div> </div>

View File

@ -26,7 +26,6 @@ const {
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
tagline={tagline} tagline={tagline}
callToAction={callToAction}
classes={{ classes={{
container: 'text-center md:text-left mb-4 md:mb-8', container: 'text-center md:text-left mb-4 md:mb-8',
title: 'mb-4 text-3xl lg:text-4xl font-bold font-heading', title: 'mb-4 text-3xl lg:text-4xl font-bold font-heading',

View File

@ -1,105 +1,97 @@
--- ---
import Layout from '~/layouts/PageLayout.astro'; import Layout from '~/layouts/PageLayout.astro';
import Prices from '~/components/widgets/Pricing.astro';
import Pricing from '~/components/widgets/Pricing.astro';
const metadata = { const metadata = {
title: "Pricing", title: 'Pricing',
}; };
--- ---
<Layout metadata={metadata}> <Layout metadata={metadata}>
<Pricing <Prices
title="Basic Pricing" title="Our prices"
tagline="Pricing" subtitle="Only pay for what you need"
subtitle="Our prices"
prices={[ prices={[
{ {
"title": "basic", title: 'basic',
"price": 29, subtitle: 'Optimal choice for personal use',
"period": "Per Month", price: 29,
"items": [ period: 'per month',
items: [
{ {
"description": "Etiam in libero", description: 'Etiam in libero, et volutpat',
"icon": "tabler:check"
}, },
{ {
"description": "Aenean ac nunc", description: 'Aenean ac nunc dolor tristique',
"icon": "tabler:check"
}, },
{ {
"description": "Cras scelerisque accumsan libero, et volutpat dolor tristique at", description: 'Cras scelerisque accumsan lib',
"icon": "tabler:check"
}, },
{ {
"description": "In hac habitasse", description: 'In hac habitasse',
"icon": "tabler:check" },
}
], ],
"callToAction": { callToAction: {
"targetBlank": true, targetBlank: true,
"text": "Free 7-day trial", text: 'Free 7-day trial',
"href": "#" href: '#',
}
},
{
"title": "standard",
"price": 69,
"period": "Per Month",
"items": [
{
"description": "Proin vel laoreet",
"icon": "tabler:check"
},
{
"description": "Ut efficitur egestas",
"icon": "tabler:check"
},
{
"description": "Pellentesque ut nibh",
"icon": "tabler:check"
},
{
"description": "Donec fringilla sem",
"icon": "tabler:check"
}
],
"callToAction": {
"targetBlank": true,
"text": "Free 15-day trial",
"href": "#"
}, },
"hasRibbon": true,
"ribbonTitle": "popular"
}, },
{ {
"title": "premium", title: 'standard',
"price": 199, subtitle: 'Optimal choice for small teams',
"period": "Per Month", price: 69,
"items": [ period: 'Per Month',
items: [
{ {
"description": "Curabitur suscipit risus", description: 'Proin vel laoreet',
"icon": "tabler:check"
}, },
{ {
"description": "Aliquam blandit malesuada", description: 'Ut efficitur habitasse egestas',
"icon": "tabler:check"
}, },
{ {
"description": "Suspendisse sit amet", description: 'Volutpat hac curabitur',
"icon": "tabler:check"
}, },
{ {
"description": "Suspendisse auctor dui", description: 'Pellentesque blandit ut nibh',
"icon": "tabler:check" },
} {
description: 'Donec fringilla sem',
},
], ],
"callToAction": { callToAction: {
"targetBlank": true, targetBlank: true,
"text": "Free 30-day trial", text: 'Free 15-day trial',
"href": "#" href: '#',
} },
} hasRibbon: true,
ribbonTitle: 'popular',
},
{
title: 'premium',
subtitle: 'Optimal choice for companies',
price: 199,
period: 'Per Month',
items: [
{
description: 'Curabitur suscipit risus',
},
{
description: 'Aliquam habitasse malesuada',
},
{
description: 'Suspendisse sit amet blandit',
},
{
description: 'Suspendisse auctor blandit dui',
},
],
callToAction: {
targetBlank: true,
text: 'Free 30-day trial',
href: '#',
},
},
]} ]}
/> />
</Layout> </Layout>

1
src/types.d.ts vendored
View File

@ -135,6 +135,7 @@ export interface Item {
export interface Price { export interface Price {
title?: string; title?: string;
subtitle?: string;
description?: string; description?: string;
price?: number; price?: number;
period?: string; period?: string;