diff --git a/apps/blog/src/app/layout.tsx b/apps/blog/src/app/layout.tsx index 3f0c7dbe77..ef5105577e 100644 --- a/apps/blog/src/app/layout.tsx +++ b/apps/blog/src/app/layout.tsx @@ -1,19 +1,20 @@ -import { RootProvider } from 'fumadocs-ui/provider/next'; -import './global.css'; -import { Inter, Barlow } from 'next/font/google'; +import { RootProvider } from "fumadocs-ui/provider/next"; +import "./global.css"; +import { Inter, Barlow } from "next/font/google"; +import { FontAwesomeScript } from "@prisma-docs/eclipse"; const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', + subsets: ["latin"], + variable: "--font-inter", }); const barlow = Barlow({ - subsets: ['latin'], - weight: ['400', '500', '600', '700'], - variable: '--font-barlow', + subsets: ["latin"], + weight: ["400", "500", "600", "700"], + variable: "--font-barlow", }); -export default function Layout({ children }: LayoutProps<'/'>) { +export default function Layout({ children }: LayoutProps<"/">) { return ( ) { {children} + ); diff --git a/apps/docs/src/app/layout.tsx b/apps/docs/src/app/layout.tsx index 614d5bd278..7dd006d841 100644 --- a/apps/docs/src/app/layout.tsx +++ b/apps/docs/src/app/layout.tsx @@ -1,30 +1,31 @@ -import { Provider } from '@/components/provider'; -import { getBaseUrl } from '@/lib/urls'; -import './global.css'; -import { Inter, Barlow } from 'next/font/google'; -import type { Metadata } from 'next'; -import type { ReactNode } from 'react'; -import Script from 'next/script'; +import { Provider } from "@/components/provider"; +import { getBaseUrl } from "@/lib/urls"; +import "./global.css"; +import { Inter, Barlow } from "next/font/google"; +import type { Metadata } from "next"; +import type { ReactNode } from "react"; +import Script from "next/script"; +import { FontAwesomeScript } from "@prisma-docs/eclipse"; const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', + subsets: ["latin"], + variable: "--font-inter", }); const barlow = Barlow({ - subsets: ['latin'], - weight: ['400', '500', '600', '700'], - variable: '--font-barlow', + subsets: ["latin"], + weight: ["400", "500", "600", "700"], + variable: "--font-barlow", }); export const metadata: Metadata = { metadataBase: new URL(getBaseUrl()), title: { - default: 'Prisma Documentation', - template: '%s | Prisma Documentation', + default: "Prisma Documentation", + template: "%s | Prisma Documentation", }, description: - 'Documentation for Prisma ORM, Prisma Postgres, Prisma Accelerate, and the Prisma ecosystem. Build type-safe database applications with ease.', + "Documentation for Prisma ORM, Prisma Postgres, Prisma Accelerate, and the Prisma ecosystem. Build type-safe database applications with ease.", }; export default function Layout({ children }: { children: ReactNode }) { @@ -46,6 +47,7 @@ export default function Layout({ children }: { children: ReactNode }) { src="https://cdn-cookieyes.com/client_data/96980f76df67ad5235fc3f0d/script.js" id="cookieyes" /> + ); diff --git a/apps/eclipse/content/design-system/molecules/datepicker.mdx b/apps/eclipse/content/design-system/molecules/datepicker.mdx new file mode 100644 index 0000000000..217ed733e6 --- /dev/null +++ b/apps/eclipse/content/design-system/molecules/datepicker.mdx @@ -0,0 +1,604 @@ +--- +title: Date Picker +description: A flexible date picker component with support for single date selection, date ranges, and preset options. +--- + +import { + DatePickerSingleExample, + DatePickerRangeExample, + DatePickerRangeWithPresetsExample, + DatePickerErrorExample, + DatePickerDisabledExample, + DatePickerWithValidationExample, +} from "../../../src/components/date-picker-examples"; + +### Usage + +**Single Date Picker** + +```tsx +import { DatePickerSingle } from "@prisma-docs/eclipse"; +import { useState } from "react"; + +export function SingleDateExample() { + const [date, setDate] = useState(); + + return ( + + ); +} +``` + +**Live Example:** + +
+ +
+ +**Date Range Picker** + +```tsx +import { DatePickerRange } from "@prisma-docs/eclipse"; +import { useState } from "react"; +import type { DateRange } from "react-day-picker"; + +export function RangeDateExample() { + const [dateRange, setDateRange] = useState(); + + return ( + + ); +} +``` + +**Live Example:** + +
+ +
+ +**Date Range Picker with Presets** + +```tsx +import { + DatePickerRange, + createDateRangePresets, +} from "@prisma-docs/eclipse"; +import { useState } from "react"; +import type { DateRange } from "react-day-picker"; + +export function RangeWithPresets() { + const [dateRange, setDateRange] = useState(); + const presets = createDateRangePresets(); + + return ( + + ); +} +``` + +**Live Example:** + +
+ +
+ +**Unified Component** + +The unified `DatePicker` component supports both modes: + +```tsx +import { DatePicker } from "@prisma-docs/eclipse"; +import { useState } from "react"; + +export function UnifiedExample() { + const [date, setDate] = useState(); + + return ( + + ); +} +``` + +### DatePicker Props + +#### Common Props + +- `placeholder` - Placeholder text when no date is selected (optional) +- `disabled` - Disabled dates (can be a function, date, or array) (optional) +- `className` - Custom className for the trigger button (optional) +- `align` - Align popover content: `"start"`, `"center"`, or `"end"` (default: `"start"`) +- `isErrored` - Whether the date picker is in an error state (optional, default: `false`) +- `disabledBtn` - Whether the trigger button is disabled (optional, default: `false`) +- `dateFormat` - Date format string for displaying dates using date-fns format tokens (optional) + +#### Single Date Mode Props + +- `mode` - Set to `"single"` for single date selection +- `date` - The selected date (optional) +- `onDateChange` - Callback when date changes: `(date: Date | undefined) => void` (optional) + +#### Range Date Mode Props + +- `mode` - Set to `"range"` for date range selection +- `dateRange` - The selected date range (optional) +- `onDateRangeChange` - Callback when date range changes: `(range: DateRange | undefined) => void` (optional) +- `presets` - Array of preset date ranges (optional) + +### Component Variants + +#### DatePickerSingle + +Convenience component that automatically sets `mode="single"`: + +```tsx + +``` + +#### DatePickerRange + +Convenience component that automatically sets `mode="range"`: + +```tsx + +``` + +### Preset Helper + +Use `createDateRangePresets()` to generate common date range presets: + +```tsx +import { createDateRangePresets } from "@prisma-docs/eclipse"; + +const presets = createDateRangePresets(); +// Returns: Today, Last 7 days, Last 14 days, Last 30 days, +// Last 90 days, This month, Last month +``` + +**Custom Presets** + +```tsx +const customPresets = [ + { + label: "Yesterday", + dateRange: { + from: new Date(Date.now() - 24 * 60 * 60 * 1000), + to: new Date(Date.now() - 24 * 60 * 60 * 1000), + }, + }, + { + label: "This Week", + dateRange: { + from: startOfWeek(new Date()), + to: new Date(), + }, + }, +]; + + +``` + +### Features + +- ✅ Single date selection +- ✅ Date range selection +- ✅ Preset date ranges +- ✅ Custom disabled dates +- ✅ Flexible date formatting with date-fns +- ✅ Keyboard navigation +- ✅ Accessible with proper ARIA attributes +- ✅ Eclipse design system integration +- ✅ Responsive popover positioning + +### Common Use Cases + +**Event Scheduling** + +```tsx + date < new Date()} +/> +``` + +**Report Date Range** + +```tsx + +``` + +**Booking System** + +```tsx + { + const day = date.getDay(); + return day === 0 || day === 6; // Disable weekends + }} + placeholder="Select booking dates" +/> +``` + +**Analytics Dashboard** + +```tsx +const analyticsPresets = [ + { label: "Last 7 days", dateRange: { from: sevenDaysAgo, to: today } }, + { label: "Last 30 days", dateRange: { from: thirtyDaysAgo, to: today } }, + { label: "This Quarter", dateRange: { from: quarterStart, to: today } }, +]; + + +``` + +### Disabling Dates + +**Disable Past Dates** + +```tsx + date < new Date()} +/> +``` + +**Disable Future Dates** + +```tsx + date > new Date()} +/> +``` + +**Disable Specific Dates** + +```tsx +const disabledDates = [ + new Date(2024, 11, 25), // Christmas + new Date(2025, 0, 1), // New Year +]; + + +``` + +**Disable Date Ranges** + +```tsx + +``` + +**Disable Days of Week** + +```tsx + { + const day = date.getDay(); + return day === 0 || day === 6; // Disable weekends + }} +/> +``` + +### Error State + +Use the `isErrored` prop to indicate validation errors: + +```tsx +import { DatePickerSingle } from "@prisma-docs/eclipse"; +import { useState } from "react"; + +export function FormWithValidation() { + const [date, setDate] = useState(); + const [submitted, setSubmitted] = useState(false); + + const hasError = submitted && !date; + + return ( +
+ + {hasError && ( +

+ Date is required +

+ )} +
+ ); +} +``` + +**Live Example (Static Error State):** + +
+ +
+ +**Live Example (With Validation):** + +
+ +
+ +The error state adds a red ring (`ring-2 ring-stroke-error`) to the button and changes the text and icon color to `text-foreground-error` to indicate the validation error. + +### Disabled State + +Use the `disabledBtn` prop to disable the entire date picker button: + +```tsx + +``` + +**Live Example:** + +
+ +
+ +**Use Cases:** +- Disable based on permissions +- Disable while data is loading +- Disable when form is submitting +- Conditional disabling based on other form fields + +```tsx +function ConditionalDatePicker() { + const [isSubscribed, setIsSubscribed] = useState(false); + const [date, setDate] = useState(); + + return ( +
+ + + +
+ ); +} +``` + +### Formatting + +The component uses `date-fns` for date formatting. You can customize the format using the `dateFormat` prop: + +**Default Formats:** +- **Single date**: `PPP` format (e.g., "April 29, 2024") +- **Date range**: `LLL dd, y` format (e.g., "Apr 01, 2024 - Apr 30, 2024") + +**Custom Formats:** + +```tsx +// European format: dd/MM/yyyy + +// Output: "17/02/2026" + +// US format: MM/dd/yyyy + +// Output: "02/17/2026" + +// ISO format: yyyy-MM-dd + +// Output: "2026-02-17" + +// Custom verbose format + +// Output: "Tuesday, February 17th, 2026 - Friday, February 20th, 2026" +``` + +**Common date-fns Format Tokens:** +- `dd` - Day of month (01-31) +- `MM` - Month (01-12) +- `yyyy` - Full year (2026) +- `yy` - 2-digit year (26) +- `MMM` - Short month name (Feb) +- `MMMM` - Full month name (February) +- `do` - Day with ordinal (17th) +- `EEEE` - Full day name (Tuesday) +- `PPP` - Long localized date (February 17th, 2026) +- `P` - Short localized date (02/17/2026) + +### Best Practices + +- Use **single date picker** for events, deadlines, or appointments +- Use **range picker** for reports, analytics, or booking periods +- Provide **presets** for common date ranges to improve UX +- **Disable irrelevant dates** (e.g., past dates for future bookings) +- Use clear **placeholder text** that indicates what the date is for +- Consider **default values** for better user experience +- Add **validation** to ensure date ranges make sense +- Show **clear labels** above date pickers in forms +- Use **consistent date formats** across your application +- Use `isErrored` prop with validation messages for better UX +- Use `disabledBtn` for conditional access or loading states +- Choose appropriate `dateFormat` based on your user's locale and preferences + +### Accessibility + +- Full keyboard navigation support +- ARIA labels and roles for screen readers +- Focus management within the calendar +- Escape key to close the popover +- Tab navigation between dates +- Enter/Space to select dates +- Arrow keys to navigate calendar days +- High contrast colors for readability +- Clear visual focus indicators + +### Design Tokens + +The component uses Eclipse design system tokens: + +- Button: Standard button variants and sizes +- Popover: `bg-popover`, `text-popover-foreground` +- Calendar: Eclipse calendar component styling +- Borders: `border-stroke-neutral` +- Text: `text-muted-foreground` for placeholder +- Icons: Lucide React `CalendarIcon` + +### TypeScript Support + +The component is fully typed with TypeScript: + +```tsx +import type { DateRange, Matcher } from "react-day-picker"; +import type { DatePickerProps } from "@prisma-docs/eclipse"; + +const props: DatePickerProps = { + mode: "range", + dateRange: { from: new Date(), to: new Date() }, + onDateRangeChange: (range) => console.log(range), + presets: [ + { label: "Last 7 days", dateRange: { from: new Date(), to: new Date() } }, + ], +}; +``` + +### Integration with Forms + +**With React Hook Form** + +```tsx +import { useForm, Controller } from "react-hook-form"; +import { DatePickerSingle } from "@prisma-docs/eclipse"; + +function MyForm() { + const { control, handleSubmit, formState: { errors } } = useForm(); + + return ( +
+ ( +
+ + {errors.eventDate && ( +

+ {errors.eventDate.message} +

+ )} +
+ )} + /> + + ); +} +``` + +**With Native State** + +```tsx +function MyForm() { + const [startDate, setStartDate] = useState(); + const [endDate, setEndDate] = useState(); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log({ startDate, endDate }); + }; + + return ( +
+
+ + + +
+
+ ); +} +``` diff --git a/apps/eclipse/content/design-system/molecules/meta.json b/apps/eclipse/content/design-system/molecules/meta.json index 68e73f73a9..67465512c9 100644 --- a/apps/eclipse/content/design-system/molecules/meta.json +++ b/apps/eclipse/content/design-system/molecules/meta.json @@ -8,6 +8,7 @@ "breadcrumb", "card", "codeblock", + "datepicker", "dialog", "dropdownmenu", "files", diff --git a/apps/eclipse/src/app/layout.tsx b/apps/eclipse/src/app/layout.tsx index 1fb716e0e7..ed1a767c49 100644 --- a/apps/eclipse/src/app/layout.tsx +++ b/apps/eclipse/src/app/layout.tsx @@ -2,6 +2,7 @@ import { Provider } from "@/components/provider"; import Script from "next/script"; import "./global.css"; import { Inter, Barlow } from "next/font/google"; +import { FontAwesomeScript } from "@prisma-docs/eclipse"; const inter = Inter({ subsets: ["latin"], @@ -16,7 +17,11 @@ const barlow = Barlow({ export default function Layout({ children }: LayoutProps<"/">) { return ( - +