feat: Implement Calendar component, UiProvider, and a cn utility, while enhancing picker components with new icons and styles.

This commit is contained in:
Firman Ramdhani
2026-02-04 16:53:37 +07:00
parent 1ceb0fad53
commit 660a9ace20
10 changed files with 1890 additions and 470 deletions
@@ -13,19 +13,27 @@ import {
MonthRangePicker,
QuarterRangePicker,
YearRangePicker,
InputStatusType
InputStatusType,
Calendar,
} from '@repo/ui'; // Sesuaikan path
import { DateUtils } from '@repo/utils';
// --- UI Helper Components ---
const Section = ({ title, description, children }: { title: string; description?: string; children: React.ReactNode }) => (
const Section = ({
title,
description,
children,
}: {
title: string;
description?: string;
children: React.ReactNode;
}) => (
<section className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden mb-8">
<div className="bg-gray-50 px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-bold text-gray-800">{title}</h2>
{description && <p className="text-sm text-gray-500 mt-1">{description}</p>}
</div>
<div className="p-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{children}
</div>
<div className="p-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">{children}</div>
</section>
);
@@ -35,22 +43,17 @@ const Card = ({ label, code, children }: { label: string; code?: string; childre
<label className="text-sm font-semibold text-gray-700">{label}</label>
{code && <span className="text-xs font-mono text-gray-400 bg-gray-100 px-1.5 py-0.5 rounded">{code}</span>}
</div>
<div className="w-full">
{children}
</div>
<div className="w-full">{children}</div>
</div>
);
export default function PickerShowcase({status}: {status: InputStatusType}) {
export default function PickerShowcase({ status }: { status: InputStatusType }) {
return (
<div className="min-h-screen bg-gray-50 py-10 px-4 sm:px-6 lg:px-8 font-sans">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="mb-10 text-center">
<h1 className="text-3xl font-extrabold text-gray-900 tracking-tight">
Component Picker Showcase
</h1>
<h1 className="text-3xl font-extrabold text-gray-900 tracking-tight">Component Picker Showcase</h1>
<p className="mt-2 text-lg text-gray-600">
Fully typed, Tailwind-styled, and robust date manipulation components.
</p>
@@ -65,7 +68,7 @@ export default function PickerShowcase({status}: {status: InputStatusType}) {
<Card label="Date Picker" code="<DatePicker />">
<DatePicker placeholder="Select date..." className="w-full" />
</Card>
<Card label="Week Picker" code="<WeekPicker />">
<WeekPicker placeholder="Select week..." className="w-full" />
</Card>
@@ -74,23 +77,21 @@ export default function PickerShowcase({status}: {status: InputStatusType}) {
<MonthPicker placeholder="Select month..." className="w-full" />
</Card>
<Card label="Quarter Picker" code="<QuarterPicker />">
<Card label="Quarter Picker" code="<QuarterPicker />">
<QuarterPicker placeholder="Select quarter..." className="w-full" />
</Card>
<Card label="Year Picker" code="<YearPicker />">
<YearPicker placeholder="Select year..." className="w-full" />
</Card>
</Section>
{/* 2. Range Pickers */}
<Section title="Range Pickers" description="Komponen untuk memilih rentang waktu (Start - End).">
<Card label="Time Range" code="<TimeRangePicker />">
<Card label="Time Range" code="<TimeRangePicker />">
<TimeRangePicker className="w-full" />
</Card>
<Card label="Date Range" code="<DateRangePicker />">
<DateRangePicker className="w-full" />
</Card>
@@ -110,23 +111,21 @@ export default function PickerShowcase({status}: {status: InputStatusType}) {
<Card label="Year Range" code="<YearRangePicker />">
<YearRangePicker className="w-full" />
</Card>
</Section>
{/* 3. Validation States */}
<Section title="Validation & States" description="Visualisasi state error, warning, dan disabled.">
<Card label="Error State" code={`status="${status}"`} >
<Card label="Error State" code={`status="${status}"`}>
<DatePicker status={status} placeholder="Field is required" />
<span className="text-xs text-red-500 mt-1 block">This field is required.</span>
</Card>
<Card label="Warning State" code={`status="${status}"`} >
<Card label="Warning State" code={`status="${status}"`}>
<DatePicker status={status} placeholder="Check compatibility" defaultValue={dayjs()} />
<span className="text-xs text-yellow-600 mt-1 block">Warning: Date is in the past.</span>
</Card>
<Card label="Success State" code={`status="${status}"`} >
<Card label="Success State" code={`status="${status}"`}>
<DatePicker status={status} defaultValue={dayjs()} />
</Card>
@@ -141,22 +140,15 @@ export default function PickerShowcase({status}: {status: InputStatusType}) {
{/* 4. Advanced & Customization */}
<Section title="Advanced Features" description="Fitur lanjutan seperti format, waktu, dan presets.">
<Card label="Date & Time" code="showTime">
<DatePicker
showTime
format="YYYY-MM-DD HH:mm"
placeholder="Select date & time"
/>
<DatePicker showTime format="YYYY-MM-DD HH:mm" placeholder="Select date & time" />
</Card>
<Card label="Custom Format" code='format="DD/MM/YYYY"'>
<DatePicker
format="DD/MM/YYYY"
placeholder="31/12/2025"
/>
<DatePicker format="DD/MM/YYYY" placeholder="31/12/2025" />
</Card>
<Card label="Range with Presets" code="presets={[...]}">
<DateRangePicker
<DateRangePicker
presets={[
{ label: 'Today', value: [dayjs(), dayjs()] },
{ label: 'Next 7 Days', value: [dayjs(), dayjs().add(7, 'd')] },
@@ -166,11 +158,51 @@ export default function PickerShowcase({status}: {status: InputStatusType}) {
</Card>
<Card label="Read Only Input" code="inputReadOnly">
<DatePicker inputReadOnly placeholder="Click to open (No typing)" />
<DatePicker inputReadOnly placeholder="Click to open (No typing)" />
</Card>
</Section>
{/* 5. STATIC CALENDAR (Picker Panel) */}
<Section
title="Static Calendar (Embedded)"
description="Kalender statis yang langsung ditampilkan di halaman (bukan popup)."
>
{/* Basic Calendar */}
<div className="col-span-1 md:col-span-1">
<Card label="Standard Calendar" code="<Calendar />">
<div className="border border-gray-200 rounded-lg shadow-sm bg-white inline-block overflow-hidden">
<Calendar />
</div>
</Card>
</div>
{/* Calendar with State & Time */}
<div className="col-span-1 md:col-span-2">
<Card label="Calendar with Date & Time" code="<Calendar showTime />">
<div className="flex flex-col sm:flex-row gap-6 items-start">
{/* Visual Calendar */}
<div className="border border-gray-200 rounded-lg shadow-sm bg-white inline-block overflow-hidden">
<Calendar
showTime
onChange={(date) => {
console.log('Selected:', new DateUtils(date as any).format('YYYY-MM-DD HH:mm:ss'));
}}
/>
</div>
{/* Info Text */}
<div className="p-4 bg-blue-50 text-blue-800 rounded-lg text-sm max-w-xs">
<p className="font-semibold mb-2">Info:</p>
<p>
Komponen <code>Calendar</code> ini adalah <i>Panel</i> dasar. Sangat berguna untuk sidebar,
dashboard widget, atau form yang membutuhkan pemilihan tanggal yang selalu terlihat.
</p>
</div>
</div>
</Card>
</div>
</Section>
</div>
</div>
);
}
}
@@ -3,6 +3,7 @@ import { InputStatusType } from '@repo/ui';
import CheckboxShowcase from './components/checkbox.showcase';
import InputShowCase from './components/input.showcase';
import { SwitchShowcase } from './components/switch.showcase';
import PickerShowcase from './components/date-picker.showcase';
export default function Showcase() {
// State untuk menyimpan status yang dipilih
@@ -66,9 +67,10 @@ export default function Showcase() {
</div>
<div className="space-y-12 p-8 ">
<SwitchShowcase />
<CheckboxShowcase status={selectedStatus} />
<InputShowCase status={selectedStatus} />
{/* <SwitchShowcase /> */}
{/* <CheckboxShowcase status={selectedStatus} /> */}
{/* <InputShowCase status={selectedStatus} /> */}
<PickerShowcase status={selectedStatus} />
</div>
</div>
</section>