diff --git a/src/content/api-reference/calendar.ts b/src/content/api-reference/calendar.ts index e8db1187b..8094d98c5 100644 --- a/src/content/api-reference/calendar.ts +++ b/src/content/api-reference/calendar.ts @@ -2,14 +2,14 @@ import type { APISchema } from "@/types"; import * as C from "@/content/constants.js"; import { asChild, - daysOfWeekSlotProp, + weekdaysSlotProp, enums, monthsSlotProp } from "@/content/api-reference/helpers.js"; import type * as Calendar from "$lib/bits/calendar/_types.js"; import { attrsSlotProp, builderAndAttrsSlotProps } from "./helpers"; -const root: APISchema = { +export const root: APISchema = { title: "Root", description: "The root calendar component which contains all other calendar components.", props: { @@ -121,7 +121,7 @@ const root: APISchema = { }, slotProps: { months: monthsSlotProp, - daysOfWeek: daysOfWeekSlotProp, + daysOfWeek: weekdaysSlotProp, ...builderAndAttrsSlotProps }, dataAttributes: [ @@ -144,7 +144,7 @@ const root: APISchema = { ] }; -const cell: APISchema = { +export const cell: APISchema = { title: "Cell", description: "A cell in the calendar grid.", props: { @@ -169,7 +169,7 @@ const cell: APISchema = { ] }; -const date: APISchema = { +export const date: APISchema = { title: "Date", description: "A date in the calendar grid.", props: { @@ -238,7 +238,7 @@ const date: APISchema = { ] }; -const grid: APISchema = { +export const grid: APISchema = { title: "Grid", description: "The grid of dates in the calendar, typically representing a month.", props: { @@ -253,7 +253,7 @@ const grid: APISchema = { ] }; -const gridBody: APISchema = { +export const gridBody: APISchema = { title: "GridBody", description: "The body of the grid of dates in the calendar.", props: { @@ -268,7 +268,7 @@ const gridBody: APISchema = { ] }; -const gridHead: APISchema = { +export const gridHead: APISchema = { title: "GridHead", description: "The head of the grid of dates in the calendar.", props: { @@ -285,7 +285,7 @@ const gridHead: APISchema = { ] }; -const gridRow: APISchema = { +export const gridRow: APISchema = { title: "GridRow", description: "A row in the grid of dates in the calendar.", props: { @@ -302,7 +302,7 @@ const gridRow: APISchema = { ] }; -const headCell: APISchema = { +export const headCell: APISchema = { title: "HeadCell", description: "A cell in the head of the grid of dates in the calendar.", props: { @@ -319,7 +319,7 @@ const headCell: APISchema = { ] }; -const header: APISchema = { +export const header: APISchema = { title: "Header", description: "The header of the calendar.", props: { @@ -336,7 +336,7 @@ const header: APISchema = { ] }; -const heading: APISchema = { +export const heading: APISchema = { title: "Heading", description: "The heading of the calendar.", props: { @@ -357,7 +357,7 @@ const heading: APISchema = { ] }; -const nextButton: APISchema = { +export const nextButton: APISchema = { title: "NextButton", description: "The next button of the calendar.", props: { @@ -374,7 +374,7 @@ const nextButton: APISchema = { ] }; -const prevButton: APISchema = { +export const prevButton: APISchema = { title: "PrevButton", description: "The previous button of the calendar.", props: { diff --git a/src/content/api-reference/date-field.ts b/src/content/api-reference/date-field.ts new file mode 100644 index 000000000..47fdb7b30 --- /dev/null +++ b/src/content/api-reference/date-field.ts @@ -0,0 +1,212 @@ +import * as C from "@/content/constants.js"; +import type * as DateField from "$lib/bits/date-field/_types.js"; +import type { APISchema } from "@/types"; +import { enums, idsSlotProp, union, asChild } from "@/content/api-reference/helpers.js"; +import { builderAndAttrsSlotProps } from "./helpers"; + +export const root: APISchema = { + title: "Root", + description: "The root date field component.", + props: { + value: { + type: "DateValue", + description: "The selected date." + }, + onValueChange: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue | undefined) => void" + }, + description: "A function that is called when the selected date changes." + }, + placeholder: { + type: "DateValue", + description: + "The placeholder date, which is used to determine what date to start the segments from when no value exists." + }, + onPlaceholderChange: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue) => void" + }, + description: "A function that is called when the placeholder date changes." + }, + isDateUnavailable: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue) => boolean" + }, + description: "A function that returns whether or not a date is unavailable." + }, + hourCycle: { + type: { + type: C.ENUM, + definition: union("12", "24") + }, + description: "The hour cycle to use for formatting times. Defaults to the locale preference" + }, + granularity: { + type: { + type: C.ENUM, + definition: enums("day", "hour", "minute", "second") + }, + description: + "The granularity to use for formatting the field. Defaults to `'day'` if a `CalendarDate` is provided, otherwise defaults to `'minute'`. The field will render segments for each part of the date up to and including the specified granularity." + }, + hideTimeZone: { + type: C.BOOLEAN, + description: "Whether or not to hide the time zone segment of the field.", + default: C.FALSE + }, + validationId: { + type: C.STRING, + description: + "The id of your validation message element, if any, which will be applied to the `aria-describedby` attribute of the appropriate elements when a validation error occurs." + }, + descriptionId: { + type: C.STRING, + description: + "The id of your description element, if any, which will be applied to the `aria-describedby` attribute of the appropriate elements." + }, + maxValue: { + type: "DateValue", + description: "The maximum valid date that can be entered." + }, + minValue: { + type: "DateValue", + description: "The minimum valid date that can be entered." + }, + locale: { + type: C.STRING, + description: "The locale to use for formatting dates." + }, + disabled: { + default: C.FALSE, + type: C.BOOLEAN, + description: "Whether or not the accordion is disabled." + }, + readonly: { + type: C.BOOLEAN, + description: "Whether or not the field is readonly.", + default: C.FALSE + } + }, + slotProps: { + ids: idsSlotProp, + isInvalid: { + type: C.BOOLEAN, + description: "Whether or not the field is invalid." + } + } +}; + +const input: APISchema = { + title: "Input", + description: "The container for the segments of the date field.", + props: { + asChild + }, + slotProps: { + ...builderAndAttrsSlotProps, + segments: { + type: { + type: C.ARRAY, + definition: "{ part: SegmentPart; value: string; }[]" + }, + description: "An array of objects used to render the segments of the date field." + } + }, + dataAttributes: [ + { + name: "invalid", + description: "Present on the element when the field is invalid." + }, + { + name: "disabled", + description: "Present on the element when the field is disabled." + }, + { + name: "date-field-input", + description: "Present on the element." + } + ] +}; + +const segment: APISchema = { + title: "Segment", + description: "A segment of the date field.", + props: { + asChild, + part: { + type: { + type: "SegmentPart", + definition: enums( + "month", + "day", + "year", + "hour", + "minute", + "second", + "dayPeriod", + "timeZoneName", + "literal" + ) + }, + description: "The part of the date to render." + } + }, + slotProps: { + ...builderAndAttrsSlotProps + }, + dataAttributes: [ + { + name: "invalid", + description: "Present on the element when the field is invalid" + }, + { + name: "disabled", + description: "Present on the element when the field is disabled" + }, + { + name: "segment", + value: enums( + "month", + "day", + "year", + "hour", + "minute", + "second", + "dayPeriod", + "timeZoneName", + "literal" + ), + isEnum: true, + description: "The type of segment the element represents." + }, + { + name: "date-field-segment", + description: "Present on the element." + } + ] +}; + +const label: APISchema = { + title: "Label", + description: "The label for the date field.", + props: { asChild }, + slotProps: { + ...builderAndAttrsSlotProps + }, + dataAttributes: [ + { + name: "invalid", + description: "Present on the element when the field is invalid" + }, + { + name: "date-field-label", + description: "Present on the element." + } + ] +}; + +export const dateField = [root, input, segment, label]; diff --git a/src/content/api-reference/date-picker.ts b/src/content/api-reference/date-picker.ts new file mode 100644 index 000000000..87640e8fe --- /dev/null +++ b/src/content/api-reference/date-picker.ts @@ -0,0 +1,280 @@ +import type { APISchema } from "@/types"; +import * as C from "@/content/constants.js"; +import { + asChild, + weekdaysSlotProp, + enums, + monthsSlotProp, + union +} from "@/content/api-reference/helpers.js"; +import type * as DatePicker from "$lib/bits/date-picker/_types.js"; +import { builderAndAttrsSlotProps, portalProp } from "./helpers"; +import { focusProp } from "./extended-types"; +import { + header, + cell, + gridBody, + gridHead, + headCell, + gridRow, + heading, + nextButton, + prevButton, + date, + grid +} from "./calendar"; +import { content, trigger } from "./popover"; + +const root: APISchema = { + title: "Root", + description: "The root date picker component.", + props: { + value: { + type: "DateValue", + description: "The selected date." + }, + onValueChange: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue | undefined) => void" + }, + description: "A function that is called when the selected date changes." + }, + placeholder: { + type: "DateValue", + description: + "The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar, and can be used to programatically control the calendar's view." + }, + onPlaceholderChange: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue) => void" + }, + description: "A function that is called when the placeholder date changes." + }, + pagedNavigation: { + type: C.BOOLEAN, + description: + "Whether or not to use paged navigation for the calendar. Paged navigation causes the previous and next buttons to navigate by the number of months displayed at once, rather than by one month.", + default: C.FALSE + }, + preventDeselect: { + type: C.BOOLEAN, + description: + "Whether or not to prevent the user from deselecting a date without selecting another date first.", + default: C.FALSE + }, + weekStartsOn: { + type: C.NUMBER, + description: "The day of the week to start the calendar on. 0 is Sunday, 1 is Monday, etc.", + default: "0" + }, + weekdayFormat: { + type: { + type: C.ENUM, + definition: enums("narrow", "short", "long") + }, + description: + "The format to use for the weekday strings provided via the `weekdays` slot prop.", + default: "'narrow'" + }, + calendarLabel: { + type: C.STRING, + description: "The accessible label for the calendar." + }, + fixedWeeks: { + type: C.BOOLEAN, + description: "Whether or not to always display 6 weeks in the calendar.", + default: C.FALSE + }, + isDateDisabled: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue) => boolean" + }, + description: "A function that returns whether or not a date is disabled." + }, + isDateUnavailable: { + type: { + type: C.FUNCTION, + definition: "(date: DateValue) => boolean" + }, + description: "A function that returns whether or not a date is unavailable." + }, + maxValue: { + type: "DateValue", + description: "The maximum date that can be selected." + }, + minValue: { + type: "DateValue", + description: "The minimum date that can be selected." + }, + locale: { + type: C.STRING, + description: "The locale to use for formatting dates." + }, + multiple: { + type: C.BOOLEAN, + description: "Whether or not multiple dates can be selected.", + default: C.FALSE + }, + numberOfMonths: { + type: C.NUMBER, + description: "The number of months to display at once.", + default: "1" + }, + disabled: { + default: C.FALSE, + type: C.BOOLEAN, + description: "Whether or not the accordion is disabled." + }, + readonly: { + type: C.BOOLEAN, + description: "Whether or not the calendar is readonly.", + default: C.FALSE + }, + hourCycle: { + type: { + type: C.ENUM, + definition: union("12", "24") + }, + description: "The hour cycle to use for formatting times. Defaults to the locale preference" + }, + granularity: { + type: { + type: C.ENUM, + definition: enums("day", "hour", "minute", "second") + }, + description: + "The granularity to use for formatting the field. Defaults to `'day'` if a `CalendarDate` is provided, otherwise defaults to `'minute'`. The field will render segments for each part of the date up to and including the specified granularity." + }, + hideTimeZone: { + type: C.BOOLEAN, + description: "Whether or not to hide the time zone segment of the field.", + default: C.FALSE + }, + validationId: { + type: C.STRING, + description: + "The id of your validation message element, if any, which will be applied to the `aria-describedby` attribute of the appropriate elements when a validation error occurs." + }, + descriptionId: { + type: C.STRING, + description: + "The id of your description element, if any, which will be applied to the `aria-describedby` attribute of the appropriate elements." + }, + disableFocusTrap: { + type: C.BOOLEAN, + default: C.FALSE, + description: + "Whether or not to disable the focus trap that is applied to the popover when it's open." + }, + preventScroll: { + type: C.BOOLEAN, + default: C.FALSE, + description: "Whether or not to prevent scrolling the body while the popover is open." + }, + closeOnOutsideClick: { + type: C.BOOLEAN, + default: C.TRUE, + description: "Whether or not to close the popover when clicking outside of it." + }, + closeOnEscape: { + type: C.BOOLEAN, + default: C.TRUE, + description: "Whether or not to close the popover when pressing the escape key." + }, + open: { + type: C.BOOLEAN, + default: C.FALSE, + description: "The open state of the link popover component." + }, + onOpenChange: { + type: { + type: C.FUNCTION, + definition: "(open: boolean) => void" + }, + description: "A callback that fires when the open state changes." + }, + openFocus: { + type: focusProp, + description: "Override the focus when the popover is opened." + }, + closeFocus: { + type: focusProp, + description: "Override the focus when the popover is closed." + }, + portal: { ...portalProp("popover") }, + + asChild + }, + slotProps: { + months: monthsSlotProp, + daysOfWeek: weekdaysSlotProp, + ...builderAndAttrsSlotProps + }, + dataAttributes: [ + { + name: "invalid", + description: "Present on the root element when the calendar is invalid." + }, + { + name: "disabled", + description: "Present on the root element when the calendar is disabled." + }, + { + name: "readonly", + description: "Present on the root element when the calendar is readonly." + }, + { + name: "calendar-root", + description: "Present on the root element." + } + ] +}; + +const calendar: APISchema = { + title: "Calendar", + description: "The calendar component containing the grids of dates.", + slotProps: { + months: monthsSlotProp, + weekdays: weekdaysSlotProp, + ...builderAndAttrsSlotProps + }, + dataAttributes: [ + { + name: "invalid", + description: "Present on the root element when the calendar is invalid." + }, + { + name: "disabled", + description: "Present on the root element when the calendar is disabled." + }, + { + name: "readonly", + description: "Present on the root element when the calendar is readonly." + }, + { + name: "calendar-root", + description: "Present on the root element." + } + ] +}; + +export const datePicker = [ + root, + calendar, + header, + content, + trigger, + heading, + nextButton, + prevButton, + cell, + date, + grid, + gridBody, + gridHead, + gridRow, + headCell +]; diff --git a/src/content/api-reference/helpers.ts b/src/content/api-reference/helpers.ts index 387f47474..66f728130 100644 --- a/src/content/api-reference/helpers.ts +++ b/src/content/api-reference/helpers.ts @@ -36,7 +36,7 @@ export const monthsSlotProp: PropSchema = { description: "The current months to display in the calendar. Used to render the calendar." }; -export const daysOfWeekSlotProp: PropSchema = { +export const weekdaysSlotProp: PropSchema = { type: "string[]", description: "The days of the week to display in the calendar, typically used within the table header." diff --git a/src/content/api-reference/index.ts b/src/content/api-reference/index.ts index 2e15f4259..5b4da2c8e 100644 --- a/src/content/api-reference/index.ts +++ b/src/content/api-reference/index.ts @@ -9,6 +9,8 @@ import { calendar } from "./calendar"; import { checkbox } from "./checkbox"; import { collapsible } from "./collapsible"; import { contextMenu } from "./context-menu"; +import { dateField } from "./date-field"; +import { datePicker } from "./date-picker"; import { dropdownMenu } from "./dropdown-menu"; import { label } from "./label"; import { linkPreview } from "./link-preview"; @@ -77,8 +79,8 @@ export const apiSchemas: Record = { checkbox, collapsible, "context-menu": contextMenu, - "date-field": [], - "date-picker": [], + "date-field": dateField, + "date-picker": datePicker, "date-range-field": [], "date-range-picker": [], dialog, diff --git a/src/content/api-reference/range-calendar.ts b/src/content/api-reference/range-calendar.ts index e33da4ead..055822ed9 100644 --- a/src/content/api-reference/range-calendar.ts +++ b/src/content/api-reference/range-calendar.ts @@ -3,7 +3,7 @@ import * as C from "@/content/constants.js"; import { asChild, attrsSlotProp, - daysOfWeekSlotProp, + weekdaysSlotProp, enums, monthsSlotProp } from "@/content/api-reference/helpers.js"; @@ -120,7 +120,7 @@ const root: APISchema = { }, slotProps: { months: monthsSlotProp, - daysOfWeek: daysOfWeekSlotProp, + daysOfWeek: weekdaysSlotProp, ...builderAndAttrsSlotProps }, dataAttributes: [ diff --git a/src/content/constants.ts b/src/content/constants.ts index 18bbe4f9c..adaa89720 100644 --- a/src/content/constants.ts +++ b/src/content/constants.ts @@ -9,6 +9,7 @@ export const OBJECT = "object"; export const FALSE = "false"; export const TRUE = "true"; export const UNKNOWN = "unknown"; +export const ARRAY = "array"; export const HORIZONTAL = "horizontal"; export const VERTICAL = "vertical"; diff --git a/src/lib/bits/date-field/_types.ts b/src/lib/bits/date-field/_types.ts index 33ba175b0..63e51d891 100644 --- a/src/lib/bits/date-field/_types.ts +++ b/src/lib/bits/date-field/_types.ts @@ -43,7 +43,7 @@ type Props = Expand< * This is used to apply the appropriate `aria-describedby` attribute to the input. */ descriptionId?: string; - } & AsChild + } >; type InputProps = AsChild; diff --git a/src/lib/bits/date-picker/_types.ts b/src/lib/bits/date-picker/_types.ts index 5c803e460..de64e13d9 100644 --- a/src/lib/bits/date-picker/_types.ts +++ b/src/lib/bits/date-picker/_types.ts @@ -4,13 +4,29 @@ * but we don't want to document the HTML attributes. */ -import type { Expand, OnChangeFn, AsChild, OmitDates } from "$lib/internal/index.js"; +import type { Expand, OnChangeFn, AsChild, OmitDates, OmitFloating } from "$lib/internal/index.js"; import type { SegmentPart } from "$lib/shared"; import type { DateValue } from "@internationalized/date"; import type { CreateDatePickerProps } from "@melt-ui/svelte"; type Props = Expand< - Omit, "required" | "name"> & { + Omit< + OmitFloating>, + "required" | "name" | "calendarIds" | "dateFieldIds" | "popoverIds" + > & { + /** + * The open state of the popover. + * You can bind this to a boolean value to programmatically control the open state. + * + * @default false + */ + open?: boolean; + + /** + * A callback function called when the open state changes. + */ + onOpenChange?: OnChangeFn; + /** * The value of the date field. * You can bind this to a `DateValue` object to programmatically control the value.