魔改react-calendar还原UI设计中的打卡日历效果

2024-07-29 18:26:31 浏览数 (2)

需求

我们需要还原UI给我们的设计图里面的日历样式, 找到了一款第三方日历库,我们如何进行魔改呢?

这是react-calendar 库官方示例中的代码,我们导入使用默认样式就是这个样子

我们需要做成下面的这个样子

咋一看,确实感觉没有什么思路, 不过跟着步伐来,你会发现其实不复杂.

因为接到这样的一个需求, 我大概了看了一下UI设计图,然后第一反应就是去掘金,GITHUB去找有没有对应的轮子库, 但找了一圈,没有找到像这种个性化定义的. 但是要是自己去写吧,自己不一定能写的出来, 而且耗时耗力. 所以也没多想就直接找了一个react用的较多的日历库react-calendar.

方案选择

下面是关于这个库的一些介绍:

React Calendar 是一个用于 React 的灵活且易于使用的日历组件。它允许开发人员在他们的 React 应用程序中轻松集成日期选择功能。以下是对 React Calendar 的详细介绍:

  1. 简单易用
    • React Calendar 提供了简单直观的 API,方便开发人员快速上手并集成到项目中。
  2. 高度可定制
    • 组件提供了多种配置选项,允许开发人员根据需要自定义日历的外观和行为。例如,可以设置日期格式、最小和最大日期、禁用特定日期等。
  3. 支持多种视图
    • React Calendar 支持多种视图模式,包括月视图、年视图等,用户可以根据需求切换视图。
  4. 事件处理
    • 组件提供了丰富的事件处理函数,如日期选择、视图切换等,方便开发人员在不同的交互事件中执行自定义逻辑。
  5. 国际化支持
    • React Calendar 支持多种语言和区域设置,可以轻松实现多语言的日期显示和选择功能。

二话不说,我们直接开始编写.

定义组件,导入库,初始化日历

我们定义一个组件 ClockInCalendar.tsx . 然后将官网的示例直接填写进去.

代码语言:javascript复制
 import { useState } from 'react';
 import Calendar from 'react-calendar';
 
 type ValuePiece = Date | null;
 
 type Value = ValuePiece | [ValuePiece, ValuePiece];
 
 function ClockInCalendar() {
   const [value, onChange] = useState<Value>(new Date());
 
   return (
     <div>
       <Calendar onChange={onChange} value={value} />
     </div>
   );
 }
 
 export default ClockInCalendar

然后在其他组件进行导入即可

app.tsx

代码语言:javascript复制
 import ClockInCalendar from './ClockInCalendar'

.......
 <ClockInCalendar></ClockInCalendar>

此时我们的页面就是这样的

我们需要修改哪些东西呢,观察一开始的那个成品就会发现:

  • 顶部全部进行修改 [改写成我们的头部样式]
  • 周一, 周二, 周三, 转换为一, 二, 三
  • 日期的话只需要数字即可
  • 数字下方需要显示打卡状态 , [绿色:已打卡] , [黄色:请假], [红色:未打卡]
  • 当天日期的背景颜色需要高亮显示
  • 日历可以进行一个展开折叠的效果
  • ....

还有好多小细节需要处理, 不要担心, 跟着我的步伐一步步来, 不难实现!

头部魔改

我们打开F12 就会看到这个, 我们的思路是 将这个进行隐藏display:none, 然后编写自己的DOM结构 CSS样式.

首先创建一个自定义的css文件, 专门用来覆盖组件的内部样式的

代码语言:javascript复制
 .react-calendar__navigation{
     display: none;
 }

然后在_app.tsx [NEXT项目]里面进行一个全局导入

代码语言:javascript复制
 import '../styles/customCalendar.css'

此时我们打开页面, 就会发现日历的头部没有了

然后我们就可以编写头部的结构和样式了,

这里就不放代码, 大概就是左边一部分, 右边一部分, 其中左边又可以分为日历icon 年月份 打卡数量, 右边则是上个月和下个月的button.

日历的周字去除

formatShortWeekdayreact-calendar 库中的一个方法,用于格式化一周中每一天的显示名称。这个方法主要用于显示日历组件中的星期几的缩写形式。

  • locale: 当前的区域设置(例如 en-USzh-CN 等),决定了日期格式的语言和地区规则。
  • date: 当前的日期对象,代表一周中的某一天。
代码语言:javascript复制
        <Calendar
          onChange={onChange}
          value={date}
          locale="zh-CN"
          formatShortWeekday={formatShortWeekday}
        />

对应的方法编写

代码语言:javascript复制
 const formatShortWeekday = (locale: any, date: Date) => {
    const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
    return weekdays[date.getDay()];
  };

date.getDay()Date 对象的一个方法,用于获取一周中某一天的索引。这个方法返回的值是一个整数,代表一周中的某一天。具体来说,返回值是一个从 06 的整数,分别对应一周的七天。

自定义日期单元格中的内容(状态指示 日期显示格式)

tileContent 是一个非常有用的属性,允许你自定义日历每个日期单元格中的内容。这个属性接收一个函数作为参数,你可以通过这个函数提供自定义的渲染逻辑来展示日期信息、事件、标记等内容。

代码语言:javascript复制
      <Calendar
        key={date.toString()}
        onChange={onChange}
        value={date}
        locale="zh-CN"
        tileContent={tileContent}
        formatShortWeekday={formatShortWeekday}
      />

这个就是上面函数的编写, 可以看下注释.

大概就是做了

  • 格式化日期
  • 比对MocK的数据日期的状态,
  • 如果是completed, 就设置指示状态的背景颜色为 绿色
  • 如果是missed, 就设置指示状态的背景颜色为 红色
  • 如果是leave, 就设置指示状态的背景颜色为 黄色
  • 比对当天的日期, 对当天的日期进行一个背景颜色的高亮

最后将这些上面格式化之后的数据进行一个数据填入, 最后将这个dom结构进行return 返回出去

代码语言:javascript复制
/**
 * 根据日期和视图类型为日历的每个瓷砖设置内容。
 *
 * 这个函数在 `month` 视图中为每个日期的瓷砖返回自定义内容,包括日期数字和状态指示点。
 *
 * @function
 * @param {{ date: Date, view: 'month' | 'year' | 'decade' | 'century' }} tileInfo - 包含日期和视图类型的信息对象。
 * @returns {JSX.Element | null} 返回一个包含日期数字和状态指示点的 JSX 元素,或者在其他视图类型中返回 `null`。
 * @example
 * // 在组件中使用示例
 * const content = tileContent({ date, view });
 * return <div>{content}</div>;
 */
  const tileContent = ({ date, view }: { date: Date, view: string }): JSX.Element | null => {
    if (view === 'month') {
      const formattedDate = format(date, 'd', { locale: zhCN });
      
      const dateString = format(date, 'yyyy-MM-dd');
      let dotStyle = styles.transparentDot;
	
      // 进行日期状态的判断,然后分别给状态指示添加不同的css的背景颜色.
      if (data[dateString] === 'completed') {
        dotStyle = { ...styles.dot, backgroundColor: '#00ee00' };
      } else if (data[dateString] === 'missed') {
        dotStyle = { ...styles.dot, backgroundColor: '#FF4500' };
      } else if (data[dateString] === 'leave') {
        dotStyle = { ...styles.dot, backgroundColor: '	#FFD700' };
      }
        
  	// 当前日期的背景颜色进行一个高亮显示
      // 判断是否为当天
      // 获取当天
      const currentDate = new Date();
      const tileStyle = isSameDay(date, currentDate)
        ? { ...styles.customCalendarTile, ...styles.highlightedTile }
        : styles.customCalendarTile;
      return (
        // 日期数字
        <div className='flex flex-col items-center'>
          <div style={tileStyle}
            className='hover:bg-slate-100'
          >
            {formattedDate}
          </div>
          {/* 状态标识 */}
          <div style={dotStyle}></div>
        </div>
      );
    }
    return null;
  };

日历的折叠/展开

这里先说下思路

通过在日历组件外面套一侧DIV, 分别为它创建两个类名

  • 一个设置高为80px [正好显示一行的高度]
  • 一个设置高为500px [全部显示]

通过点击动态添加类名,即可Ok

代码语言:javascript复制
const styles: { [key: string]: React.CSSProperties } = {
calendarContainer: {
  overflow: 'hidden',
  transition: 'max-height 0.3s ease-out',
},
calendarContainerExpanded: {
  maxHeight: '500px', 
},
calendarContainerCollapsed: {
  maxHeight: '70px', // 只显示头部和第一排日期的高度
}
};
代码语言:javascript复制
const [collapsed, setCollapsed] = useState<boolean>(false);  

const toggleCollapse = () => {
    setCollapsed(!collapsed);
  };

 <div style={{
        ...styles.calendarContainer,
        ...(collapsed ? styles.calendarContainerCollapsed : styles.calendarContainerExpanded)
      }}>
        <Calendar
          key={date.toString()}
          onChange={onChange}
          value={date}
          className={`w-full h-auto ${ClockInCalendarStyle.customCalendar}`} // 添加自定义样式类
          locale="zh-CN"
          tileContent={tileContent}
          formatShortWeekday={formatShortWeekday}
        />
</div>

<button
        onClick={toggleCollapse}
        style={collapsed ? { ...styles.collapseButton, ...styles.collapseButtonHover } : styles.collapseButton}
      >
        {collapsed ? '⬆️ 展开' : '⬇️ 收起'}
</button>

结语

以上就是我的方法,如果能对您有些帮助,希望可以点个赞,有任何问题,也欢迎进行交流!!!

0 人点赞