"use client" import * as React from "react" import type { ComponentDocSpec } from "@/lib/design-system/component-doc-types" import { SelectGroupsPreview, SelectPreview, SelectScrollablePreview, SelectStatesPreview, } from "@/components/design-system/data-display-previews" import { CalendarDropdownCaptionPreview, CalendarPreview, CalendarRangePreview, CalendarTwoMonthPreview, DatePickerPreview, DateRangePickerPreview, DateTextInputPreview, } from "@/components/design-system/date-previews" import { CheckboxFieldPreview, FieldBasicPreview, FieldDisabledPreview, FieldInvalidPreview, FieldLayoutsPreview, FieldReadOnlyPreview, FormHierarchyPreview, FormInCardPreview, FormRhfPreview, RadioGroupChoiceCardPreview, RadioGroupDefaultPreview, RichTextFormPreview, } from "@/components/design-system/form-previews" import { InlineEditPreview } from "@/components/design-system/inline-edit-previews" import { InputDefaultPreview, InputGroupPreview, InputMaskPreview, InputOTPPreview, InputWithPrimaryButtonPreview, LabelPreview, PasswordValidationPreview, PaymentCardFieldsPreview, SliderPreview, TextareaDefaultPreview, TextareaStatesPreview, } from "@/components/design-system/input-previews" function ex( section: Omit, children: React.ReactNode, description?: string, ) { return { ...section, description, children } } const formGuidelines: ComponentDocSpec["guidelines"] = { do: [ "Prefer Field + native control for simple settings and hub filters.", "Use Form + FormField + react-hook-form for multi-field submit flows.", "Use one form header, clear section headings, and subsections only when they improve scanning.", "Use Card for one compact form task; use open sections for longer page forms.", "Wire errors to FormMessage or Field invalid state. Keep feedback inline.", "Disable each control directly. Field disabled styles the group but does not block descendant controls.", "Use InlineInputEdit or InlineSelectEdit only for short, reversible changes without help text.", "Sanitize stored rich-text HTML at the server boundary before rendering it outside RichTextEditor.", ], dont: [ "Do not use raw Label without Field in product forms.", "Do not skip visible labels on required fields.", "Do not use SettingsFormRow as a general Form primitive. It is a settings-page grid recipe.", "Do not replace a full form with inline editing when the value needs validation guidance.", ], } const inputGuidelines: ComponentDocSpec["guidelines"] = { do: [ "Wrap text controls in Field (or FormField) so label, help, and error stay linked.", "Use InputGroup for leading/trailing addons; MaskedInput for phone, ZIP, and date shapes.", "Use PaymentCardFieldsGroup for card number / expiry / CVC — one payment-inputs hook.", ], dont: [ "Do not invent local mask formatting when MaskedInput covers the pattern.", "Do not use Label alone when FieldLabel is available in a form row.", ], } export const formComponentDoc: ComponentDocSpec = { slug: "form", summary: "Complete form compositions: hierarchy, card-contained forms, sections, rich text, validation, submission states, and field anatomy.", sections: [ ex( { id: "hierarchy", title: "Form header, sections, and subsections" }, , "One form title, visible section hierarchy, nested subsection, and one form-level action row.", ), ex( { id: "card", title: "Form in a card" }, , "Use a card for one compact task with a short field set and card-level actions.", ), ex( { id: "rhf", title: "Validation and submission lifecycle" }, , "Blur validation, linked error summary, busy submit state, keyboard submit, and inline success.", ), ex( { id: "rich-text", title: "Rich text field" }, , "Fixed block controls plus a contextual toolbar when text is selected.", ), ex( { id: "inline-edit", title: "Inline editing" }, , "Short text and enumerated values with explicit Save and Cancel controls.", ), ex( { id: "field", title: "Field layouts" }, , "Toggle top and left labels. Includes select, date, checkbox, and radio controls.", ), ex( { id: "field-basic", title: "Basic field" }, , "Label + Input + FieldDescription.", ), ex( { id: "field-invalid", title: "Invalid field" }, , "data-invalid on Field, aria-invalid on the control, and a visible FieldError.", ), ex( { id: "field-readonly", title: "Read-only field" }, , "Focusable and selectable value that cannot be edited.", ), ex( { id: "field-disabled", title: "Disabled field" }, , "data-disabled on Field, disabled on the control.", ), ex( { id: "field-set", title: "FieldSet (checkboxes)" }, , "FieldSet + FieldLegend for related options.", ), ], anatomy: [ { part: "Field", description: "Label + control + description. Vertical or horizontal.", slug: "field" }, { part: "FieldSet / FieldLegend", description: "Grouped radios or checkboxes with shared legend." }, { part: "FieldDescription", description: "Help text below the control." }, { part: "FieldError", description: "Inline error list under the control." }, { part: "Form", description: "Provider wrapper from react-hook-form." }, { part: "FormField", description: "Connects name to control + message." }, { part: "FormMessage", description: "Inline validation copy." }, { part: "RichTextEditor", description: "Structured text input with fixed and contextual formatting.", slug: "rich-text-editor" }, ], guidelines: formGuidelines, relatedSlugs: ["input", "textarea", "select", "checkbox", "radio-group", "inline-edit", "rich-text-editor"], extraImports: [ { label: "Field", path: "@exxatdesignux/ui/components/ui/field" }, { label: "RichTextEditor", path: "@exxatdesignux/ui/components/ui/rich-text-editor" }, ], } export const textareaComponentDoc: ComponentDocSpec = { slug: "textarea", summary: "Multiline text input with persistent help, invalid, disabled, read-only, resizable, and fixed-height examples.", sections: [ ex( { id: "default", title: "Default" }, , "Textarea paired with a visible FieldLabel and persistent description.", ), ex( { id: "states", title: "Variants and states" }, , "Help, invalid, disabled, read-only, resizable, and deliberate fixed-height use.", ), ], anatomy: [ { part: "Textarea", description: "Native multiline control with shared field chrome." }, { part: "FieldLabel", description: "Visible accessible name." }, { part: "FieldDescription", description: "Persistent format or validation guidance." }, { part: "FieldError", description: "Inline error associated through aria-describedby." }, ], guidelines: { do: [ "Keep the default resize behavior when the page can grow.", "Use rows to provide a useful initial height for the expected response.", "Use read-only when users may need to focus, select, or copy the value.", ], dont: [ "Do not use placeholder text as the only instruction.", "Do not use resize-none unless the surrounding layout cannot grow.", "Do not use Textarea for formatted content. Use RichTextEditor.", ], }, accessibility: [ "Pair every textarea with FieldLabel or another programmatic accessible name.", "Set aria-invalid and connect FieldError when validation fails.", "Disabled values are unavailable. Read-only values remain focusable and selectable.", ], relatedSlugs: ["form", "input", "rich-text-editor"], } export const inlineEditComponentDoc: ComponentDocSpec = { slug: "inline-edit", summary: "Compact in-place editing for short text and enumerated values. Draft changes require an explicit Save or Cancel action.", sections: [ ex( { id: "field-values", title: "Text and select values" }, , "Use on detail rows or compact metadata where the edit is short and reversible.", ), ], anatomy: [ { part: "InlineInputEdit", description: "Text value, Input draft, and Save or Cancel actions." }, { part: "InlineSelectEdit", description: "Display value, Select draft, and Save or Cancel actions." }, { part: "InlineInputCell", description: "DataTable sized text editor.", slug: "table-cells" }, { part: "InlineSelectCell", description: "DataTable sized select editor.", slug: "table-cells" }, ], guidelines: { do: [ "Use for one short value whose meaning is already clear from surrounding context.", "Keep Save and Cancel explicit. Enter saves text and Escape cancels.", "Provide a specific label so table cell controls include the row identity.", ], dont: [ "Do not use for destructive changes or values with complex validation.", "Do not hide required format guidance inside an inline editor.", "Do not commit a select option before the user confirms it.", ], }, accessibility: [ "View mode announces Edit, the field label, and the current value.", "Save and Cancel icon buttons expose matching aria-label and Tip text.", "Cell editors stop row activation while editing.", ], relatedSlugs: ["form", "input", "select", "table", "table-cells"], extraImports: [ { label: "Inline edit", path: "@exxatdesignux/ui/components/ui/inline-edit" }, { label: "Inline table cells", path: "@exxatdesignux/ui/components/data-views/table-cells" }, ], } export const inputComponentDoc: ComponentDocSpec = { slug: "input", summary: "Text-entry family on one page: Input, icon addons, action composition, password validation, OTP, Textarea, masks, and payment fields.", sections: [ ex( { id: "default", title: "Input" }, , "Default, disabled, and invalid. Pair with Field in product UI.", ), ex( { id: "textarea", title: "Textarea" }, , "Multiline stem / notes. Same Field pairing as Input.", ), ex( { id: "input-group", title: "Input group" }, , "Leading and trailing icons, a trailing unit, and a trailing action.", ), ex( { id: "input-button", title: "Input with primary action" }, , "Input and one filled primary button for a compact submit composition.", ), ex( { id: "password", title: "Password with validation" }, , "Persistent requirements update as the password changes.", ), ex( { id: "otp", title: "One time password" }, , "Six digit verification code with grouped slots.", ), ex( { id: "input-mask", title: "Input mask" }, , "Phone, ZIP, date (MM/DD/YYYY), and custom reference via MaskedInput + exxatMaskPatterns.", ), ex( { id: "label", title: "Label" }, , "Low-level primitive. Prefer FieldLabel inside Field.", ), ex({ id: "slider", title: "Slider" }, ), ex( { id: "payment-card-fields", title: "Payment card fields" }, , "Card number, expiry, and CVC as one group.", ), ], anatomy: [ { part: "Input", description: "Single-line text control." }, { part: "Textarea", description: "Multiline text control.", slug: "textarea" }, { part: "InputGroup", description: "Chrome + addons around InputGroupInput.", slug: "input-group" }, { part: "InputOTP", description: "Accessible one time password input with grouped slots.", slug: "input-otp" }, { part: "MaskedInput", description: "Input + use-mask-input patterns.", slug: "input-mask" }, { part: "Label", description: "Standalone label; prefer FieldLabel.", slug: "label" }, { part: "Slider", description: "Range value picker.", slug: "slider" }, { part: "PaymentCardFieldsGroup", description: "Card number / expiry / CVC.", slug: "payment-card-fields" }, ], guidelines: inputGuidelines, relatedSlugs: ["form", "select", "date-picker"], extraImports: [ { label: "Textarea", path: "@exxatdesignux/ui/components/ui/textarea" }, { label: "InputGroup", path: "@exxatdesignux/ui/components/ui/input-group" }, { label: "InputOTP", path: "@exxatdesignux/ui/components/ui/input-otp" }, { label: "MaskedInput", path: "@exxatdesignux/ui/components/ui/input-mask" }, { label: "Label", path: "@exxatdesignux/ui/components/ui/label" }, { label: "Slider", path: "@exxatdesignux/ui/components/ui/slider" }, { label: "PaymentCardFieldsGroup", path: "@exxatdesignux/ui/components/ui/payment-card-fields" }, ], } export const selectComponentDoc: ComponentDocSpec = { slug: "select", summary: "Dropdown select (shadcn / Radix): item-aligned or popper, grouped lists, scrollable options, invalid/disabled/sm.", sections: [ ex( { id: "default", title: "Item-aligned (default)" }, , "Selected item sits over the trigger; open uses animate-none.", ), ex( { id: "popper", title: "Popper (edge-aligned)" }, , "Anchors to the trigger edge with a side-aware enter animation.", ), ex( { id: "groups", title: "Groups and separators" }, , "SelectGroup + SelectLabel + SelectSeparator. Disabled items supported.", ), ex( { id: "scrollable", title: "Scrollable list" }, , "Long lists with max-h on SelectContent.", ), ex( { id: "states", title: "Invalid, disabled, and size" }, , ), ], relatedSlugs: ["form", "input", "date-picker", "dropdown-menu"], } export const radioGroupComponentDoc: ComponentDocSpec = { slug: "radio-group", summary: "Radio set, choice cards, and selection tiles for exclusive picks.", sections: [ ex({ id: "default", title: "Horizontal rows" }, ), ex({ id: "choice-card", title: "Choice cards" }, ), ], guidelines: { do: ["Use Field + RadioGroupItem for simple lists.", "Use choice cards when options need descriptions."], dont: ["Do not use Tabs for exclusive single-select — use RadioGroup or segmented control."], }, relatedSlugs: ["form", "checkbox"], } export const datePickerComponentDoc: ComponentDocSpec = { slug: "date-picker", summary: "Calendar grids and date field triggers: single, range, dropdown caption, DatePickerField, DateRangePickerField, DateTextInputField.", sections: [ ex( { id: "calendar", title: "Calendar (label caption)" }, , "Low-level DayPicker grid with month label + chevrons.", ), ex( { id: "calendar-dropdown", title: "Calendar (dropdown caption)" }, , "Month and year Selects in the caption — used inside date fields.", ), ex( { id: "calendar-range", title: "Calendar range (one month)" }, , ), ex( { id: "calendar-two-month", title: "Calendar range (two months)" }, , "Default for DateRangePickerField on wide surfaces.", ), ex( { id: "date-picker", title: "Date picker field" }, , "Popover trigger with formatted value + calendar icon.", ), ex( { id: "date-range", title: "Date range field" }, , "One-month and two-month popover variants.", ), ex( { id: "date-text", title: "Masked date text field" }, , "Type MM/DD/YYYY or open the calendar from the icon.", ), ], anatomy: [ { part: "Calendar", description: "react-day-picker grid primitive.", slug: "calendar" }, { part: "DatePickerField", description: "Single-date popover field." }, { part: "DateRangePickerField", description: "Range popover field." }, { part: "DateTextInputField", description: "Masked input + calendar affordance." }, ], guidelines: { do: [ "Use DatePickerField / DateRangePickerField in forms; bare Calendar only inside custom popovers.", "Prefer captionLayout dropdown when jumping years (placements, compliance windows).", ], dont: [ "Do not use a plain Input for dates when a picker or mask exists.", "Do not put two-month range calendars in narrow sheets — pass numberOfMonths={1}.", ], }, relatedSlugs: ["form", "input", "select"], extraImports: [ { label: "Calendar", path: "@exxatdesignux/ui/components/ui/calendar" }, { label: "DatePickerField", path: "@exxatdesignux/ui/components/ui/date-picker-field" }, ], }