Skip to content
34 changes: 20 additions & 14 deletions playground/Coalesce.Web.Vue3/src/components/test.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,48 +46,54 @@
step="10"
v-model="date"
clearable
/>

<c-datetime-picker
showTodayButton
/>

<c-datetime-picker
label="EST"
density="compact"
variant="outlined"
v-model="date"
timeZone="America/New_York"
/>
</v-form>

<c-datetime-picker
showTodayButton
/>
</v-form>

<c-datetime-picker
label="Time"
density="compact"
variant="outlined"
date-kind="time"
v-model="date"
/>
<c-datetime-picker
showTodayButton
/>
<c-datetime-picker
label="DateTime"
density="compact"
variant="outlined"
date-kind="datetime"
v-model="date"
clearable
/>
<c-datetime-picker
showTodayButton
/>
<c-datetime-picker
label="Date"
density="compact"
variant="outlined"
date-kind="date"
v-model="date"
/>
<c-datetime-picker
showTodayButton
/>
<c-datetime-picker
label="DateTime Native"
native
density="compact"
variant="outlined"
date-kind="datetime"
v-model="date"
showTodayButton
/>
<c-input :model="caseVm" for="openedAt" variant="outlined"> </c-input>
<c-input :model="caseVm" for="openedAt" variant="outlined" showTodayButton> </c-input>

<br />
{{ date }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<v-text-field
v-else
class="c-datetime-picker"
:class="{ 'today-btn': showTodayButton }"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unusual class name. The entire component is not a today-btn. Only the button itself inside the component's menu would make sense to have a today-btn class.

Consider a more appropriate class name like c-datetime-picker__menu--has-today-btn on the menu's contentClass, which also adheres to the (mildly consistent) use of BEM selectors in this project.

v-bind="inputBindAttrs"
:rules="effectiveRules"
:modelValue="internalTextValue == null ? displayedValue : internalTextValue"
Expand Down Expand Up @@ -76,6 +77,12 @@
:max="max ? endOfDay(max) : undefined"
v-bind="datePickerProps"
>
<template v-slot:actions v-if="showTodayButton">
<v-btn @click="setToday" :disabled="!isDateAllowed(new Date())">
Today
</v-btn>
<v-spacer />
</template>
</v-date-picker>

<v-divider vertical></v-divider>
Expand Down Expand Up @@ -108,6 +115,11 @@
}
}
}

.today-btn .c-time-picker__column {
max-height: 365px;
}

.v-date-picker {
width: 300px;
overflow-y: auto;
Expand Down Expand Up @@ -225,6 +237,9 @@ const props = withDefaults(
allowedDates?: null | Date[] | ((date: Date) => boolean);
// Object containing extra props to pass through to `v-date-picker`.
datePickerProps?: any;
/** Determines whether the 'Today' button is displayed in the date picker actions.
* When enabled, the 'Today' button allows users to quickly select the current date. */
showTodayButton?: boolean;
}>(),
{ closeOnDatePicked: null }
);
Expand Down Expand Up @@ -535,25 +550,7 @@ function dateChanged(input: unknown) {

function emitInput(value: Date | null) {
if (value) {
if (props.allowedDates) {
// With validation of allowedDates, we have to just return early without emitting
// since there's no logic we can apply to clamp the date to a valid date.

if (
Array.isArray(props.allowedDates) &&
!props.allowedDates.includes(startOfDay(value))
) {
error.value.push("The selected date is not allowed.");
return;
} else if (
typeof props.allowedDates == "function" &&
!props.allowedDates(value)
) {
error.value.push("The selected date is not allowed.");
return;
}
}

// Clamp value within min and max bounds
if (props.min && value.valueOf() < props.min.valueOf()) {
value = props.min;
}
Expand All @@ -562,6 +559,13 @@ function emitInput(value: Date | null) {
value = props.max;
}

if (!isDateAllowed(value)) {
// With validation of allowedDates, we have to just return early without emitting
// since there's no logic we can apply to clamp the date to a valid date.
error.value.push("The selected date is not allowed.");
return;
}

if (props.step) {
const stepMs = props.step * 60 * 1000;
let newTime = Math.round(value.valueOf() / stepMs) * stepMs;
Expand All @@ -584,6 +588,39 @@ function emitInput(value: Date | null) {
}
}

function isDateAllowed(date: Date) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should just be axed and if you click 'today' when there is a min/max value and today doesn't fall within it we just clamp to be within the bounds?

if (!date) return false;

const minDate = props.min ? startOfDay(props.min) : null;
const maxDate = props.max ? endOfDay(props.max) : null;

// Check min and max constraints
if (minDate && date < minDate) return false;
if (maxDate && date > maxDate) return false;

// Check allowedDates array or function
if (props.allowedDates) {
if (
Array.isArray(props.allowedDates) &&
!props.allowedDates.some(
(allowedDate) =>
startOfDay(allowedDate).getTime() === startOfDay(date).getTime()
)
) {
return false;
}
if (typeof props.allowedDates === "function" && !props.allowedDates(date)) {
return false;
}
}

return true;
}

function setToday() {
dateChanged(new Date());
}

function close() {
menu.value = false;
}
Expand Down