feat(forms): add useMaskedField and useMaskedInput composables for input masking
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* A single `role="gridcell"` day container (`<td>`). Reflects the date's state
|
||||
* (selected, disabled, unavailable, outside-view, today) as `data-*`
|
||||
* attributes and `aria-*` for styling, and wraps the focusable
|
||||
* `CalendarCellTrigger`.
|
||||
*/
|
||||
export interface CalendarCellProps extends PrimitiveProps {
|
||||
/** The date this cell represents. */
|
||||
date: Date;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* The focusable, clickable day button inside a `CalendarCell`. Selects its
|
||||
* `day` on click/Enter/Space, drives roving focus and full arrow-key /
|
||||
* Home-End / PageUp-Down keyboard navigation (paging the month when focus
|
||||
* crosses the visible range), and exposes day state through its slot.
|
||||
*/
|
||||
export interface CalendarCellTriggerProps extends PrimitiveProps {
|
||||
/** The day this trigger represents. */
|
||||
day: Date;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* The `role="grid"` table for a single month. Provides grid context (the month
|
||||
* it renders) to its head/body cells; render one per visible month when
|
||||
* `numberOfMonths > 1`.
|
||||
*/
|
||||
export interface CalendarGridProps extends PrimitiveProps {
|
||||
/** The month this grid represents. Defaults to the root placeholder's month. */
|
||||
month?: Date;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* The grid's `<tbody>` wrapper containing the week rows (`CalendarGridRow`) of
|
||||
* day cells.
|
||||
*/
|
||||
export interface CalendarGridBodyProps extends PrimitiveProps {}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* The grid's `<thead>` wrapper holding the row of weekday `CalendarHeadCell`
|
||||
* labels.
|
||||
*/
|
||||
export interface CalendarGridHeadProps extends PrimitiveProps {}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* A single table row (`<tr>`) representing one week of the month, or the
|
||||
* weekday-label row inside the grid head.
|
||||
*/
|
||||
export interface CalendarGridRowProps extends PrimitiveProps {}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* A `scope="col"` weekday header cell (`<th>`). Renders the localized short
|
||||
* label in its slot while exposing the full weekday name as the `aria-label`
|
||||
* when a `day` is provided.
|
||||
*/
|
||||
export interface CalendarHeadCellProps extends PrimitiveProps {
|
||||
/** The day this header cell represents — used for `aria-label`. */
|
||||
day?: Date;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* Layout container for the calendar's top bar. Holds the `CalendarPrev`,
|
||||
* `CalendarHeading`, and `CalendarNext` controls above the month grid(s).
|
||||
*/
|
||||
export interface CalendarHeaderProps extends PrimitiveProps {}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* Displays the currently visible month and year (e.g. "June 2026"), or a range
|
||||
* when multiple months are shown. Marked `aria-hidden` since the grid already
|
||||
* carries the full accessible label; expose the value via its default slot to
|
||||
* customize the rendering.
|
||||
*/
|
||||
export interface CalendarHeadingProps extends PrimitiveProps {}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* Button that pages the calendar forward (by one month, or by
|
||||
* `numberOfMonths` when paged navigation is enabled). Auto-disables when the
|
||||
* next page would fall after `maxValue` or the calendar is disabled.
|
||||
*/
|
||||
export interface CalendarNextProps extends PrimitiveProps {
|
||||
/** Override the root's `nextPage` for just this button. */
|
||||
nextPage?: (placeholder: Date) => Date;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
|
||||
/**
|
||||
* Button that pages the calendar backward (by one month, or by
|
||||
* `numberOfMonths` when paged navigation is enabled). Auto-disables when the
|
||||
* previous page would fall before `minValue` or the calendar is disabled.
|
||||
*/
|
||||
export interface CalendarPrevProps extends PrimitiveProps {
|
||||
/** Override the root's `prevPage` for just this button. */
|
||||
prevPage?: (placeholder: Date) => Date;
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
import type { PrimitiveProps } from '../primitive';
|
||||
import type { CalendarMonth, WeekDayFormat } from './utils';
|
||||
|
||||
/**
|
||||
* A fully accessible, headless date calendar for picking a single day. The
|
||||
* root owns the selected value and the displayed month ("placeholder"), builds
|
||||
* the localized month grid(s), and wires up roving keyboard navigation,
|
||||
* min/max bounds, and disabled/unavailable predicates. Use it to build an
|
||||
* inline date picker or as the body of a popover/`DatePicker`.
|
||||
*
|
||||
* Compose it with `CalendarHeader` (`CalendarPrev` / `CalendarHeading` /
|
||||
* `CalendarNext`) and one `CalendarGrid` per month. Supports `v-model` for the
|
||||
* selected date and `v-model:placeholder` for the visible month.
|
||||
*/
|
||||
export interface CalendarRootProps extends PrimitiveProps {
|
||||
/** Uncontrolled default selected date. */
|
||||
defaultValue?: Date;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
CalendarCell,
|
||||
CalendarCellTrigger,
|
||||
CalendarGrid,
|
||||
CalendarGridBody,
|
||||
CalendarGridHead,
|
||||
CalendarGridRow,
|
||||
CalendarHeadCell,
|
||||
CalendarHeader,
|
||||
CalendarHeading,
|
||||
CalendarNext,
|
||||
CalendarPrev,
|
||||
CalendarRoot,
|
||||
} from '@robonen/primitives';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const value = ref<Date>(new Date());
|
||||
|
||||
function formatSelected(date: Date | undefined) {
|
||||
if (!date) return 'None';
|
||||
return date.toLocaleDateString('en', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full max-w-sm flex-col gap-3">
|
||||
<CalendarRoot
|
||||
v-slot="{ grid, weekDays }"
|
||||
v-model="value"
|
||||
class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 text-(--fg) shadow-sm"
|
||||
>
|
||||
<CalendarHeader class="mb-3 flex items-center justify-between gap-2">
|
||||
<CalendarPrev
|
||||
aria-label="Previous month"
|
||||
class="inline-flex size-8 items-center justify-center rounded-lg border border-(--border) bg-(--bg) text-(--fg-muted) transition hover:bg-(--bg-inset) hover:text-(--fg) active:scale-95 cursor-pointer disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
‹
|
||||
</CalendarPrev>
|
||||
<CalendarHeading class="text-sm font-semibold tracking-tight" />
|
||||
<CalendarNext
|
||||
aria-label="Next month"
|
||||
class="inline-flex size-8 items-center justify-center rounded-lg border border-(--border) bg-(--bg) text-(--fg-muted) transition hover:bg-(--bg-inset) hover:text-(--fg) active:scale-95 cursor-pointer disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
›
|
||||
</CalendarNext>
|
||||
</CalendarHeader>
|
||||
|
||||
<CalendarGrid
|
||||
v-for="month in grid"
|
||||
:key="month.value.toString()"
|
||||
:month="month.value"
|
||||
class="w-full border-collapse select-none"
|
||||
>
|
||||
<CalendarGridHead>
|
||||
<CalendarGridRow class="mb-1 flex">
|
||||
<CalendarHeadCell
|
||||
v-for="(weekday, i) in weekDays"
|
||||
:key="weekday + i"
|
||||
class="w-9 text-center text-xs font-medium text-(--fg-subtle)"
|
||||
>
|
||||
{{ weekday }}
|
||||
</CalendarHeadCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridHead>
|
||||
|
||||
<CalendarGridBody>
|
||||
<CalendarGridRow
|
||||
v-for="(week, w) in month.weeks"
|
||||
:key="w"
|
||||
class="flex w-full"
|
||||
>
|
||||
<CalendarCell
|
||||
v-for="day in week"
|
||||
:key="day.toString()"
|
||||
:date="day"
|
||||
class="p-0.5"
|
||||
>
|
||||
<CalendarCellTrigger
|
||||
v-slot="{ dayValue, selected, today }"
|
||||
:day="day"
|
||||
:month="month.value"
|
||||
class="flex size-8 items-center justify-center rounded-lg text-sm tabular-nums transition outline-none cursor-pointer
|
||||
focus-visible:ring-2 focus-visible:ring-(--ring)
|
||||
hover:bg-(--bg-inset)
|
||||
data-[selected]:bg-(--accent) data-[selected]:font-semibold data-[selected]:text-(--accent-fg) data-[selected]:hover:bg-(--accent-hover)
|
||||
data-[outside-view]:text-(--fg-subtle) data-[outside-view]:opacity-50
|
||||
data-[unavailable]:cursor-not-allowed data-[unavailable]:text-red-500 data-[unavailable]:line-through data-[unavailable]:hover:bg-transparent
|
||||
data-[disabled]:cursor-not-allowed data-[disabled]:opacity-30"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
today && !selected ? 'relative after:absolute after:bottom-1 after:left-1/2 after:size-1 after:-translate-x-1/2 after:rounded-full after:bg-(--accent)' : '',
|
||||
]"
|
||||
>
|
||||
{{ dayValue }}
|
||||
</span>
|
||||
</CalendarCellTrigger>
|
||||
</CalendarCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridBody>
|
||||
</CalendarGrid>
|
||||
</CalendarRoot>
|
||||
|
||||
<p class="text-xs text-(--fg-muted)">
|
||||
Selected:
|
||||
<span class="font-medium text-(--fg)">{{ formatSelected(value) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user