diff --git a/packages/api-generator/src/locale/en/VDatePickerMonth.json b/packages/api-generator/src/locale/en/VDatePickerMonth.json new file mode 100644 index 00000000000..e7697b78d2b --- /dev/null +++ b/packages/api-generator/src/locale/en/VDatePickerMonth.json @@ -0,0 +1,7 @@ +{ + "props": { + "hideWeekdays": "Hide the days of the week letters.", + "transition": "The transition used when changing months into the future", + "reverseTransition": "The transition used when changing months into the past" + } +} diff --git a/packages/api-generator/src/locale/en/VPicker.json b/packages/api-generator/src/locale/en/VPicker.json new file mode 100644 index 00000000000..0e720d6074f --- /dev/null +++ b/packages/api-generator/src/locale/en/VPicker.json @@ -0,0 +1,6 @@ +{ + "props": { + "landscape": "Puts the picker into landscape mode.", + "hideHeader": "Hide the picker header." + } +} diff --git a/packages/api-generator/src/locale/en/calendar.json b/packages/api-generator/src/locale/en/calendar.json new file mode 100644 index 00000000000..e96908554f0 --- /dev/null +++ b/packages/api-generator/src/locale/en/calendar.json @@ -0,0 +1,9 @@ +{ + "props": { + "displayValue": "The value that determines the month to show. This is different from modelValue, which determines the selected value.", + "month": "The current month number to show", + "weekdays": "An array of weekdays to display.", + "weeksInMonth": "A dynamic number of weeks in a month will grow and shrink depending on how many days are in the month. A static number always shows 7 weeks.", + "year": "The current year number to show" + } +} diff --git a/packages/docs/src/data/nav.json b/packages/docs/src/data/nav.json index 097e219856a..9666b28d020 100644 --- a/packages/docs/src/data/nav.json +++ b/packages/docs/src/data/nav.json @@ -231,6 +231,10 @@ "title": "confirm-edit", "subfolder": "components" }, + { + "title": "date-inputs", + "subfolder": "components" + }, { "title": "empty-states", "subfolder": "components" diff --git a/packages/docs/src/data/page-to-api.json b/packages/docs/src/data/page-to-api.json index be62a3cf190..49014d5d39a 100644 --- a/packages/docs/src/data/page-to-api.json +++ b/packages/docs/src/data/page-to-api.json @@ -69,8 +69,9 @@ "VDataTableServer", "VDataTableVirtual" ], + "components/date-inputs": ["VDateInput", "VDatePicker"], "components/date-pickers-month": ["VDatePicker"], - "components/date-pickers": ["VDatePicker"], + "components/date-pickers": ["VDatePicker", "VDateInput"], "components/defaults-providers": ["VDefaultsProvider"], "components/dialogs": ["VDialog", "VOverlay"], "components/dividers": ["VDivider"], diff --git a/packages/docs/src/examples/v-date-input/misc-passenger.vue b/packages/docs/src/examples/v-date-input/misc-passenger.vue new file mode 100644 index 00000000000..546a5207657 --- /dev/null +++ b/packages/docs/src/examples/v-date-input/misc-passenger.vue @@ -0,0 +1,53 @@ + diff --git a/packages/docs/src/examples/v-date-input/prop-model.vue b/packages/docs/src/examples/v-date-input/prop-model.vue new file mode 100644 index 00000000000..5123cc86bf6 --- /dev/null +++ b/packages/docs/src/examples/v-date-input/prop-model.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/packages/docs/src/examples/v-date-input/prop-multiple-range.vue b/packages/docs/src/examples/v-date-input/prop-multiple-range.vue new file mode 100644 index 00000000000..db613aabd24 --- /dev/null +++ b/packages/docs/src/examples/v-date-input/prop-multiple-range.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/packages/docs/src/examples/v-date-input/prop-multiple.vue b/packages/docs/src/examples/v-date-input/prop-multiple.vue new file mode 100644 index 00000000000..15202ef1eeb --- /dev/null +++ b/packages/docs/src/examples/v-date-input/prop-multiple.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/packages/docs/src/examples/v-date-input/prop-prepend-icon.vue b/packages/docs/src/examples/v-date-input/prop-prepend-icon.vue new file mode 100644 index 00000000000..427c4be6487 --- /dev/null +++ b/packages/docs/src/examples/v-date-input/prop-prepend-icon.vue @@ -0,0 +1,22 @@ + diff --git a/packages/docs/src/examples/v-date-input/usage.vue b/packages/docs/src/examples/v-date-input/usage.vue new file mode 100644 index 00000000000..93e6dfdd1a5 --- /dev/null +++ b/packages/docs/src/examples/v-date-input/usage.vue @@ -0,0 +1,44 @@ + + + diff --git a/packages/docs/src/pages/en/components/date-inputs.md b/packages/docs/src/pages/en/components/date-inputs.md new file mode 100644 index 00000000000..0bf278bad86 --- /dev/null +++ b/packages/docs/src/pages/en/components/date-inputs.md @@ -0,0 +1,102 @@ +--- +emphasized: true +meta: + nav: Date inputs + title: Date input component + description: The date input is a specialized input that provides a clean interface for selecting dates, showing detailed selection information. + keywords: date input, date picker, date field +related: + - /components/date-pickers/ + - /components/text-fields/ + - /components/menus/ +features: + label: 'C: VDateInput' + report: true + github: /labs/VDateInput/ +--- + +# Date inputs + +The `v-date-input` component combines a text field with a date picker. It is meant to be a direct replacement for a standard date input. + + + +::: warning + +This feature requires [v3.6.0](/getting-started/release-notes/?version=v3.6.0) + +::: + +## Installation + +Labs components require a manual import and installation of the component. + +```js { resource="src/plugins/vuetify.js" } +import { VDateInput } from 'vuetify/labs/VDateInput' + +export default createVuetify({ + components: { + VDateInput, + }, +}) +``` + +## Usage + +At its core, the `v-date-input` component is a basic container that extends [v-text-field](/components/text-fields). + + + + + +## API + +| Component | Description | +| - | - | +| [v-date-input](/api/v-date-input/) | Primary component | +| [v-date-picker](/api/v-date-picker/) | Date picker component | +| [v-text-field](/api/v-text-field/) | Text field component | + + + +## Guide + +The `v-date-input` component is a replacement for the standard date input. It provides a clean interface for selecting dates and shows detailed selection information. + +### Props + +The `v-date-input` component extends the [v-text-field](/components/text-fields/) and [v-date-picker](/components/date-pickers/) component; and supports all of their props. + +#### Model + +The default model value is a Date object, but is displayed as formatted text in the input.. + + + +#### Multiple + +Using the **multiple** prop, the default model value is an empty array. + + + +#### Range + +Using the multiple prop with a value of **range**, select 2 dates to select them and all the dates between them. + + + +#### Calendar icon + +You can move the calendar icon within the input or entirely by utilizing the **prepend-icon** and **prepend-inner-icon** properties. + + + +## Examples + +The following are a collection of examples that demonstrate more advanced and real world use of the `v-date-input` component. + +### Passenger + +In this example, the `v-date-input` component is used to select a date of birth. + + diff --git a/packages/docs/src/pages/en/labs/introduction.md b/packages/docs/src/pages/en/labs/introduction.md index 532e8cac8eb..9588cd6a8fb 100644 --- a/packages/docs/src/pages/en/labs/introduction.md +++ b/packages/docs/src/pages/en/labs/introduction.md @@ -78,6 +78,7 @@ The following is a list of available and up-and-coming components for use with L | - | - | - | | [v-calendar](/components/calendars/) | A calendar component | [v3.4.9](/getting-started/release-notes/?version=v3.4.9) | | [v-confirm-edit](/components/confirm-edit/) | A component for confirming model changes | [v3.4.0](/getting-started/release-notes/?version=v3.4.0) | +| [v-date-input](/components/date-inputs/) | A date input component | [v3.6.0](/getting-started/release-notes/?version=v3.6.0) | | [v-empty-state](/components/empty-states/) | A component for displaying empty states | [v3.5.7](/getting-started/release-notes/?version=v3.5.7) | | [v-fab](/components/floating-action-buttons/) | A layout aware [v-btn](/components/buttons/) | [v3.5.7](/getting-started/release-notes/?version=v3.5.7) | | [v-number-input](/components/number-input/) | A component for numerical data | [v3.5.10](/getting-started/release-notes/?version=v3.5.10) | diff --git a/packages/vuetify/src/components/VDataTable/VDataTableHeaders.tsx b/packages/vuetify/src/components/VDataTable/VDataTableHeaders.tsx index f0d6abb75c4..0a3b5eb9e54 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableHeaders.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableHeaders.tsx @@ -224,9 +224,7 @@ export const VDataTableHeaders = genericComponent()({ name="v-data-table-progress" absolute active - color={ typeof props.loading === 'boolean' || props.loading === 'true' - ? props.color - : props.loading } + color={ typeof props.loading === 'boolean' ? props.color : props.loading } indeterminate v-slots={{ default: slots.loader }} /> diff --git a/packages/vuetify/src/labs/VDateInput/VDateInput.tsx b/packages/vuetify/src/labs/VDateInput/VDateInput.tsx new file mode 100644 index 00000000000..678a7937285 --- /dev/null +++ b/packages/vuetify/src/labs/VDateInput/VDateInput.tsx @@ -0,0 +1,161 @@ +// Components +import { makeVDatePickerProps, VDatePicker } from '@/components/VDatePicker/VDatePicker' +import { VMenu } from '@/components/VMenu/VMenu' +import { makeVTextFieldProps, VTextField } from '@/components/VTextField/VTextField' +import { makeVConfirmEditProps, VConfirmEdit } from '@/labs/VConfirmEdit/VConfirmEdit' + +// Composables +import { useDate } from '@/composables/date' +import { makeFocusProps, useFocus } from '@/composables/focus' +import { useLocale } from '@/composables/locale' +import { useProxiedModel } from '@/composables/proxiedModel' + +// Utilities +import { computed, shallowRef } from 'vue' +import { genericComponent, omit, propsFactory, useRender, wrapInArray } from '@/util' + +// Types +export interface VDateInputSlots { + default: never +} + +export const makeVDateInputProps = propsFactory({ + hideActions: Boolean, + + ...makeFocusProps(), + ...makeVConfirmEditProps(), + ...makeVTextFieldProps({ + placeholder: 'mm/dd/yyyy', + prependIcon: '$calendar', + }), + ...omit(makeVDatePickerProps({ + weeksInMonth: 'dynamic' as const, + hideHeader: true, + }), ['active']), +}, 'VDateInput') + +export const VDateInput = genericComponent()({ + name: 'VDateInput', + + props: makeVDateInputProps(), + + emits: { + 'update:modelValue': (val: string) => true, + }, + + setup (props, { slots }) { + const { t } = useLocale() + const adapter = useDate() + const { isFocused, focus, blur } = useFocus(props) + const model = useProxiedModel(props, 'modelValue', props.multiple ? [] : null) + const menu = shallowRef(false) + + const display = computed(() => { + const value = wrapInArray(model.value) + + if (!value.length) return null + + if (props.multiple === true) { + return t('$vuetify.datePicker.itemsSelected', value.length) + } + + if (props.multiple === 'range') { + const start = value[0] + const end = value[value.length - 1] + + return adapter.isValid(start) && adapter.isValid(end) + ? `${adapter.format(start, 'keyboardDate')} - ${adapter.format(end, 'keyboardDate')}` + : '' + } + + return adapter.isValid(model.value) ? adapter.format(model.value, 'keyboardDate') : '' + }) + + function onKeydown (e: KeyboardEvent) { + if (e.key !== 'Enter') return + + if (!menu.value || !isFocused.value) { + menu.value = true + + return + } + + const target = e.target as HTMLInputElement + + model.value = adapter.date(target.value) + } + + function onClick (e: MouseEvent) { + e.preventDefault() + e.stopPropagation() + + menu.value = true + } + + function onSave () { + menu.value = false + } + + useRender(() => { + const confirmEditProps = VConfirmEdit.filterProps(props) + const datePickerProps = VDatePicker.filterProps(omit(props, ['active'])) + const textFieldProps = VTextField.filterProps(props) + + return ( + + + + {{ + default: ({ actions, model: proxyModel }) => { + return ( + { + if (!props.hideActions) { + proxyModel.value = val + } else { + model.value = val + + if (!props.multiple) menu.value = false + } + }} + onMousedown={ (e: MouseEvent) => e.preventDefault() } + > + {{ + actions: !props.hideActions ? () => actions : undefined, + }} + + ) + }, + }} + + + + { slots.default?.() } + + ) + }) + }, +}) + +export type VDateInput = InstanceType diff --git a/packages/vuetify/src/labs/VDateInput/index.ts b/packages/vuetify/src/labs/VDateInput/index.ts new file mode 100644 index 00000000000..73ccf785b24 --- /dev/null +++ b/packages/vuetify/src/labs/VDateInput/index.ts @@ -0,0 +1 @@ +export { VDateInput } from './VDateInput' diff --git a/packages/vuetify/src/labs/components.ts b/packages/vuetify/src/labs/components.ts index 037336bc275..41c5a37956e 100644 --- a/packages/vuetify/src/labs/components.ts +++ b/packages/vuetify/src/labs/components.ts @@ -1,5 +1,6 @@ export * from './VCalendar' export * from './VConfirmEdit' +export * from './VDateInput' export * from './VEmptyState' export * from './VFab' export * from './VNumberInput'