Variable: RDateField
ts
const RDateField: DefineComponent<ExtractPropTypes<AppendDefault<{
active: BooleanConstructor;
allowUnspecifiedChoices: {
default: boolean;
type: BooleanConstructor;
};
autocomplete: PropType<string>;
autofocus: BooleanConstructor;
baseColor: StringConstructor;
bgColor: StringConstructor;
choices: {
default: undefined;
type: PropType<any[] | undefined>;
};
class: PropType<any>;
clearable: BooleanConstructor;
clearIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<
| IconValue
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>>;
};
color: StringConstructor;
datePickerIcon: {
default: () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<IconValue>;
};
density: {
default: string;
type: PropType<Density>;
validator: (v: any) => boolean;
};
direction: {
default: string;
type: PropType<"vertical" | "horizontal">;
validator: (v: any) => boolean;
};
dirty: BooleanConstructor;
disabled: {
default: null;
type: BooleanConstructor;
};
displayFormat: {
default: string;
type: PropType<"short" | "med" | "medWithWeekday" | "full" | "huge">;
};
displayLocale: {
default: undefined;
type: PropType<string | undefined>;
};
displayTimezone: {
default: undefined;
type: PropType<string | Zone<boolean> | undefined>;
};
error: BooleanConstructor;
errorMessages: {
default: () => never[];
type: PropType<string | readonly string[] | null>;
};
firstDayOfWeek: {
default: undefined;
type: (NumberConstructor | StringConstructor)[];
};
flat: BooleanConstructor;
focused: BooleanConstructor;
glow: BooleanConstructor;
hideDetails: PropType<boolean | "auto">;
hint: StringConstructor;
href: StringConstructor;
iconColor: (StringConstructor | BooleanConstructor)[];
id: StringConstructor;
label: {
default: undefined;
type: PropType<string | undefined>;
};
loading: (StringConstructor | BooleanConstructor)[];
max: PropType<unknown>;
maxErrors: {
default: number;
type: (NumberConstructor | StringConstructor)[];
};
maxWidth: (NumberConstructor | StringConstructor)[];
menuIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<
| IconValue
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>>;
};
messages: {
default: () => never[];
type: PropType<string | readonly string[]>;
};
min: PropType<unknown>;
minWidth: (NumberConstructor | StringConstructor)[];
modeIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<
| IconValue
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>>;
};
modelValue: {
default: undefined;
type: PropType<string | string[] | null | undefined>;
};
multiple: {
default: boolean;
type: PropType<boolean>;
};
name: StringConstructor;
nextIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<
| IconValue
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>>;
};
noValuesText: {
default: undefined;
type: PropType<string | undefined>;
};
onBlur: {
default: undefined;
type: PropType<() => void | undefined>;
};
onChange: {
default: undefined;
type: PropType<() => void | undefined>;
};
onClick:append?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:appendInner?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:clear?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:control?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:prepend?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:prependInner?: PropType<(...args: [MouseEvent]) => void | undefined>;
onInput: {
default: undefined;
type: PropType<() => void | undefined>;
};
onMousedown:control?: PropType<(...args: [MouseEvent]) => void | undefined>;
onUpdate:focused?: PropType<(...args: [boolean]) => void | undefined>;
onUpdate:model-value?: PropType<(...args: [string | ...[] | null]) => void | undefined>;
onUpdate:modelValue?: PropType<(...args: [string | ...[] | null]) => void | undefined>;
persistentClear: BooleanConstructor;
persistentHint: BooleanConstructor;
persistentPlaceholder: BooleanConstructor;
placeholder: StringConstructor;
prefix: StringConstructor;
prevIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<
| IconValue
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>>;
};
readonly: {
default: null;
type: PropType<boolean | null>;
};
renderMode: {
default: string;
type: PropType<"edit" | "read">;
};
reverse: BooleanConstructor;
role: {
default: string;
type: PropType<string>;
};
rounded: {
default: undefined;
type: (NumberConstructor | StringConstructor | BooleanConstructor)[];
};
showWeek: BooleanConstructor;
singleLine: BooleanConstructor;
style: {
default: null;
type: PropType<StyleValue>;
};
suffix: StringConstructor;
theme: StringConstructor;
tile: BooleanConstructor;
to: PropType<
| string
| RouteLocationAsRelativeGeneric
| RouteLocationAsPathGeneric>;
validateOn: PropType<
| "eager"
| "input"
| "blur"
| "submit"
| "invalid-input"
| "input lazy"
| "blur lazy"
| "submit lazy"
| "invalid-input lazy"
| "input eager"
| "blur eager"
| "submit eager"
| "invalid-input eager"
| "lazy input"
| "lazy blur"
| "lazy submit"
| "lazy invalid-input"
| "eager input"
| "eager blur"
| "eager submit"
| "eager invalid-input"
| "lazy"
| undefined>;
variant: {
default: string;
type: PropType<
| "outlined"
| "plain"
| "underlined"
| "filled"
| "solo"
| "solo-inverted"
| "solo-filled">;
validator: (v: any) => boolean;
};
width: (NumberConstructor | StringConstructor)[];
}, {
}>>, () =>
| VNode<RendererNode, RendererElement, {
[key: string]: any;
}>
| null, {
}, {
}, {
}, ComponentOptionsMixin, ComponentOptionsMixin, EmitFunctions<{
click:append: (...args: [MouseEvent]) => boolean;
click:appendInner: (...args: [MouseEvent]) => boolean;
click:clear: (...args: [MouseEvent]) => boolean;
click:control: (...args: [MouseEvent]) => boolean;
click:prepend: (...args: [MouseEvent]) => boolean;
click:prependInner: (...args: [MouseEvent]) => boolean;
mousedown:control: (...args: [MouseEvent]) => boolean;
update:focused: (...args: [boolean]) => boolean;
update:model-value: (value: string | string[] | null | undefined) => boolean;
update:modelValue: (value: string | string[] | null | undefined) => boolean;
}>, string, PublicProps, ToResolvedProps<ExtractPropTypes<AppendDefault<{
active: BooleanConstructor;
allowUnspecifiedChoices: {
default: boolean;
type: BooleanConstructor;
};
autocomplete: PropType<string>;
autofocus: BooleanConstructor;
baseColor: StringConstructor;
bgColor: StringConstructor;
choices: {
default: undefined;
type: PropType<any[] | undefined>;
};
class: PropType<any>;
clearable: BooleanConstructor;
clearIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>;
type: PropType<
| IconValue
| () => VNode<..., ..., ...>>;
};
color: StringConstructor;
datePickerIcon: {
default: () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
type: PropType<IconValue>;
};
density: {
default: string;
type: PropType<Density>;
validator: (v: any) => boolean;
};
direction: {
default: string;
type: PropType<"vertical" | "horizontal">;
validator: (v: any) => boolean;
};
dirty: BooleanConstructor;
disabled: {
default: null;
type: BooleanConstructor;
};
displayFormat: {
default: string;
type: PropType<"short" | "med" | "medWithWeekday" | "full" | "huge">;
};
displayLocale: {
default: undefined;
type: PropType<string | undefined>;
};
displayTimezone: {
default: undefined;
type: PropType<string | Zone<boolean> | undefined>;
};
error: BooleanConstructor;
errorMessages: {
default: () => never[];
type: PropType<string | readonly string[] | null>;
};
firstDayOfWeek: {
default: undefined;
type: (NumberConstructor | StringConstructor)[];
};
flat: BooleanConstructor;
focused: BooleanConstructor;
glow: BooleanConstructor;
hideDetails: PropType<boolean | "auto">;
hint: StringConstructor;
href: StringConstructor;
iconColor: (StringConstructor | BooleanConstructor)[];
id: StringConstructor;
label: {
default: undefined;
type: PropType<string | undefined>;
};
loading: (StringConstructor | BooleanConstructor)[];
max: PropType<unknown>;
maxErrors: {
default: number;
type: (NumberConstructor | StringConstructor)[];
};
maxWidth: (NumberConstructor | StringConstructor)[];
menuIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>;
type: PropType<
| IconValue
| () => VNode<..., ..., ...>>;
};
messages: {
default: () => never[];
type: PropType<string | readonly string[]>;
};
min: PropType<unknown>;
minWidth: (NumberConstructor | StringConstructor)[];
modeIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>;
type: PropType<
| IconValue
| () => VNode<..., ..., ...>>;
};
modelValue: {
default: undefined;
type: PropType<string | string[] | null | undefined>;
};
multiple: {
default: boolean;
type: PropType<boolean>;
};
name: StringConstructor;
nextIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>;
type: PropType<
| IconValue
| () => VNode<..., ..., ...>>;
};
noValuesText: {
default: undefined;
type: PropType<string | undefined>;
};
onBlur: {
default: undefined;
type: PropType<() => void | undefined>;
};
onChange: {
default: undefined;
type: PropType<() => void | undefined>;
};
onClick:append?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:appendInner?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:clear?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:control?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:prepend?: PropType<(...args: [MouseEvent]) => void | undefined>;
onClick:prependInner?: PropType<(...args: [MouseEvent]) => void | undefined>;
onInput: {
default: undefined;
type: PropType<() => void | undefined>;
};
onMousedown:control?: PropType<(...args: [MouseEvent]) => void | undefined>;
onUpdate:focused?: PropType<(...args: [boolean]) => void | undefined>;
onUpdate:model-value?: PropType<(...args: [... | ... | ... | ...]) => void | undefined>;
onUpdate:modelValue?: PropType<(...args: [... | ... | ... | ...]) => void | undefined>;
persistentClear: BooleanConstructor;
persistentHint: BooleanConstructor;
persistentPlaceholder: BooleanConstructor;
placeholder: StringConstructor;
prefix: StringConstructor;
prevIcon: Omit<{
default: string;
type: PropType<IconValue>;
}, "type" | "default"> & {
default: | NonNullable<IconValue>
| () => VNode<RendererNode, RendererElement, {
[key: ...]: ...;
}>;
type: PropType<
| IconValue
| () => VNode<..., ..., ...>>;
};
readonly: {
default: null;
type: PropType<boolean | null>;
};
renderMode: {
default: string;
type: PropType<"edit" | "read">;
};
reverse: BooleanConstructor;
role: {
default: string;
type: PropType<string>;
};
rounded: {
default: undefined;
type: (NumberConstructor | StringConstructor | BooleanConstructor)[];
};
showWeek: BooleanConstructor;
singleLine: BooleanConstructor;
style: {
default: null;
type: PropType<StyleValue>;
};
suffix: StringConstructor;
theme: StringConstructor;
tile: BooleanConstructor;
to: PropType<
| string
| RouteLocationAsRelativeGeneric
| RouteLocationAsPathGeneric>;
validateOn: PropType<
| "eager"
| "input"
| "blur"
| "submit"
| "invalid-input"
| "input lazy"
| "blur lazy"
| "submit lazy"
| "invalid-input lazy"
| "input eager"
| "blur eager"
| "submit eager"
| "invalid-input eager"
| "lazy input"
| "lazy blur"
| "lazy submit"
| "lazy invalid-input"
| "eager input"
| "eager blur"
| "eager submit"
| "eager invalid-input"
| "lazy"
| undefined>;
variant: {
default: string;
type: PropType<
| "outlined"
| "plain"
| "underlined"
| "filled"
| "solo"
| "solo-inverted"
| "solo-filled">;
validator: (v: any) => boolean;
};
width: (NumberConstructor | StringConstructor)[];
}, {
}>>, EmitFunctions<{
click:append: (...args: [MouseEvent]) => boolean;
click:appendInner: (...args: [MouseEvent]) => boolean;
click:clear: (...args: [MouseEvent]) => boolean;
click:control: (...args: [MouseEvent]) => boolean;
click:prepend: (...args: [MouseEvent]) => boolean;
click:prependInner: (...args: [MouseEvent]) => boolean;
mousedown:control: (...args: [MouseEvent]) => boolean;
update:focused: (...args: [boolean]) => boolean;
update:model-value: (value: string | string[] | null | undefined) => boolean;
update:modelValue: (value: string | string[] | null | undefined) => boolean;
}>>, {
active: boolean;
allowUnspecifiedChoices: boolean;
autofocus: boolean;
choices: any[] | undefined;
clearable: boolean;
clearIcon: | IconValue
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
datePickerIcon: IconValue;
density: Density;
direction: "vertical" | "horizontal";
dirty: boolean;
disabled: boolean;
displayFormat: "short" | "med" | "medWithWeekday" | "full" | "huge";
displayLocale: string | undefined;
displayTimezone: string | Zone<boolean> | undefined;
error: boolean;
errorMessages: string | readonly string[] | null;
firstDayOfWeek: string | number;
flat: boolean;
focused: boolean;
glow: boolean;
label: string | undefined;
maxErrors: string | number;
menuIcon: | IconValue
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
messages: string | readonly string[];
modeIcon: | IconValue
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
modelValue: string | string[] | null | undefined;
multiple: boolean;
nextIcon: | IconValue
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
noValuesText: string | undefined;
onBlur: () => void | undefined;
onChange: () => void | undefined;
onInput: () => void | undefined;
persistentClear: boolean;
persistentHint: boolean;
persistentPlaceholder: boolean;
prevIcon: | IconValue
| () => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>;
readonly: boolean | null;
renderMode: "edit" | "read";
reverse: boolean;
role: string;
rounded: string | number | boolean;
showWeek: boolean;
singleLine: boolean;
style: StyleValue;
tile: boolean;
variant: | "outlined"
| "plain"
| "underlined"
| "filled"
| "solo"
| "solo-inverted"
| "solo-filled";
}, SlotsType<Partial<{
append: (arg: VInputSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
append-inner: (arg: DefaultInputSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
clear: (arg: DefaultInputSlot & {
props: Record<string, any>;
}) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
counter: (arg: VCounterSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
default: (arg: {
id: Readonly<Ref<string, string>>;
}) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
details: (arg: VInputSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
label: (arg: DefaultInputSlot & {
label: string | undefined;
props: Record<string, any>;
}) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
loader: (arg: LoaderSlotProps) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
message: (arg: VMessageSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
prepend: (arg: VInputSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
prepend-inner: (arg: DefaultInputSlot) => VNode<RendererNode, RendererElement, {
[key: string]: any;
}>[];
}>>, {
}, {
}, string, ComponentProvideOptions, true, {
}, any>;RDateField - A sophisticated Vue component for ISO 8601 date input and selection.
This component provides a comprehensive date input solution that combines the accessibility and validation benefits of HTML5 date inputs with a completely custom visual interface. It features dual input methods (manual text entry and visual picker) with intelligent mobile optimization.
Key Features
- Dual Input Interface: Manual text entry + integrated VDatePicker
- Mobile Optimized: VBottomSheet on mobile, VDialog on desktop
- ISO 8601 Standard: All dates stored as YYYY-MM-DD strings
- Luxon Formatting: Flexible display formatting with internationalization
- Browser UI Suppression: Custom picker while preserving HTML5 benefits
- Multiple Values: Chip-based interface for date arrays
- Smart Readonly: Mobile inputs become readonly to prevent keyboard conflicts
- Validation Pipeline: Ensures valid dates with midnight time components
Technical Architecture
The component uses a sophisticated multi-layer approach:
Base Layer (VTextField)
- HTML5
type="date"for validation and mobile keyboard optimization - Injected CSS suppresses all native browser date picker UI
- Maintains accessibility and semantic benefits
Picker Layer (VDatePicker)
- Integrated via VDialog (desktop) or VBottomSheet (mobile)
- Full calendar navigation with month/year selection
- Configurable date constraints (min, max, firstDayOfWeek)
Mobile Layer (Touch Optimization)
- Input becomes readonly on mobile to prevent keyboard conflicts
- onClick:control handler directly opens picker interface
- VBottomSheet provides optimal touch-based date selection UX
Validation Layer
- Ensures ISO 8601 compliance (YYYY-MM-DD format)
- Validates time components are at midnight (pure dates)
- Luxon-based parsing for robust date validation
Usage Patterns
Example
vue
<template>
<!-- Basic date selection -->
<RDateField v-model="selectedDate" label="Select Date" displayFormat="med" />
<!-- Multiple dates with constraints -->
<RDateField
v-model="eventDates"
multiple
label="Event Dates"
:min="todayISO"
:max="maxDateISO"
displayFormat="medWithWeekday"
/>
<!-- Read-only display -->
<RDateField
v-model="historicalDates"
render-mode="read"
displayFormat="full"
multiple
/>
<!-- Custom picker options -->
<RDateField
v-model="appointmentDate"
label="Appointment"
:firstDayOfWeek="1"
:showWeek="true"
displayFormat="huge"
/>
</template>Data Flow
- Input: User types date or selects from picker
- Validation: Luxon validates ISO format and date validity
- Storage: ISO 8601 string stored in modelValue
- Display: Luxon formats for presentation using displayFormat
- Emission: Validated ISO string emitted to parent
Browser Compatibility
Automatic CSS injection handles native picker suppression across:
- WebKit: Safari, Chrome, Edge (webkit-calendar-picker-indicator)
- Mozilla: Firefox (moz-calendar-picker-indicator)
- Microsoft: Legacy Edge, IE (ms-clear, ms-reveal)
This ensures consistent custom UI while preserving HTML5 input benefits like validation, accessibility, and mobile keyboard optimization.