Skip to content

Commit

Permalink
feat(ui): complete datepicker #1
Browse files Browse the repository at this point in the history
  • Loading branch information
BQXBQX committed Feb 8, 2024
1 parent b04cb9c commit cc0e2be
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 86 deletions.
3 changes: 2 additions & 1 deletion packages/competition/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Button, Input, showToast } from "../../ui/dist";
import { Button, DatePicker, Input, showToast } from "../../ui/dist";
import "./App.css";

function App() {
return (
<>
<Button onClick={() => showToast()}>showToast</Button>
<Input></Input>
<DatePicker></DatePicker>
</>
);
}
Expand Down
22 changes: 12 additions & 10 deletions packages/ui/lib/Calendar/Calendar.module.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
@use '../variables' as *;
.base {
height: fit-content;
width: fit-content;
padding: 10px;
border-radius: 10px;
background-color: var(--white-color);
color: var(--black-color);
box-shadow: 1px 1px 4px 0px var(--shadow-color);
box-shadow: $shadow;
.calendarTitle {
font-size: 18px;
font-weight: 1000;
Expand All @@ -18,15 +19,16 @@
.buttonContainer {
display: flex;
gap: 3px;
cursor: pointer;
svg {
opacity: 0.8;
transition: all 0.2s ease-in-out;
}
svg:hover {
scale: 0.95;
opacity: 1;
transition: all 0.2s ease-in-out;
.button {
height: 24px;
width: 24px;
position: relative;
svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions packages/ui/lib/Calendar/Calendar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ type Story = StoryObj<typeof meta>;

const defaultProps: CalendarProps = {
onChange: function () {},
selected: new Date(),
defaultSelected: new Date(),
defaultSelected: undefined,
};

export const DefaultCalendar: Story = {
Expand All @@ -37,6 +36,5 @@ export const ExampleCalendar: Story = {
args: {
...defaultProps,
onChange: test,
selected: new Date(0),
},
};
102 changes: 37 additions & 65 deletions packages/ui/lib/Calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import styles from './Calendar.module.scss';
import DayItem from './DayItem';
import { Button } from '..';

export interface CalendarProps {
export interface CalendarProps extends React.HtmlHTMLAttributes<HTMLDivElement> {
/**
* the onChange of the calendar
*/
onChange?: (value: Date) => void;
onchange?: (value: Date) => void;
/**
* the selected of the date
*/
Expand All @@ -19,9 +20,9 @@ export interface CalendarProps {
}

export const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(
({ onChange, selected, defaultSelected }, ref) => {
({ onchange, selected = undefined, defaultSelected = undefined, ...rest }, ref) => {
// Initialize the state to store the selected date
const [selectDate, setSelectDate] = useState<Date>(defaultSelected || new Date());
const [selectDate, setSelectDate] = useState<Date | undefined>(defaultSelected);
const [currentDate] = useState<Date>(new Date());
const [numberOfDaysFromPrevMonth, setNumberOfDaysFromPrevMonth] = useState<number>(0);
const [numberOfDaysInLastMonth, setNumberOfDaysInLastMonth] = useState<number>(0);
Expand Down Expand Up @@ -86,82 +87,53 @@ export const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(
};

useEffect(() => {
onChange && onChange(selectDate);
}, [selectDate, onChange]);
selectDate && onchange && onchange(selectDate);
}, [selectDate, onchange]);

return (
<div
className={calendarClass}
ref={ref}
{...rest}
>
<div className={styles['calendarTitle']}>
<div className={styles['buttonContainer']}>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
<Button
color="border"
onClick={() => changeMonth(true)}
className={styles['button']}
>
<rect
width="24"
height="24"
rx="5"
fill="white"
/>
<rect
x="0.5"
y="0.5"
width="23"
height="23"
rx="4.5"
stroke="#808080"
/>
<path
d="M17.25 12.0001C17.25 11.8454 17.1885 11.697 17.0791 11.5876C16.9697 11.4782 16.8213 11.4167 16.6666 11.4167H7.62496C7.47025 11.4167 7.32188 11.4782 7.21248 11.5876C7.10308 11.697 7.04163 11.8454 7.04163 12.0001C7.04163 12.1548 7.10308 12.3032 7.21248 12.4126C7.32188 12.522 7.47025 12.5834 7.62496 12.5834H16.6666C16.8213 12.5834 16.9697 12.522 17.0791 12.4126C17.1885 12.3032 17.25 12.1548 17.25 12.0001Z"
fill="black"
/>
<path
d="M11.2457 16.204C11.3551 16.0946 11.4165 15.9463 11.4165 15.7916C11.4165 15.6369 11.3551 15.4886 11.2457 15.3792L7.86649 11.9999L11.2457 8.62066C11.352 8.51064 11.4108 8.36329 11.4095 8.21034C11.4081 8.0574 11.3468 7.91109 11.2386 7.80293C11.1305 7.69478 10.9842 7.63343 10.8312 7.6321C10.6783 7.63077 10.5309 7.68957 10.4209 7.79583L7.04166 11.1751C6.82294 11.3939 6.70007 11.6906 6.70007 11.9999C6.70007 12.3093 6.82294 12.606 7.04166 12.8247L10.4209 16.204C10.5303 16.3134 10.6786 16.3748 10.8333 16.3748C10.988 16.3748 11.1363 16.3134 11.2457 16.204Z"
fill="black"
/>
</svg>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
<svg
width="16"
height="16"
fill="#333333"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M11.7265 12L12.6665 11.06L9.61317 8L12.6665 4.94L11.7265 4L7.7265 8L11.7265 12Z" />
<path d="M7.33344 12L8.27344 11.06L5.2201 8L8.27344 4.94L7.33344 4L3.33344 8L7.33344 12Z" />
</svg>
</Button>
<Button
color="border"
onClick={() => changeMonth(false)}
className={styles['button']}
>
<rect
width="24"
height="24"
rx="5"
fill="white"
/>
<rect
x="0.5"
y="0.5"
width="23"
height="23"
rx="4.5"
stroke="#808080"
/>
<path
d="M6.75 12.0001C6.75 11.8454 6.81146 11.697 6.92085 11.5876C7.03025 11.4782 7.17862 11.4167 7.33333 11.4167H16.375C16.5297 11.4167 16.6781 11.4782 16.7875 11.5876C16.8969 11.697 16.9583 11.8454 16.9583 12.0001C16.9583 12.1548 16.8969 12.3032 16.7875 12.4126C16.6781 12.522 16.5297 12.5834 16.375 12.5834H7.33333C7.17862 12.5834 7.03025 12.522 6.92085 12.4126C6.81146 12.3032 6.75 12.1548 6.75 12.0001Z"
fill="black"
/>
<path
d="M12.7543 16.204C12.6449 16.0946 12.5835 15.9463 12.5835 15.7916C12.5835 15.6369 12.6449 15.4886 12.7543 15.3792L16.1335 11.9999L12.7543 8.62066C12.648 8.51064 12.5892 8.36329 12.5906 8.21034C12.5919 8.0574 12.6532 7.91109 12.7614 7.80293C12.8695 7.69478 13.0159 7.63343 13.1688 7.6321C13.3218 7.63077 13.4691 7.68957 13.5791 7.79583L16.9584 11.1751C17.1771 11.3939 17.3 11.6906 17.3 11.9999C17.3 12.3093 17.1771 12.606 16.9584 12.8247L13.5791 16.204C13.4697 16.3134 13.3214 16.3748 13.1667 16.3748C13.012 16.3748 12.8637 16.3134 12.7543 16.204Z"
fill="black"
/>
</svg>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="#333333"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M4.2735 4L3.3335 4.94L6.38683 8L3.3335 11.06L4.2735 12L8.2735 8L4.2735 4Z" />
<path d="M8.66656 4L7.72656 4.94L10.7799 8L7.72656 11.06L8.66656 12L12.6666 8L8.66656 4Z" />
</svg>
</Button>
</div>
<span>
{(([, month, , year]) => `${month}, ${year}`)(
new Date(currentDate.getFullYear(), selectMonth, 0).toDateString().split(' '),
new Date(currentDate.getFullYear(), selectMonth + 1, 0).toDateString().split(' '),
)}
</span>
</div>
Expand Down
16 changes: 10 additions & 6 deletions packages/ui/lib/Calendar/DayItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import styles from './Calendar.module.scss';
interface DayItemProps {
index: number;
isOtherMonth: 'pre' | 'this' | 'after';
selectDate: Date;
selectDate?: Date;
currentDate: Date;
onChange: (value: Date) => void;
selectMonth: number;
Expand Down Expand Up @@ -32,24 +32,28 @@ const DayItem = memo(function otherMonthItem({
break;
}

function isSameDay(date1: Date, date2: Date) {
function isSameDay(date1?: Date, date2?: Date) {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
date1?.getFullYear() === date2?.getFullYear() &&
date1?.getMonth() === date2?.getMonth() &&
date1?.getDate() === date2?.getDate()
);
}

const thisDate = new Date(currentDate.getFullYear(), month, index);

const handleClick = () => {
thisDate && onChange(thisDate);
};

return (
<div
key={`${isOtherMonth} ${index}`}
className={`${styles['calendarItem']}
${styles[isOtherMonth !== 'this' ? 'otherMonth' : '']}
${styles[isSameDay(thisDate, currentDate) ? 'today' : '']}
${styles[isSameDay(thisDate, selectDate) ? 'select' : '']}`}
onClick={() => onChange(thisDate)}
onClick={handleClick}
>
{index}
</div>
Expand Down
59 changes: 59 additions & 0 deletions packages/ui/lib/DatePicker/DatePicker.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@mixin animation($type) {
animation-name: #{$type};
animation-duration: 200ms;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}
.date-picker-button {
display: flex;
align-items: center;
gap: 10px;
color: var(--shadow-color) !important;
transition: all 0.2s ease-in-out;
padding: 10px 15px !important;
width: 280px;
&.select {
color: var(--primary-color) !important;
svg {
fill: var(--primary-color) !important;
}
}
svg {
fill: var(--shadow-color);
transition: all 0.2s ease-in-out;
}
&:hover {
color: var(--primary-color) !important;
svg {
fill: var(--primary-color);
}
}
}
.calendar-container {
position: absolute;
margin-top: 5px;
&.in {
@include animation(in);
}
&.hide {
@include animation(hide);
}
}

@keyframes in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@keyframes hide {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
55 changes: 55 additions & 0 deletions packages/ui/lib/DatePicker/DatePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Meta, StoryObj } from '@storybook/react';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React from 'react';
import { DatePicker, type DatePickerProps } from './DatePicker';

const test = (value: Date) => {
console.log('selectDate', value);
};

function generateRandomDate() {
const startTimestamp = new Date(2000, 0, 1).getTime(); // 开始日期的时间戳,这里设置为2000年1月1日
const endTimestamp = new Date().getTime(); // 当前日期的时间戳,作为结束日期

const randomTimestamp = Math.random() * (endTimestamp - startTimestamp) + startTimestamp;
const randomDate = new Date(randomTimestamp);

return randomDate;
}

const meta = {
title: 'Components/DatePicker',
component: DatePicker,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<typeof DatePicker>;

export default meta;

type Story = StoryObj<typeof meta>;

const defaultProps: DatePickerProps = {
onchange: test,
};

export const DefaultDatePicker: Story = {
args: {
...defaultProps,
},
};

export const RandomDefaultDatePicker: Story = {
args: {
...defaultProps,
defaultPickDate: new Date(),
},
};

export const RandomDatePicker: Story = {
args: {
pickDate: generateRandomDate(),
},
};
Loading

0 comments on commit cc0e2be

Please sign in to comment.