Skip to content
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
4 changes: 2 additions & 2 deletions src/components/datepicker/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
CalendarHeader,
CalendarRow
} from "./DatePicker.style";
import { getCalenderRow, ICalendarCell } from "./utils";
import { getCalendarRow, ICalendarCell } from "./utils";

interface Props {
date: Dayjs | null;
Expand Down Expand Up @@ -54,7 +54,7 @@ export default function Calendar({
return onChange(value);
};

const rows = useMemo((): ICalendarCell[] => getCalenderRow(month), [month]);
const rows = useMemo((): ICalendarCell[] => getCalendarRow(month), [month]);

return (
<>
Expand Down
36 changes: 36 additions & 0 deletions src/components/datepicker/DatePicker.style.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,45 @@ import { colors } from "../../lib/styles/colors";
import { zIndex } from "../../lib/styles/zIndex";
import { transition } from "../ui/styles";

const CrossIcon = (props: Record<string, unknown>) => (
<svg
height="20"
width="20"
viewBox="0 0 20 20"
aria-hidden="true"
focusable="false"
{...props}
>
<path d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"></path>
</svg>
);
export const ClearButton = styled(CrossIcon)`
position: absolute;
background-color: ${colors.lightGrey};
top: 0;
right: 0;
height: 100%;
margin: 0 5px;
cursor: pointer;
display: inline-block;
fill: #cccccc;
line-height: 1;
stroke: #cccccc;
stroke-width: 0;
:hover * {
stroke: #999999;
fill: #999999;
}
`;
export const Picker = styled.div`
width: 100%;
position: relative;
:hover,
:focus-within {
${ClearButton} {
display: initial;
}
}
`;
export const PickerGrid = styled.div.attrs(
(props: { selectTime: boolean }) => ({
Expand Down
118 changes: 92 additions & 26 deletions src/components/datepicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,86 +8,134 @@ dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(advancedFormat);

import { useCallback, useEffect, useRef, useState } from "react";
import { getTimeZoneWithGMT } from "lib/utils/time";
import {
FocusEventHandler,
memo,
useCallback,
useEffect,
useRef,
useState
} from "react";

import { useDidMountEffect } from "../../lib/utils/hooks/useDidMountEffect";
import { FieldInput } from "../form/Field.styles";
import Calendar from "./Calendar";
import { DatePickerWrapper, Picker, PickerGrid } from "./DatePicker.style";
import {
ClearButton,
DatePickerWrapper,
Picker,
PickerGrid
} from "./DatePicker.style";
import SelectMonth from "./SelectMonth";
import SelectTime from "./SelectTime";

interface Props {
export type DatePickerProps = {
initialValue?: Dayjs | Array<Dayjs> | null;
onChange?: (selected: Dayjs | Array<Dayjs | null> | null) => void;
onChange?: (selected: Dayjs | Array<Dayjs | null> | null | undefined) => void;
onBlur?: FocusEventHandler<HTMLInputElement> | undefined;
onClick?: () => void;
error?: string;
period: boolean;
selectTime: boolean;
minDate?: Dayjs | null;
maxDate?: Dayjs | null;
isClearable?: boolean;
placeholder?: string;
name?: string;
[x: string]: any;
}
export interface ChoosenTime {
};
export type ChoosenTime = {
hour: string | Array<string>;
minute: string | Array<string>;
timezone: string;
}
};

const handleInitialDates = (
initialValue: Dayjs | Array<Dayjs> | null | undefined
) => {
let startDate: Dayjs | null = null;
let endDate: Dayjs | null = null;
let chosenTime: ChoosenTime | null = null;

if (Array.isArray(initialValue)) {
if (initialValue.length) {
startDate = dayjs(initialValue[0]);
endDate = dayjs(initialValue[1]);
if (initialValue) {
if (Array.isArray(initialValue)) {
if (initialValue.length) {
startDate = dayjs(initialValue[0]);
endDate = dayjs(initialValue[1]);
chosenTime = {
hour: [
startDate.toDate().getHours().toString(),
endDate.toDate().getHours().toString()
],
minute: [
startDate.toDate().getMinutes().toString(),
endDate.toDate().getMinutes().toString()
],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
timezone: startDate.$x.$timezone ?? endDate.$x.$timezone
};
}
} else {
startDate = dayjs(initialValue);
chosenTime = {
hour: startDate.toDate().getHours().toString(),
minute: startDate.toDate().getMinutes().toString(),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
timezone: startDate.$x.$timezone
};
}
} else {
startDate = dayjs(initialValue);
}

return {
startDate,
endDate
endDate,
chosenTime
};
};
const dateTimeFormat = "MMMM D, YYYY HH:mm";
export default function DatePicker({
export default memo(function DatePicker({
initialValue,
onChange,
onBlur,
onClick,
period,
selectTime,
maxDate,
minDate = dayjs(),
isClearable,
placeholder = "Choose dates...",
...props
}: Props) {
}: DatePickerProps) {
const ref = useRef<HTMLDivElement | null>(null);

const [month, setMonth] = useState<Dayjs>(dayjs());
const [time, setTime] = useState<ChoosenTime | null>(null);
const [date, setDate] = useState<Dayjs | null>(null);
const [secondDate, setSecondDate] = useState<Dayjs | null>(null);
const [shownDate, setShownDate] = useState<string>("Choose dates...");
const [shownDate, setShownDate] = useState<string>();
const [show, setShow] = useState<boolean>(false);
const [showTime, setShowTime] = useState<boolean>(false);

useEffect(() => {
const { startDate, endDate } = handleInitialDates(initialValue);
const { startDate, endDate, chosenTime } = handleInitialDates(initialValue);
if (date === null) setDate(startDate);
if (secondDate === null) setSecondDate(endDate);
if (chosenTime === null) setTime(chosenTime);
}, [initialValue]); // eslint-disable-line

const handleShow = () => {
setShow(!show);
onClick?.();
};

const reset = useCallback(() => {
setDate(null);
setSecondDate(null);
setShowTime(false);
setShownDate("Choose dates...");
setShownDate("");
}, []);

const handleDateChange = (inputDate: Dayjs | null) => {
Expand Down Expand Up @@ -123,11 +171,11 @@ export default function DatePicker({
(!period && date === null) ||
(period && date === null && secondDate === null)
) {
setShownDate("Choose dates...");
setShownDate("");
if (period) {
onChange?.([]);
} else {
onChange?.(null);
onChange?.(undefined);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -156,7 +204,11 @@ export default function DatePicker({
setShownDate(
`${newDate?.format(dateTimeFormat)} - ${newSecondDate?.format(
dateTimeFormat
)} ${time ? `(${time.timezone} GMT${newDate?.format("Z")})` : ""}`
)} ${
time && newDate.isValid()
? `(${getTimeZoneWithGMT(time.timezone)})`
: ""
}`
);
onChange?.([newDate, newSecondDate]);
}
Expand All @@ -173,14 +225,20 @@ export default function DatePicker({
}
setShownDate(
`${newDate?.format(dateTimeFormat)} ${
time ? `(${time.timezone} GMT${newDate?.format("Z")})` : ""
time && newDate.isValid()
? `(${getTimeZoneWithGMT(time.timezone)})`
: ""
}`
);
onChange?.(newDate);
}
}
}, [date, secondDate, time]); // eslint-disable-line

useEffect(() => {
if (!period) {
setSecondDate(null);
}
}, [period]);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
const clicksOn =
Expand All @@ -198,7 +256,14 @@ export default function DatePicker({

return (
<Picker>
<FieldInput value={shownDate} onClick={handleShow} {...props} readOnly />
<FieldInput
value={shownDate}
onClick={handleShow}
onBlur={onBlur}
{...props}
readOnly
placeholder={placeholder}
/>
<DatePickerWrapper
show={show}
selectTime={selectTime && showTime}
Expand Down Expand Up @@ -229,6 +294,7 @@ export default function DatePicker({
)}
</PickerGrid>
</DatePickerWrapper>
{isClearable && <ClearButton onClick={reset} />}
</Picker>
);
}
});
48 changes: 15 additions & 33 deletions src/components/datepicker/SelectTime.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint @typescript-eslint/no-explicit-any: "off" */
import dayjs from "dayjs";
import advancedFormat from "dayjs/plugin/advancedFormat";
import isToday from "dayjs/plugin/isToday";
Expand All @@ -9,10 +10,9 @@ dayjs.extend(advancedFormat);
dayjs.extend(isToday);

import type { Dayjs } from "dayjs";
import timezones from "lib/constants/timezones.json";
import { timezones } from "lib/utils/time";
import { useCallback, useEffect, useMemo, useState } from "react";

import { isTruthy } from "../../lib/types/helpers";
import BaseSelect from "../form/BaseSelect";
import Grid from "../ui/Grid";
import Typography from "../ui/Typography";
Expand All @@ -34,21 +34,8 @@ const BASE_MINUTES = Array.from(Array(60).keys()).map((v) => ({
label: ("0" + v).slice(-2).toString(),
value: ("0" + v).slice(-2).toString()
}));
const OPTIONS_TIMEZONES = timezones
.map((timezone) => {
try {
return {
...timezone,
label: `${timezone.value} (GMT${dayjs()
.tz(timezone.value)
.format("Z")})`
};
} catch (error) {
return false;
}
})
.filter(isTruthy);

const OPTIONS_TIMEZONES = timezones;
export default function SelectTime({
setTime,
period,
Expand All @@ -58,29 +45,24 @@ export default function SelectTime({
const DEFAULT_HOUR = useMemo(
() =>
period
? [date?.isToday() ? dayjs().format("HH") : "00", "23"]
: date?.isToday()
? dayjs().format("HH")
: "00",
[period, date]
? [date?.format("HH") || "00", secondDate?.format("HH") || "23"]
: date?.format("HH") || "00",
[period, date, secondDate]
);
const DEFAULT_MINUTE = useMemo(
() =>
period
? [date?.isToday() ? dayjs().add(5, "minute").format("mm") : "00", "59"]
: date?.isToday()
? dayjs().add(5, "minute").format("mm")
: "00",
[period, date]
? [date?.format("mm") || "00", secondDate?.format("mm") || "59"]
: date?.format("mm") || "00",
[period, date, secondDate]
);
const DEFAULT_TIME = useMemo(() => (period ? ["00", "00"] : "00"), [period]);
const [timezone, setTimezone] = useState<string>(dayjs.tz.guess());
const [hour, setHour] = useState<string | string[]>(
DEFAULT_HOUR || DEFAULT_TIME
);
const [minute, setMinute] = useState<string | string[]>(
DEFAULT_MINUTE || DEFAULT_TIME
const [timezone, setTimezone] = useState<string>(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
date?.$x.$timezone ?? secondDate?.$x.$timezone ?? dayjs.tz.guess()
);
const [hour, setHour] = useState<string | string[]>(DEFAULT_HOUR);
const [minute, setMinute] = useState<string | string[]>(DEFAULT_MINUTE);

useEffect(() => {
setTime({
Expand Down
2 changes: 1 addition & 1 deletion src/components/datepicker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function getCells(selectedDate: Dayjs, date: Dayjs): ICalendarCell[] {
return cells;
}

export function getCalenderRow(date: Dayjs): ICalendarCell[] {
export function getCalendarRow(date: Dayjs): ICalendarCell[] {
const cells = getCells(date, date.startOf("month"));
const rows: Array<ICalendarCell[]> = [];
rows.push(cells);
Expand Down
3 changes: 2 additions & 1 deletion src/components/detail/Detail.style.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { defaultFontFamily } from "lib/styles/fonts";
import styled, { css } from "styled-components";

import frameImage from "../../assets/frame.png";
Expand Down Expand Up @@ -492,7 +493,7 @@ export const Widget = styled.div`
flex-wrap: wrap;
width: 100%;
background: ${colors.white};
font-family: "Plus Jakarta Sans";
font-family: ${defaultFontFamily};
> div {
padding: 0 2rem;
&:first-of-type {
Expand Down
Loading