Skip to content

Commit

Permalink
fix(datepicker): refactor datepicker related components (#782)
Browse files Browse the repository at this point in the history
* fix datepicker vue internal issue
* refactor useVModel composable to defineModel macro
* refactor datepicker utils
* adjust datepicker related component imports
  • Loading branch information
mlmoravek authored Feb 15, 2024
1 parent 22b36a8 commit da9eb0f
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 85 deletions.
22 changes: 7 additions & 15 deletions packages/oruga-next/src/components/datepicker/Datepicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,12 @@ import ODatepickerTable from "./DatepickerTable.vue";
import ODatepickerMonth from "./DatepickerMonth.vue";
import { getOption } from "@/utils/config";
import {
defineClasses,
getActiveClasses,
useVModelBinding,
useMatchMedia,
usePropBinding,
} from "@/composables";
import { defineClasses, getActiveClasses, useMatchMedia } from "@/composables";
import { useDatepickerMixins } from "./useDatepickerMixins";
import { getMonthNames, getWeekdayNames } from "./utils";
import {
useDatepickerShare,
type DatepickerEvent,
type FocusedDate,
} from "./useDatepickerShare";
import type { DatepickerEvent, FocusedDate } from "./types";
import type { ComponentClass, OrugaOptions } from "@/types";
/**
Expand Down Expand Up @@ -600,14 +591,15 @@ const emits = defineEmits<{
(e: "icon-right-click", event: Event): void;
}>();
const { defaultDateFormatter, defaultDateParser } = useDatepickerShare(props);
const { defaultDateFormatter, defaultDateParser } = useDatepickerMixins(props);
const { isMobile } = useMatchMedia(props.mobileBreakpoint);
const vmodel = useVModelBinding<Date | Date[]>(props, emits, { passive: true });
/** modelvalue of selected date */
const vmodel = defineModel<Date | Date[]>();
/** Dropdown active state */
const isActive = usePropBinding<boolean>("active", props, emits);
const isActive = defineModel<boolean>("active");
/** modelValue formated into string */
const formattedValue = computed(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import {
import { isDefined } from "@/utils/helpers";
import { defineClasses } from "@/composables";
import {
type DatepickerProps,
type DatepickerEvent,
type FocusedDate,
} from "./useDatepickerShare";
import type { DatepickerProps, DatepickerEvent, FocusedDate } from "./types";
import type { ClassBind } from "@/types";
defineOptions({
Expand Down
50 changes: 23 additions & 27 deletions packages/oruga-next/src/components/datepicker/DatepickerTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import { computed, ref, type PropType } from "vue";
import ODatepickerTableRow from "./DatepickerTableRow.vue";
import { isDefined } from "@/utils/helpers";
import { defineClasses, usePropBinding } from "@/composables";
import { defineClasses } from "@/composables";
import { useDatepickerMixins } from "./useDatepickerMixins";
import { weekBuilder } from "./utils";
import {
useDatepickerShare,
type DatepickerProps,
type DatepickerEvent,
type FocusedDate,
} from "./useDatepickerShare";
import type { DatepickerProps, DatepickerEvent, FocusedDate } from "./types";
defineOptions({
name: "ODatepickerTable",
Expand Down Expand Up @@ -43,16 +39,16 @@ const emits = defineEmits<{
(e: "week-number-click", value: number): void;
}>();
const { isDateSelectable } = useDatepickerShare(props.pickerProps);
const { isDateSelectable } = useDatepickerMixins(props.pickerProps);
const focusedDateModel = defineModel<FocusedDate>("focusedDate");
const selectedBeginDate = ref<Date>();
const selectedEndDate = ref<Date>();
const hoveredEndDate = ref<Date>();
const datepicker = computed<DatepickerProps>(() => props.pickerProps);
const focusedDate = usePropBinding<FocusedDate>("focusedDate", props, emits);
const visibleDayNames = computed(() => {
const visibleDayNames = [];
let index = datepicker.value.firstDayOfWeek;
Expand All @@ -74,16 +70,16 @@ const eventsInThisMonth = computed(() => {
)
.filter(
(event) =>
event.date.getMonth() === focusedDate.value.month &&
event.date.getFullYear() === focusedDate.value.year,
event.date.getMonth() === focusedDateModel.value.month &&
event.date.getFullYear() === focusedDateModel.value.year,
);
});
/** Return array of all weeks in the specified month */
const weeksInThisMonth = computed(() => {
validateFocusedDay();
const month = focusedDate.value.month;
const year = focusedDate.value.year;
const month = focusedDateModel.value.month;
const year = focusedDateModel.value.year;
const weeksInThisMonth = [];
let startingDay = 1;
Expand Down Expand Up @@ -123,29 +119,29 @@ const hoveredDateRange = computed(() => {
function validateFocusedDay(): void {
const currentDate = new Date(
focusedDate.value.year,
focusedDate.value.month,
focusedDate.value.day,
focusedDateModel.value.year,
focusedDateModel.value.month,
focusedDateModel.value.day,
);
if (isDateSelectable(currentDate, focusedDate.value.month)) return;
if (isDateSelectable(currentDate, focusedDateModel.value.month)) return;
let day = 0;
// Number of days in the current month
const monthDays = new Date(
focusedDate.value.year,
focusedDate.value.month + 1,
focusedDateModel.value.year,
focusedDateModel.value.month + 1,
0,
).getDate();
let firstFocusable = null;
while (!firstFocusable && ++day < monthDays) {
const date = new Date(
focusedDate.value.year,
focusedDate.value.month,
focusedDateModel.value.year,
focusedDateModel.value.month,
day,
);
if (isDateSelectable(date, focusedDate.value.month)) {
if (isDateSelectable(date, focusedDateModel.value.month)) {
firstFocusable = currentDate;
focusedDate.value = {
focusedDateModel.value = {
day: date.getDate(),
month: date.getMonth(),
year: date.getFullYear(),
Expand Down Expand Up @@ -225,7 +221,7 @@ function onRangeHoverEndDate(date: Date): void {
}
function onChangeFocus(date: Date): void {
focusedDate.value = {
focusedDateModel.value = {
day: date.getDate(),
month: date.getMonth(),
year: date.getFullYear(),
Expand Down Expand Up @@ -272,9 +268,9 @@ const tableBodyClasses = defineClasses([
v-for="(week, index) in weeksInThisMonth"
:key="index"
:selected-date="modelValue"
:day="focusedDate.day"
:day="focusedDateModel.day"
:week="week"
:month="focusedDate.month"
:month="focusedDateModel.month"
:events="eventsInThisWeek(week)"
:hovered-date-range="hoveredDateRange"
:picker-props="props.pickerProps"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ import {
import { defineClasses } from "@/composables";
import { useDatepickerMixins } from "./useDatepickerMixins";
import { weeksInYear, firstWeekOffset } from "./utils";
import {
useDatepickerShare,
type DatepickerProps,
type DatepickerEvent,
} from "./useDatepickerShare";
import type { DatepickerProps, DatepickerEvent } from "./types";
import type { ClassBind } from "@/types";
defineOptions({
Expand Down Expand Up @@ -47,7 +43,7 @@ const emits = defineEmits<{
(e: "week-number-click", value: number): void;
}>();
const { isDateSelectable } = useDatepickerShare(props.pickerProps);
const { isDateSelectable } = useDatepickerMixins(props.pickerProps);
const datepicker = computed<DatepickerProps>(() => props.pickerProps);
Expand All @@ -74,6 +70,12 @@ watch(
},
);
watch(
() => props.month,
// clear day refs on month change
() => (dayRefs.value = new Map()),
);
function clickWeekNumber(week: number): void {
if (datepicker.value.weekNumberClickable) emits("week-number-click", week);
}
Expand Down Expand Up @@ -182,7 +184,11 @@ function setRangeHoverEndDate(day): void {
// --- Computed Component Classes ---
function dateMatch(dateOne, dateTwo, multiple = false): boolean {
function dateMatch(
dateOne: Date,
dateTwo: Date | Date[],
multiple = false,
): boolean {
// if either date is null or undefined, return false
// if using multiple flag, return false
if (!dateOne || !dateTwo || multiple) return false;
Expand All @@ -202,7 +208,11 @@ function dateMatch(dateOne, dateTwo, multiple = false): boolean {
);
}
function dateWithin(dateOne, dates, multiple = false): boolean {
function dateWithin(
dateOne: Date,
dates: Date | Date[],
multiple = false,
): boolean {
if (!Array.isArray(dates) || multiple) return false;
return dateOne > dates[0] && dateOne < dates[1];
}
Expand Down Expand Up @@ -395,7 +405,7 @@ const cellEventsClass = defineClasses([
:class="eventClasses(event)" />
</div>
</div>
<div v-else :key="idx" :class="cellClasses(weekDay)">
<div v-else :class="cellClasses(weekDay)">
<span>{{ weekDay.getDate() }}</span>
</div>
</template>
Expand Down
5 changes: 3 additions & 2 deletions packages/oruga-next/src/components/datepicker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import type { App, Plugin } from "vue";

import Datepicker from "./Datepicker.vue";

export type { DatepickerEvent, FocusedDate } from "./useDatepickerShare";

import { registerComponent } from "@/utils/plugins";

/** export datepicker specific types */
export type { DatepickerEvent, FocusedDate } from "./types";

/** export datepicker plugin */
export default {
install(app: App) {
Expand Down
15 changes: 15 additions & 0 deletions packages/oruga-next/src/components/datepicker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ComponentProps } from "vue-component-type-helpers";
import Datepicker from "./Datepicker.vue";

export type DatepickerProps = ComponentProps<typeof Datepicker>;

export type DatepickerEvent = {
date: Date;
type?: string;
};

export type FocusedDate = {
day: number;
month: number;
year: number;
};
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
import { computed } from "vue";
import Datepicker from "./Datepicker.vue";
import { matchWithGroups } from "./utils";
import type { DatepickerProps } from "./types";

export type DatepickerProps = InstanceType<typeof Datepicker>["$props"];

export type DatepickerEvent = {
date: Date;
type?: string;
};

export type FocusedDate = {
day: number;
month: number;
year: number;
};

export function useDatepickerShare(props: DatepickerProps) {
export function useDatepickerMixins(props: DatepickerProps) {
/**
* Check that selected date is within earliest/latest params and
* is within a given month
Expand Down
8 changes: 4 additions & 4 deletions packages/oruga-next/src/components/datepicker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @param {String} format long (ex. March), short (ex. Mar) or narrow (M)
* @return {Array<String>} An array of month names
*/
type monthType =
type MonthType =
| "numeric"
| "2-digit"
| "long"
Expand All @@ -14,7 +14,7 @@ type monthType =

export function getMonthNames(
locale: string = undefined,
format: monthType = "long",
format: MonthType = "long",
): string[] {
const dates = [];
for (let i = 0; i < 12; i++) {
Expand All @@ -35,12 +35,12 @@ export function getMonthNames(
* @return {Array<String>} An array of weekday names
*/

type weekdayType = "long" | "short" | "narrow" | undefined;
type WeekdayType = "long" | "short" | "narrow" | undefined;

export function getWeekdayNames(
locale: string = undefined,
firstDayOfWeek: number = 0,
format: weekdayType = "narrow",
format: WeekdayType = "narrow",
): string[] {
const dates = [];
for (let i = 1, j = 0; j < 7; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { isMobileAgent } from "@/utils/helpers";
import { defineClasses, useInputHandler, usePropBinding } from "@/composables";
import { matchWithGroups } from "../datepicker/utils";
import type { DatepickerProps } from "../datepicker/useDatepickerShare";
import type { TimepickerProps } from "../timepicker/useTimepickerShare";
import type { DatepickerProps } from "../datepicker/types";
import type { TimepickerProps } from "../timepicker/types";
import type { ComponentClass } from "@/types";
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
getActiveClasses,
} from "@/composables";
import { useTimepickerMixins } from "./useTimepickerShare";
import { useTimepickerMixins } from "./useTimepickerMixins";
import type { ComponentClass } from "@/types";
Expand Down
4 changes: 4 additions & 0 deletions packages/oruga-next/src/components/timepicker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { ComponentProps } from "vue-component-type-helpers";
import Timepicker from "./Timepicker.vue";

export type TimepickerProps = ComponentProps<typeof Timepicker>;
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { computed } from "vue";
import Timepicker from "./Timepicker.vue";
import { matchWithGroups } from "../datepicker/utils";

export type TimepickerProps = InstanceType<typeof Timepicker>["$props"];
import type { TimepickerProps } from "./types";

const AM = "AM";
const PM = "PM";
Expand Down

0 comments on commit da9eb0f

Please sign in to comment.