Compare commits
3 Commits
c3867ccbd4
...
886afab1ef
Author | SHA1 | Date | |
---|---|---|---|
886afab1ef | |||
0f6cd57f3d | |||
ffb8e8d506 |
@ -368,8 +368,8 @@ export default function FireCalculatorForm() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-3xl">
|
<>
|
||||||
<Card className="mb-8">
|
<Card className="mb-4">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-2xl">FIRE Calculator</CardTitle>
|
<CardTitle className="text-2xl">FIRE Calculator</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@ -541,181 +541,199 @@ export default function FireCalculatorForm() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<>
|
<div className="mb-4 grid grid-cols-1 gap-2 md:grid-cols-2">
|
||||||
<Card className="mb-8">
|
{result.error ? (
|
||||||
<CardHeader>
|
<Card className="col-span-full">
|
||||||
<CardTitle>Results</CardTitle>
|
<CardContent className="pt-6">
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{result.error ? (
|
|
||||||
<p className="text-destructive">{result.error}</p>
|
<p className="text-destructive">{result.error}</p>
|
||||||
) : (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<Label>
|
|
||||||
FIRE Number (Required Capital at Retirement - Strategy:{" "}
|
|
||||||
{form.getValues().retirementStrategy})
|
|
||||||
</Label>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatNumber(result.fireNumber)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Estimated Retirement Age</Label>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{result.retirementAge ?? "N/A"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{result.inflationAdjustedAllowance && (
|
|
||||||
<div>
|
|
||||||
<Label>
|
|
||||||
Monthly Allowance at Retirement (Inflation Adjusted)
|
|
||||||
</Label>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{formatNumber(result.inflationAdjustedAllowance)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{result.retirementYears && (
|
|
||||||
<div>
|
|
||||||
<Label>Retirement Duration (Years)</Label>
|
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
{result.retirementYears}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{result.yearlyData && result.yearlyData.length > 0 && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Financial Projection</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Projected balance growth and FIRE number threshold
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<ChartContainer
|
|
||||||
className="h-80"
|
|
||||||
config={{
|
|
||||||
balance: {
|
|
||||||
label: "Balance",
|
|
||||||
color: "var(--chart-1)",
|
|
||||||
},
|
|
||||||
fireNumber: {
|
|
||||||
label: "FIRE Number",
|
|
||||||
color: "var(--chart-3)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AreaChart
|
|
||||||
data={result.yearlyData}
|
|
||||||
margin={{ top: 20, right: 30, left: 20, bottom: 20 }}
|
|
||||||
>
|
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
|
||||||
<XAxis
|
|
||||||
dataKey="year"
|
|
||||||
label={{
|
|
||||||
value: "Year",
|
|
||||||
position: "insideBottom",
|
|
||||||
offset: -10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
tickFormatter={(value: number) => {
|
|
||||||
if (value >= 1000000) {
|
|
||||||
return `${(value / 1000000).toFixed(1)}M`;
|
|
||||||
} else if (value >= 1000) {
|
|
||||||
return `${(value / 1000).toFixed(0)}K`;
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}}
|
|
||||||
width={80}
|
|
||||||
/>
|
|
||||||
<ChartTooltip
|
|
||||||
content={({ active, payload }) => {
|
|
||||||
if (active && payload?.[0]?.payload) {
|
|
||||||
const data = payload[0]
|
|
||||||
.payload as (typeof result.yearlyData)[0];
|
|
||||||
return (
|
|
||||||
<div className="bg-background border p-2 shadow-sm">
|
|
||||||
<p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p>
|
|
||||||
<p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p>
|
|
||||||
{result.fireNumber !== null && (
|
|
||||||
<p className="text-destructive">{`Target FIRE Number: ${formatNumber(result.fireNumber)}`}</p>
|
|
||||||
)}
|
|
||||||
<p>{`Phase: ${data.phase === "accumulation" ? "Accumulation" : "Retirement"}`}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient
|
|
||||||
id="fillBalance"
|
|
||||||
x1="0"
|
|
||||||
y1="0"
|
|
||||||
x2="0"
|
|
||||||
y2="1"
|
|
||||||
>
|
|
||||||
<stop
|
|
||||||
offset="5%"
|
|
||||||
stopColor="var(--chart-1)"
|
|
||||||
stopOpacity={0.8}
|
|
||||||
/>
|
|
||||||
<stop
|
|
||||||
offset="95%"
|
|
||||||
stopColor="var(--chart-1)"
|
|
||||||
stopOpacity={0.1}
|
|
||||||
/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<Area
|
|
||||||
type="monotone"
|
|
||||||
dataKey="balance"
|
|
||||||
name="balance"
|
|
||||||
stroke="var(--chart-1)"
|
|
||||||
fill="url(#fillBalance)"
|
|
||||||
fillOpacity={0.4}
|
|
||||||
activeDot={{ r: 6 }}
|
|
||||||
/>
|
|
||||||
{result.fireNumber && (
|
|
||||||
<ReferenceLine
|
|
||||||
y={result.fireNumber}
|
|
||||||
stroke="var(--chart-3)"
|
|
||||||
strokeWidth={2}
|
|
||||||
strokeDasharray="5 5"
|
|
||||||
label={{
|
|
||||||
value: "FIRE Number",
|
|
||||||
position: "insideBottomRight",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{result.retirementAge && (
|
|
||||||
<ReferenceLine
|
|
||||||
x={
|
|
||||||
currentYear +
|
|
||||||
(result.retirementAge - form.getValues().currentAge)
|
|
||||||
}
|
|
||||||
stroke="var(--chart-2)"
|
|
||||||
strokeWidth={2}
|
|
||||||
label={{
|
|
||||||
value: "Retirement",
|
|
||||||
position: "insideTopRight",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AreaChart>
|
|
||||||
</ChartContainer>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>FIRE Number</CardTitle>
|
||||||
|
<CardDescription className="text-xs">
|
||||||
|
Required capital at retirement using{" "}
|
||||||
|
{form.getValues().retirementStrategy}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">
|
||||||
|
{formatNumber(result.fireNumber)}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Retirement Age</CardTitle>
|
||||||
|
<CardDescription className="text-xs">
|
||||||
|
Estimated age when you can retire
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">
|
||||||
|
{result.retirementAge ?? "N/A"}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{result.inflationAdjustedAllowance && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Monthly Allowance</CardTitle>
|
||||||
|
<CardDescription className="text-xs">
|
||||||
|
At retirement (inflation adjusted)
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">
|
||||||
|
{formatNumber(result.inflationAdjustedAllowance)}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{result.retirementYears && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Retirement Duration</CardTitle>
|
||||||
|
<CardDescription className="text-xs">
|
||||||
|
Years in retirement
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">
|
||||||
|
{result.retirementYears}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
{result?.yearlyData && result.yearlyData.length > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Financial Projection</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Projected balance growth and FIRE number threshold
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ChartContainer
|
||||||
|
className="aspect-auto h-80 w-full"
|
||||||
|
config={{
|
||||||
|
balance: {
|
||||||
|
label: "Balance",
|
||||||
|
color: "var(--chart-1)",
|
||||||
|
},
|
||||||
|
fireNumber: {
|
||||||
|
label: "FIRE Number",
|
||||||
|
color: "var(--chart-3)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AreaChart
|
||||||
|
data={result.yearlyData}
|
||||||
|
margin={{ top: 20, right: 30, left: 20, bottom: 20 }}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis
|
||||||
|
dataKey="year"
|
||||||
|
label={{
|
||||||
|
value: "Year",
|
||||||
|
position: "insideBottom",
|
||||||
|
offset: -10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
tickFormatter={(value: number) => {
|
||||||
|
if (value >= 1000000) {
|
||||||
|
return `${(value / 1000000).toPrecision(3)}M`;
|
||||||
|
} else if (value >= 1000) {
|
||||||
|
return `${(value / 1000).toPrecision(3)}K`;
|
||||||
|
}
|
||||||
|
return value.toString();
|
||||||
|
}}
|
||||||
|
width={25}
|
||||||
|
/>
|
||||||
|
<ChartTooltip
|
||||||
|
content={({ active, payload }) => {
|
||||||
|
if (active && payload?.[0]?.payload) {
|
||||||
|
const data = payload[0]
|
||||||
|
.payload as (typeof result.yearlyData)[0];
|
||||||
|
return (
|
||||||
|
<div className="bg-background border p-2 shadow-sm">
|
||||||
|
<p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p>
|
||||||
|
<p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p>
|
||||||
|
{result.fireNumber !== null && (
|
||||||
|
<p className="text-destructive">{`Target FIRE Number: ${formatNumber(result.fireNumber)}`}</p>
|
||||||
|
)}
|
||||||
|
<p>{`Phase: ${data.phase === "accumulation" ? "Accumulation" : "Retirement"}`}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="fillBalance" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop
|
||||||
|
offset="5%"
|
||||||
|
stopColor="var(--chart-1)"
|
||||||
|
stopOpacity={0.8}
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="95%"
|
||||||
|
stopColor="var(--chart-1)"
|
||||||
|
stopOpacity={0.1}
|
||||||
|
/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="balance"
|
||||||
|
name="balance"
|
||||||
|
stroke="var(--chart-1)"
|
||||||
|
fill="url(#fillBalance)"
|
||||||
|
fillOpacity={0.4}
|
||||||
|
activeDot={{ r: 6 }}
|
||||||
|
/>
|
||||||
|
{result.fireNumber && (
|
||||||
|
<ReferenceLine
|
||||||
|
y={result.fireNumber}
|
||||||
|
stroke="var(--chart-3)"
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeDasharray="5 5"
|
||||||
|
label={{
|
||||||
|
value: "FIRE Number",
|
||||||
|
position: "insideBottomRight",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{result.retirementAge && (
|
||||||
|
<ReferenceLine
|
||||||
|
x={
|
||||||
|
currentYear +
|
||||||
|
(result.retirementAge - form.getValues().currentAge)
|
||||||
|
}
|
||||||
|
stroke="var(--chart-2)"
|
||||||
|
strokeWidth={2}
|
||||||
|
label={{
|
||||||
|
value: "Retirement",
|
||||||
|
position: "insideTopRight",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AreaChart>
|
||||||
|
</ChartContainer>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-4">
|
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-4">
|
||||||
<div className="container mx-auto flex flex-col items-center justify-center gap-4 px-4 py-16 text-center">
|
<div className="mx-auto flex flex-col items-center justify-center gap-4 text-center">
|
||||||
<div className="flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
|
<div className="flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
|
||||||
<Image
|
<Image
|
||||||
src="/investingfire_logo_no-bg.svg"
|
src="/investingfire_logo_no-bg.svg"
|
||||||
@ -31,7 +31,7 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Added SEO Content Sections */}
|
{/* Added SEO Content Sections */}
|
||||||
<div className="container mx-auto max-w-4xl px-4 py-8 text-left">
|
<div className="mx-auto max-w-2xl py-12 text-left">
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="mb-4 text-3xl font-bold">
|
<h2 className="mb-4 text-3xl font-bold">
|
||||||
What is FIRE? Understanding Financial Independence and Early
|
What is FIRE? Understanding Financial Independence and Early
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Select({
|
function Select({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||||
return <SelectPrimitive.Root data-slot="select" {...props} />
|
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectGroup({
|
function SelectGroup({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||||
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
return <SelectPrimitive.Group data-slot="select-group" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectValue({
|
function SelectValue({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectTrigger({
|
function SelectTrigger({
|
||||||
@ -30,7 +30,7 @@ function SelectTrigger({
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||||
size?: "sm" | "default"
|
size?: "sm" | "default";
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
@ -38,7 +38,7 @@ function SelectTrigger({
|
|||||||
data-size={size}
|
data-size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@ -47,7 +47,7 @@ function SelectTrigger({
|
|||||||
<ChevronDownIcon className="size-4 opacity-50" />
|
<ChevronDownIcon className="size-4 opacity-50" />
|
||||||
</SelectPrimitive.Icon>
|
</SelectPrimitive.Icon>
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectContent({
|
function SelectContent({
|
||||||
@ -64,7 +64,7 @@ function SelectContent({
|
|||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
position={position}
|
position={position}
|
||||||
{...props}
|
{...props}
|
||||||
@ -74,7 +74,7 @@ function SelectContent({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"p-1",
|
"p-1",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@ -82,7 +82,7 @@ function SelectContent({
|
|||||||
<SelectScrollDownButton />
|
<SelectScrollDownButton />
|
||||||
</SelectPrimitive.Content>
|
</SelectPrimitive.Content>
|
||||||
</SelectPrimitive.Portal>
|
</SelectPrimitive.Portal>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectLabel({
|
function SelectLabel({
|
||||||
@ -95,7 +95,7 @@ function SelectLabel({
|
|||||||
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectItem({
|
function SelectItem({
|
||||||
@ -108,7 +108,7 @@ function SelectItem({
|
|||||||
data-slot="select-item"
|
data-slot="select-item"
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@ -119,7 +119,7 @@ function SelectItem({
|
|||||||
</span>
|
</span>
|
||||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
</SelectPrimitive.Item>
|
</SelectPrimitive.Item>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectSeparator({
|
function SelectSeparator({
|
||||||
@ -132,7 +132,7 @@ function SelectSeparator({
|
|||||||
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectScrollUpButton({
|
function SelectScrollUpButton({
|
||||||
@ -144,13 +144,13 @@ function SelectScrollUpButton({
|
|||||||
data-slot="select-scroll-up-button"
|
data-slot="select-scroll-up-button"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default items-center justify-center py-1",
|
"flex cursor-default items-center justify-center py-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronUpIcon className="size-4" />
|
<ChevronUpIcon className="size-4" />
|
||||||
</SelectPrimitive.ScrollUpButton>
|
</SelectPrimitive.ScrollUpButton>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectScrollDownButton({
|
function SelectScrollDownButton({
|
||||||
@ -162,13 +162,13 @@ function SelectScrollDownButton({
|
|||||||
data-slot="select-scroll-down-button"
|
data-slot="select-scroll-down-button"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default items-center justify-center py-1",
|
"flex cursor-default items-center justify-center py-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronDownIcon className="size-4" />
|
<ChevronDownIcon className="size-4" />
|
||||||
</SelectPrimitive.ScrollDownButton>
|
</SelectPrimitive.ScrollDownButton>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -182,4 +182,4 @@ export {
|
|||||||
SelectSeparator,
|
SelectSeparator,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
}
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user