我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
前言
本人准大三前端菜鸡一枚,本文记录我为开源库贡献组件pr以及第一次封装通用组件,文章里有什么不合理地方希望大家能够帮忙指出👂
成品展示
为什么想敲这样一个组件
说到为何有这个想法,那就得从一个平平无奇的凌晨说起,在掘金上逛到了文章React组件库Concis,寻求社区有兴趣的小伙伴加入...
,对这很有兴趣的我在博主的GitHub上留下issue表达了我的想法,并在昨天贡献了我的第一个组件(DatePicker)。下面我就来说说我如何一步步写出相关代码(💩!!!)的。
构思需要的提供的API
- className:自定义类名
- format:日期格式化
- showClear:是否显示清除按钮
- align:组件出现方向(沿用已经完成的Poover组件进行包裹)
- handleChange:选择点击事件时回调函数
- disableCheck:是否禁用选项
开始设计
1.做出组件显示结构
Header部分
<div className="date-picker-select">
<div className="left-select">
<DoubleLeftOutlined onClick={() => setYear(nowDate.year - 1)} />
<LeftOutlined onClick={() => setMonth(nowDate.month - 1, 'del')} />
</div>
<div className="middle-select">
<span>{nowDate.year}</span>-<span>{nowDate.month}</span>
</div>
<div className="right-select">
<RightOutlined onClick={() => setMonth(nowDate.month + 1, 'add')} />
<DoubleRightOutlined onClick={() => setYear(nowDate.year + 1)} />
</div>
</div>
Panel部分
样式采用Flex布局,子元素style使用flex:1,使子元素均分宽度
<table>
<thead>
<tr>
{titleList.map((title, idx) => (
<th key={idx}>{title}</th>
))}
</tr>
</thead>
<tbody>
{nowDayList.map((row, idx) => (
<tr key={idx}>
{row.map((day, idx) => (
<td
key={idx}
onClick={() => setInputVal(day)}
className={`${day.value === '' ? 'day-empty' : ''} ${
day.disable ? 'disable' : ''
} ${isSameDate(day.date) ? 'active' : ''}`}
>
{day.value}
</td>
))}
</tr>
))}
</tbody>
</table>
2.功能代码设计
更新Panel面板
更新年月
随着用户在Header中更改年份与月份,需要更新对应月份的DatePanel面板,更新年份只需要简单对数据进行+-1操作,而月份有着1~12的显示,所以在更改之前需要做一些简单的判断。附上对应代码:
const setMonth = (month: number, type: string): void => {
let date = {} as NowDateProps;
if (type === 'add') {
if (month > 12) {
date = { ...nowDate, month: 1, year: nowDate.year + 1 };
} else {
date = { ...nowDate, month };
}
} else {
if (month < 0) {
date = { ...nowDate, month: 12, year: nowDate.year - 1 };
} else {
date = { ...nowDate, month };
}
}
setNowDate(date);
};
更新日期面板
通过解构取得nowDate
对象中的year
与month
值,我们此刻需要知道该月1号是星期几「firstWeekDay」
以及该月的总天数「lastDay」
,通过js的Date对象的内置方法可以解决前面的问题,在面板渲染中我使用了数组嵌套数组的方式渲染tr>td
的嵌套结构,daysArr
得到了长度为td个数的空数组(tr个数=td个数/7),对dayArr
进行遍历,当index
-firstWeekDay
等于0时说明本次遍历应该填如该月份的1号。本次遍历还会储存Date
对象(用于点击事件传值)对date
进行disableCheck
函数的验证,以供后续添加类名以及判断,最后使用lodash/chunk
分割数组。贴上相关代码:
useEffect(() => {
const { year, month } = nowDate;
//1号是星期几
const firstWeekDay = new Date(year, month, 1).getDay();
//最后一天几号
const lastDay = new Date(year, month, 0).getDate();
const daysArr = new Array(Math.ceil((firstWeekDay + lastDay) / 7) * 7).fill('');
setNowDayList(
chunk(
daysArr.map((_, i) => {
const day = `${
i - firstWeekDay <= -1 || i - firstWeekDay + 1 > lastDay ? '' : i - firstWeekDay + 1
}`;
const date = new Date(year, month - 1, Number(day));
return {
date,
disable: disableCheck(date),
value: day,
};
}),
7,
),
);
}, [nowDate.year, nowDate.month]);
active/disable类名
还记得得到日期面板时遍历中进行的disableCheck
函数吗?disable
类名就是由他决定是否添加上去的啦!
而active
类名的添加是通过isSameDate
函数进行判断年月日是否相同,返回值为布尔类型,默认的active类名添加在与今天相同的日期上,后续用户通过点击或赋值更改类名归属。贴上相关代码:
const [clickDate, setClickDate] = useState(new Date());
const isSameDate = (date: Date) => {
return (
date.getFullYear() === clickDate.getFullYear() &&
date.getMonth() + 1 === clickDate.getMonth() + 1 &&
date.getDate() === clickDate.getDate()
);
};
3.table样式代码
直接贴代码:
table {
width: 100%;
padding: 10px;
//border-top: 1px solid #e0e0e0;
tr {
display: flex;
td,
th {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 32px;
height: 32px;
margin: 5px 0;
border-radius: 50%;
&:hover:not(th):not(.day-empty, .disable, .active) {
background-image: linear-gradient(#e0e0e0, #e0e0e0);
background-position: center;
cursor: pointer;
}
}
.disable:not(.day-empty) {
color: #a8abb2;
background-color: #f5f7fa;
border-radius: 0;
cursor: not-allowed;
opacity: 1;
}
.active {
color: #fff0ec;
background-image: linear-gradient(@concis-primary-color, @concis-primary-color);
}
}
}
终
总结
这样一个简单的DatePicker
组件就完成啦,如果代码中有什么问题希望大家能够提出来,如果这篇文章对你有帮助,不妨点个赞喔。
源码链接
如果你看到了这,不妨给给Concis
点个star⭐
再走啊!!!
对这个项目有兴趣的也欢迎一起来提pr!!!