Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(datepicker): refactor datepicker related components #782

Merged
merged 3 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading