136 lines
3.4 KiB
TypeScript
136 lines
3.4 KiB
TypeScript
import { JSX } from 'preact/jsx-runtime'
|
|
import { Budget } from '../models.ts'
|
|
import { Currency } from '../components/Currency.tsx'
|
|
import { Percentage } from '../components/Percentage.tsx'
|
|
import { Component } from 'https://esm.sh/stable/preact@10.19.6/denonext/preact.mjs'
|
|
|
|
function BudgetCardLit(
|
|
{ budget, ...props }:
|
|
& { budget: Budget }
|
|
& JSX.HTMLAttributes<HTMLDivElement>,
|
|
) {
|
|
const remainingRatio = 1.0 - (budget.spent / budget.target)
|
|
const bgColor = remainingRatio < 0 ? 'bg-red' : 'bg-green'
|
|
const textColor = remainingRatio < 0 ? 'text-red' : 'text-green'
|
|
|
|
return (
|
|
<div
|
|
class='bg-surface0 rounded shadow hover:bg-surface1 transition-colors flex justify-between relative z-0 cursor-pointer'
|
|
{...props}
|
|
>
|
|
<div
|
|
class={`rounded absolute top-0 bottom-0 left-0 ${bgColor} z-[-1] opacity-10`}
|
|
style={`right: ${
|
|
(remainingRatio < 0 ? 0 : (Math.abs(1.0 - remainingRatio))) * 100
|
|
}%;`}
|
|
/>
|
|
<h2
|
|
class={`py-2 px-3 text-2xl flex flex-none max-w-xs items-center ${textColor} truncate`}
|
|
>
|
|
{budget.name}
|
|
</h2>
|
|
<div class={`p-2 pl-0 flex-1 leading-none`}>
|
|
<span class={textColor}>
|
|
</span>
|
|
<br />
|
|
<small>
|
|
<Currency amount={budget.spent} /> of{' '}
|
|
<Currency amount={budget.target} />
|
|
{' spent '}
|
|
(<Percentage ratio={remainingRatio} /> remaining)
|
|
</small>
|
|
</div>
|
|
{
|
|
/*
|
|
<button class='text-surface1 cursor-grab p-2'>
|
|
<svg
|
|
xmlns='http://www.w3.org/2000/svg'
|
|
fill='none'
|
|
viewBox='0 0 24 24'
|
|
strokeWidth={1.5}
|
|
stroke='currentColor'
|
|
className='size-6'
|
|
>
|
|
<path
|
|
strokeLinecap='round'
|
|
strokeLinejoin='round'
|
|
d='M3.75 9h16.5m-16.5 6.75h16.5'
|
|
/>
|
|
</svg>
|
|
</button>
|
|
*/
|
|
}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function BudgetCard(
|
|
{ budget, ...props }:
|
|
& { budget: Budget }
|
|
& JSX.HTMLAttributes<HTMLDivElement>,
|
|
) {
|
|
const remainingRatio = 1.0 - (budget.spent / budget.target)
|
|
const bgClass = (remainingRatio < 0 ? 'bg-red' : 'bg-green') + ' opacity-10'
|
|
const accentTextClass = remainingRatio < 0 ? 'text-red' : 'text-green'
|
|
|
|
return (
|
|
<ProgressBarCard
|
|
heading={budget.name}
|
|
bgClass={bgClass}
|
|
accentTextClass={accentTextClass}
|
|
ratio={remainingRatio}
|
|
subheading={
|
|
<>
|
|
<Currency amount={Math.abs(budget.target - budget.spent)} />
|
|
{remainingRatio >= 0 ? ` left ` : ` over `}
|
|
</>
|
|
}
|
|
{...props}
|
|
>
|
|
<small>
|
|
<Currency amount={budget.spent} /> of{' '}
|
|
<Currency amount={budget.target} />
|
|
{' spent '}
|
|
(<Percentage ratio={remainingRatio} /> remaining)
|
|
</small>
|
|
</ProgressBarCard>
|
|
)
|
|
}
|
|
|
|
interface ProgressBarCardProps {
|
|
heading: string
|
|
subheading: JSX.Element
|
|
bgClass: string
|
|
accentTextClass: string
|
|
ratio: number
|
|
}
|
|
export function ProgressBarCard(
|
|
{ heading, subheading, bgClass, accentTextClass, ratio, children, ...props }:
|
|
& ProgressBarCardProps
|
|
& JSX.HTMLAttributes<HTMLDivElement>,
|
|
) {
|
|
return (
|
|
<div
|
|
class='bg-surface0 rounded shadow hover:bg-surface1 transition-colors flex justify-between relative z-0 cursor-pointer'
|
|
{...props}
|
|
>
|
|
<div
|
|
class={`rounded absolute top-0 bottom-0 left-0 ${bgClass} z-[-1]`}
|
|
style={`right: ${ratio * 100}%;`}
|
|
/>
|
|
<h2
|
|
class={`py-2 px-3 text-2xl flex flex-none max-w-xs items-center ${accentTextClass} truncate`}
|
|
>
|
|
{heading}
|
|
</h2>
|
|
<div class={`p-2 pl-0 flex-1 leading-none`}>
|
|
<span class={accentTextClass}>
|
|
{subheading}
|
|
</span>
|
|
<br />
|
|
{children}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|