Skip to content

Commit

Permalink
fix(datepicker): add datepicker conditional typed by prop multiple (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mlmoravek authored Jul 9, 2024
1 parent affbb54 commit 8689a40
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 178 deletions.
2 changes: 1 addition & 1 deletion packages/docs/components/Datepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ title: Datepicker
| mobileNative | Enable mobile native input if mobile agent | boolean | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>datepicker: {<br>&nbsp;&nbsp;mobileNative: false<br>}</code> |
| v-model | The input value state | Date \| Date[] | - | <code style='white-space: nowrap; padding: 0;'>null</code> |
| monthNames | Set custom month names, else use names based on locale | string[] | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>datepicker: {<br>&nbsp;&nbsp;monthNames: undefined<br>}</code> |
| multiple | Same as native, also push new item to v-model instead of replacing | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| multiple | Same as native, also push new item to v-model instead of replacing | boolean | - | <code style='white-space: nowrap; padding: 0;'></code> |
| nearbyMonthDays | Show nearby month days | boolean | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>datepicker: {<br>&nbsp;&nbsp;nearbyMonthDays: true<br>}</code> |
| nearbySelectableMonthDays | Define if nearby month days can be selected | boolean | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>datepicker: {<br>&nbsp;&nbsp;nearbySelectableMonthDays: false<br>}</code> |
| openOnFocus | Open dropdown on focus | boolean | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>datepicker: {<br>&nbsp;&nbsp;openOnFocus: true<br>}</code> |
Expand Down
154 changes: 82 additions & 72 deletions packages/oruga/src/components/datepicker/Datepicker.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<script setup lang="ts" generic="IsRange extends boolean = false">
<script
setup
lang="ts"
generic="
IsRange extends boolean = false,
IsMultiple extends boolean = false
">
import { computed, ref, watch } from "vue";
import OButton from "../button/Button.vue";
Expand Down Expand Up @@ -34,75 +40,79 @@ defineOptions({
configField: "datepicker",
});
const props = withDefaults(defineProps<DatepickerProps<IsRange>>(), {
override: undefined,
modelValue: null,
// range: false,
active: false,
type: "date",
dayNames: () => getOption("datepicker.dayNames", undefined),
monthNames: () => getOption("datepicker.monthNames", undefined),
size: () => getOption("datepicker.size"),
focusedDate: undefined,
events: undefined,
indicators: "dots",
minDate: undefined,
maxDate: undefined,
expanded: false,
rounded: false,
inline: false,
placeholder: undefined,
readonly: false,
multiple: false,
disabled: false,
openOnFocus: () => getOption("datepicker.openOnFocus", true),
closeOnClick: () => getOption("datepicker.closeOnClick", true),
locale: () => getOption("locale"),
dateFormatter: (date) =>
getOption<(date) => string>(
"datepicker.dateFormatter",
() => undefined,
)(date),
dateParser: (date: string) =>
getOption<(date: string) => any>(
"datepicker.dateParser",
() => undefined,
)(date),
dateCreator: () => getOption("datepicker.dateCreator", () => new Date())(),
selectableDates: undefined,
unselectableDates: undefined,
unselectableDaysOfWeek: () =>
getOption("datepicker.unselectableDaysOfWeek", undefined),
nearbyMonthDays: () => getOption("datepicker.nearbyMonthDays", true),
nearbySelectableMonthDays: () =>
getOption("datepicker.nearbySelectableMonthDays", false),
showWeekNumber: () => getOption("datepicker.showWeekNumber", false),
weekNumberClickable: () =>
getOption("datepicker.weekNumberClickable", false),
firstDayOfWeek: () => getOption("datepicker.firstDayOfWeek", 0),
rulesForFirstWeek: 4,
yearsRange: () => getOption("datepicker.yearsRange", [-100, 10]),
trapFocus: () => getOption("datepicker.trapFocus", true),
position: undefined,
mobileModal: () => getOption("datepicker.mobileModal", true),
mobileNative: () => getOption("datepicker.mobileNative", false),
iconPack: () => getOption("datepicker.iconPack", undefined),
icon: () => getOption("datepicker.icon", undefined),
iconRight: () => getOption("datepicker.iconRight", undefined),
iconRightClickable: false,
iconPrev: () => getOption("datepicker.iconPrev", "chevron-left"),
iconNext: () => getOption("datepicker.iconNext", "chevron-right"),
mobileBreakpoint: () => getOption("datepicker.mobileBreakpoint"),
teleport: () => getOption("datepicker.teleport", false),
useHtml5Validation: () => getOption("useHtml5Validation", true),
validationMessage: undefined,
ariaNextLabel: () => getOption("datepicker.ariaNextLabel", "Next Page"),
ariaPreviousLabel: () =>
getOption("datepicker.ariaNextLabel", "Previous Page"),
inputClasses: () => getOption("datepicker.inputClasses", {}),
dropdownClasses: () => getOption("datepicker.dropdownClasses", {}),
selectClasses: () => getOption("datepicker.selectClasses", {}),
});
const props = withDefaults(
defineProps<DatepickerProps<IsRange, IsMultiple>>(),
{
override: undefined,
modelValue: null,
// range: false,
// multiple: false,
active: false,
type: "date",
dayNames: () => getOption("datepicker.dayNames", undefined),
monthNames: () => getOption("datepicker.monthNames", undefined),
size: () => getOption("datepicker.size"),
focusedDate: undefined,
events: undefined,
indicators: "dots",
minDate: undefined,
maxDate: undefined,
expanded: false,
rounded: false,
inline: false,
placeholder: undefined,
readonly: false,
disabled: false,
openOnFocus: () => getOption("datepicker.openOnFocus", true),
closeOnClick: () => getOption("datepicker.closeOnClick", true),
locale: () => getOption("locale"),
dateFormatter: (date) =>
getOption<(date) => string>(
"datepicker.dateFormatter",
() => undefined,
)(date),
dateParser: (date: string) =>
getOption<(date: string) => any>(
"datepicker.dateParser",
() => undefined,
)(date),
dateCreator: () =>
getOption("datepicker.dateCreator", () => new Date())(),
selectableDates: undefined,
unselectableDates: undefined,
unselectableDaysOfWeek: () =>
getOption("datepicker.unselectableDaysOfWeek", undefined),
nearbyMonthDays: () => getOption("datepicker.nearbyMonthDays", true),
nearbySelectableMonthDays: () =>
getOption("datepicker.nearbySelectableMonthDays", false),
showWeekNumber: () => getOption("datepicker.showWeekNumber", false),
weekNumberClickable: () =>
getOption("datepicker.weekNumberClickable", false),
firstDayOfWeek: () => getOption("datepicker.firstDayOfWeek", 0),
rulesForFirstWeek: 4,
yearsRange: () => getOption("datepicker.yearsRange", [-100, 10]),
trapFocus: () => getOption("datepicker.trapFocus", true),
position: undefined,
mobileModal: () => getOption("datepicker.mobileModal", true),
mobileNative: () => getOption("datepicker.mobileNative", false),
iconPack: () => getOption("datepicker.iconPack", undefined),
icon: () => getOption("datepicker.icon", undefined),
iconRight: () => getOption("datepicker.iconRight", undefined),
iconRightClickable: false,
iconPrev: () => getOption("datepicker.iconPrev", "chevron-left"),
iconNext: () => getOption("datepicker.iconNext", "chevron-right"),
mobileBreakpoint: () => getOption("datepicker.mobileBreakpoint"),
teleport: () => getOption("datepicker.teleport", false),
useHtml5Validation: () => getOption("useHtml5Validation", true),
validationMessage: undefined,
ariaNextLabel: () => getOption("datepicker.ariaNextLabel", "Next Page"),
ariaPreviousLabel: () =>
getOption("datepicker.ariaNextLabel", "Previous Page"),
inputClasses: () => getOption("datepicker.inputClasses", {}),
dropdownClasses: () => getOption("datepicker.dropdownClasses", {}),
selectClasses: () => getOption("datepicker.selectClasses", {}),
},
);
type ModelValue = typeof props.modelValue;
Expand Down Expand Up @@ -520,13 +530,13 @@ defineExpose({ focus: () => pickerRef.value?.focus(), value: vmodel });
v-model:active="isActive"
data-oruga="datepicker"
:value="vmodel"
:picker="props"
:picker-props="props"
:formatted-value="formattedValue"
:native-type="!isTypeMonth ? 'date' : 'month'"
:native-value="formatNative(vmodel)"
:native-max="formatNative(maxDate)"
:native-min="formatNative(minDate)"
:stay-open="multiple"
:stay-open="props.multiple"
:dropdown-classes="dropdownClass"
:root-classes="rootClasses"
:box-class="boxClassBind"
Expand Down
46 changes: 28 additions & 18 deletions packages/oruga/src/components/datepicker/DatepickerMonth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type ComponentPublicInstance,
} from "vue";
import { isDefined } from "@/utils/helpers";
import { isDefined, isTrueish } from "@/utils/helpers";
import { defineClasses } from "@/composables";
import type { DatepickerProps, DatepickerEvent, FocusedDate } from "./types";
Expand Down Expand Up @@ -96,7 +96,7 @@ const monthDates = computed(() => {
});
const hoveredDateRange = computed(() => {
if (!datepicker.value.range || !selectedEndDate.value) return [];
if (!isTrueish(datepicker.value.range) || !selectedEndDate.value) return [];
return (
hoveredEndDate.value < selectedBeginDate.value
Expand Down Expand Up @@ -217,14 +217,10 @@ function onKeydown(event: KeyboardEvent, weekDay: Date): void {
*/
function selectDate(date: Date): void {
if (datepicker.value.disabled || datepicker.value.readonly) return;
if (
!datepicker.value.range &&
!datepicker.value.multiple &&
isDateSelectable(date)
)
emits("update:modelValue", date);
else if (datepicker.value.range) handleSelectRangeDate(date);
else if (datepicker.value.multiple) handleSelectMultipleDates(date);
else if (isTrueish(datepicker.value.range)) handleSelectRangeDate(date);
else if (isTrueish(datepicker.value.multiple))
handleSelectMultipleDates(date);
else emits("update:modelValue", date);
}
/*
Expand Down Expand Up @@ -256,7 +252,9 @@ function handleSelectRangeDate(date: Date): void {
}
const multipleSelectedDates = computed(() =>
datepicker.value.multiple && props.modelValue ? props.modelValue : [],
isTrueish(datepicker.value.multiple) && props.modelValue
? props.modelValue
: [],
);
function handleSelectMultipleDates(date: Date): void {
Expand Down Expand Up @@ -293,7 +291,7 @@ function changeFocus(month: Date, inc: number): void {
}
function onRangeHoverEndDate(day: Date): void {
if (datepicker.value.range) hoveredEndDate.value = day;
if (isTrueish(datepicker.value.range)) hoveredEndDate.value = day;
}
// --- Computed Component Classes ---
Expand Down Expand Up @@ -354,12 +352,20 @@ function cellClasses(day: Date): ClassBind[] {
"monthCellSelectedClass",
"o-dpck__month__cell--selected",
null,
dateMatch(day, props.modelValue, datepicker.value.multiple) ||
dateWithin(day, props.modelValue, datepicker.value.multiple) ||
dateMatch(
day,
props.modelValue,
isTrueish(datepicker.value.multiple),
) ||
dateWithin(
day,
props.modelValue,
isTrueish(datepicker.value.multiple),
) ||
dateMultipleSelected(
day,
multipleSelectedDates.value,
datepicker.value.multiple,
isTrueish(datepicker.value.multiple),
),
],
Expand All @@ -370,14 +376,18 @@ function cellClasses(day: Date): ClassBind[] {
dateMatch(
day,
Array.isArray(props.modelValue) && props.modelValue[0],
datepicker.value.multiple,
isTrueish(datepicker.value.multiple),
),
],
[
"monthCellWithinSelectedClass",
"o-dpck__month__cell--within-selected",
null,
dateWithin(day, props.modelValue, datepicker.value.multiple),
dateWithin(
day,
props.modelValue,
isTrueish(datepicker.value.multiple),
),
],
[
"monthCellLastSelectedClass",
Expand All @@ -386,7 +396,7 @@ function cellClasses(day: Date): ClassBind[] {
dateMatch(
day,
Array.isArray(props.modelValue) && props.modelValue[1],
datepicker.value.multiple,
isTrueish(datepicker.value.multiple),
),
],
[
Expand Down
18 changes: 9 additions & 9 deletions packages/oruga/src/components/datepicker/DatepickerTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { computed, ref, type PropType } from "vue";
import ODatepickerTableRow from "./DatepickerTableRow.vue";
import { isDefined } from "@/utils/helpers";
import { isTrueish, isDefined } from "@/utils/helpers";
import { defineClasses } from "@/composables";
import { useDatepickerMixins } from "./useDatepickerMixins";
Expand Down Expand Up @@ -109,7 +109,7 @@ function eventsInThisWeek(week: Date[]): DatepickerEvent[] {
}
const hoveredDateRange = computed(() => {
if (!datepicker.value.range || selectedEndDate.value) return [];
if (!isTrueish(datepicker.value.range) || selectedEndDate.value) return [];
return (
hoveredEndDate.value < selectedBeginDate.value
? [hoveredEndDate.value, selectedBeginDate.value]
Expand Down Expand Up @@ -155,10 +155,10 @@ function validateFocusedDay(): void {
/** Emit input event with selected date as payload for v-model in parent */
function onSelectedDate(date: Date): void {
if (datepicker.value.disabled) return;
if (!datepicker.value.range && !datepicker.value.multiple)
emits("update:modelValue", date);
else if (datepicker.value.range) handleSelectRangeDate(date);
else if (datepicker.value.multiple) handleSelectMultipleDates(date);
else if (isTrueish(datepicker.value.range)) handleSelectRangeDate(date);
else if (isTrueish(datepicker.value.multiple))
handleSelectMultipleDates(date);
else emits("update:modelValue", date);
}
/*
Expand Down Expand Up @@ -194,9 +194,9 @@ function handleSelectRangeDate(date: Date): void {
* Otherwise, add date to list of selected dates
*/
function handleSelectMultipleDates(date: Date): void {
if (!Array.isArray(props.modelValue)) return;
let multipleSelectedDates = props.modelValue;
let multipleSelectedDates = Array.isArray(props.modelValue)
? props.modelValue
: [];
const multipleSelect = multipleSelectedDates.filter(
(selectedDate) =>
selectedDate.getDate() === date.getDate() &&
Expand Down
Loading

0 comments on commit 8689a40

Please sign in to comment.