import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { formatCurrency } from "@/lib/format"
const priceVariants = cva("inline-flex flex-wrap items-baseline gap-x-1.5", {
variants: {
size: {
sm: "text-sm",
default: "text-base",
lg: "text-lg",
},
},
defaultVariants: { size: "default" },
})
// The amount is the one thing on the numeral register; the period and the old
// price stay on the body register beside it, so a card of plans reads as one
// number with its terms rather than three competing figures.
const priceAmountVariants = cva("type-numeral tabular-nums", {
variants: {
size: {
sm: "text-lg",
default: "text-2xl",
lg: "text-4xl",
},
},
defaultVariants: { size: "default" },
})
export type PriceSize = NonNullable<VariantProps<typeof priceVariants>["size"]>
export type PriceProps = React.ComponentProps<"span"> & {
amount: number
/** ISO 4217 code the amount is in. */
currency?: string
/** What the amount buys — "month", "seat", "1,000 events". Read out as "per month". */
period?: string
/** The price this one replaces; struck through and announced as the old one. */
compareAt?: number
size?: PriceSize
/** BCP 47 tag deciding the symbol's place and the separators. */
locale?: string
}
/**
* A price: the amount in its currency, what it buys, and what it used to cost.
*
* Both figures go through `formatCurrency`, so the symbol, the separators and
* the fraction digits are the locale's rather than a template's — and the
* digits are tabular, because a column of plans that does not line up reads as
* a mistake before it reads as a number.
*/
function Price({
className,
amount,
currency = "USD",
period,
compareAt,
size = "default",
locale,
...props
}: PriceProps) {
const money = (value: number) => formatCurrency(value, currency, { locale })
return (
<span
data-slot="price"
data-size={size}
className={cn(priceVariants({ size }), className)}
{...props}
>
<span data-slot="price-amount" className={priceAmountVariants({ size })}>
{money(amount)}
</span>
{period ? (
<span data-slot="price-period" className="font-sans tracking-normal text-muted-foreground">
{/* The slash is a typographic shorthand; the preposition is what a
screen reader should hear, so each is given to one audience. */}
<span aria-hidden="true">/</span>
<span className="sr-only">per </span>
{period}
</span>
) : null}
{compareAt !== undefined ? (
<s
data-slot="price-compare"
className="font-sans tabular-nums tracking-normal text-muted-foreground"
>
<span className="sr-only">was </span>
{money(compareAt)}
</s>
) : null}
</span>
)
}
export { Price }