“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Project source: gitee.com/yang-yiming…

This article implements the add task

Create a file

Create a new AddTask component under TaskList/ Components

index.tsx

Let’s take a look at what variables we need, and their types, because our data is used inside the component, so we don’t need props.

  • First, you need to think about the content.
  • Task time, preferably with create time, and deadline.
  • There are also mouse focus on the input box and not the input box state, to switch styles.
import styles from './index.less';
interface AddTask{
    content:string;
    startTime:string;
    endTime:string;
    clickStatu:boolean;
    showTime: boolean;
}
export default function AddTask() {
  const [task, setTask] = useState<AddTask>({
    content:' '.startTime:' '.endTime:' '.clickStatu:false.showTime: false;
})
  return (
    <div className={styles.task_add}>
      
    </div>
  );
}
Copy the code

Perfect style function

Before clicking

index.tsx

  • Through the task. ClickStatutrue/falseTo switch.
  • We’re using ICONS here,<PlusOutlined> The importimport {PlusOutlined} from '@ant-design/icons'
import styles from './index.less';
import {useState} from 'react';
import {PlusOutlined} from '@ant-design/icons'
import {Input} from 'antd'
interface AddTask{
    content:string;
    startTime:string;
    endTime:string;
    clickStatu:boolean;
}
export default function AddTask() {
const [task, setTask] = useState<AddTask>({
    content:' '.startTime:' '.endTime:' '.clickStatu:false
})
  return (
    <div className={styles.task_add}>
      {
      
          task.clickStatu ? <div>11</div> :
             <div className={styles.task_add_before} onClick={()= >{setTask({... task, clickStatu:true})}}><div className={styles.task_add_before_icon_container}>
                    <PlusOutlined className={styles.task_add_before_icon} /> 
                 </div>
                 <div className={styles.task_add_before_name}>Add tasks</div>
             </div>
      }
    </div>
  );
}
Copy the code

index.less

.task_add {
  box-shadow: 0 0 1px 1px #dddadaad;
  transition: all .4s cubic-bezier(.215.610.355.1);
  background: white;
  margin-bottom: 8px;
  border-radius: 5px;
  margin: 3px 20px 10px 10px;

  &_before {
    height: 50px;
    width: 100%;
    display: flex;

    &_icon_container {
      width: 30px;
      line-height: 50px;
      padding-left: 10px;

      .task_add_before_icon {
        font-size: 20px; 
   
        color: #3372fe;
        cursor: pointer;
      
        :hover {
          font-size: 22px;
          color: #2565ee; }}}&_name {
      color: #ada7a7;
      line-height: 50px;
      padding-left: 10px; }}}Copy the code

We achieved the following effect and clicked to switch to another state (the add content state to be written below).

Click [Add Content]

At the top is an input box, below are today, tomorrow and custom times, as well as create buttons and cancel buttons.Here’s what we achieved

I’ll post the entire code here and then break it down

import styles from './index.less';
import React, { useState, useEffect } from 'react';
import { PlusOutlined, CloseCircleOutlined } from '@ant-design/icons';
import IconFont from '@/public/Icon';
import { Input, Button, Tag, DatePicker, Popover } from 'antd';
import 'moment/locale/zh-cn';
// Time selector bottom English --> Chinese
import locale from 'antd/lib/date-picker/locale/zh_CN';
import moment from 'moment';
interface AddTask {
  content: string;
  startTime: string;
  endTime: string;
  clickStatu: boolean;
  showTime: boolean;
}
export default function AddTask() {
  const [task, setTask] = useState<AddTask>({
    content: ' '.startTime: ' '.endTime: ' '.clickStatu: true.showTime: false});// The time used by the time selector and tag
  const [defaultTime, setDefaultTime] = useState<any>({
    time: ' '.tag: ' '});// Whether to display the time selector when you customize the time
  const [definedDatePickShow, setDefinedDatePick] = useState(false);
  // useRef
  const inputRef = React.useRef<any>(null);
  // Input gets the focus configuration parameter
  const sharedProps = {
    style: { width: '100%' },
    ref: inputRef,
  };
  / / closing Tag
  const closeTag = () = >{ setTask({ ... task,showTime: false });
  };
  // Cancel the task
  const onCancle = () = >{ setTask({ ... task,clickStatu: false });
  };
  // Modify the date in the time picker
  const changeDate = date= > {
    const todayStart = moment(moment().format('YYYY-MM-DD 00:00'));
    const yestardayStart = moment(moment(todayStart).subtract(1.'days'));
    const todayEnd = moment(moment().format('YYYY-MM-DD 24:00'));
    const tomorrowEnd = moment(moment(todayEnd).add(1.'days'));
    console.log(tomorrowEnd, 'tomorrowEndtomorrowEnd');
    let today = ' ';
    if (todayStart < date && date < todayEnd) {
      today = '(today);
    } else if (todayEnd < date && date < tomorrowEnd) {
      today = '(tomorrow);
    } else if (yestardayStart < date && date < todayStart) {
      today = '(yesterday);
    } else {
      today = ` (week${moment(date).day() == 0 ? 'day' : moment(date).day()}) `;
    }
    setDefaultTime({ time: date, tag: moment(date).format(` on DD MM${today} HH:mm`)}); };// Create a task
  const onCreate = () = > {
    console.log(defaultTime);
  };
  // Select today and tomorrow
  const chooseDefaultTime = (day: string) = > {
    if (day == 'today') {
      const time = moment(moment().format('YYYY-MM-DD 18:00'));
      const tag = `${moment().format('M month DD date ')}18:00 ` (today);
      setDefaultTime({ time, tag });
    } else if (day == 'tomorrow') {
      const time = moment(moment().add(1.'days').format('YYYY-MM-DD 18:00'));
      const tag = `${moment().add(1.'days').format('M month DD date ')}18:00 ` (tomorrow);
      setDefaultTime({ time, tag });
    }
    setDefinedDatePick(false);
  };
  useEffect(() = > {
    // Click on the true state to get Input into focustask.clickStatu ? inputRef.current! .focus({cursor: 'start' }) : ' ';
  }, [task.clickStatu]);
  return (
    <div className={styles.task_add}>
      {task.clickStatu ? (
        <div className={styles.task_add_after}>
          <Input {. sharedProps} placeholder="Enter task content" />
          <div className={styles.btn_container}>
            {task.showTime && (
              <div>
                <Popover
                  placement="bottomLeft"
                  title={"'}content={(
                    <DatePicker
                      showTime
                      defaultValue={defaultTime.time}
                      locale={locale}
                      autoFocus
                      onChange={e= > {
                        changeDate(e);
                      }}
                    />
                  )}
                  trigger="click"
                >
                  <Tag closable onClose={closeTag} color="gold" style={{ padding: '5px' }}>
                    {defaultTime.tag}
                  </Tag>
                </Popover>
              </div>)} {! task.showTime && (<div className={styles.time_btn_container}>
                <div
                  className={` ${styles.time_btn} ${definedDatePickShow ? styles.disabled :"'} `}onClick={()= >{ setTask({ ... task, showTime: true }); chooseDefaultTime('today'); }} ><IconFont type="iconjintian" className={styles.icon} />today</div>
                <div
                  className={` ${styles.time_btn} ${definedDatePickShow ? styles.disabled :"'} `}onClick={()= >{ setTask({ ... task, showTime: true }); chooseDefaultTime('tomorrow'); }} ><IconFont type="icona-rili2" className={styles.icon} />tomorrow</div>

                <div
                  className={styles.time_btn}
                  onClick={()= > {
                    setDefinedDatePick(true);
                  }}
                >
                  <IconFont type="icona-rili3" className={styles.icon} />The custom</div>
                {definedDatePickShow ? (
                  <div style={{ height: '30px', lineHeight: '40px', marginLeft: '30px' }}>
                    <DatePicker
                      showTime
                      defaultValue={defaultTime.time}
                      locale={locale}
                      autoFocus
                      style={{ height: '30px', lineHeight: '40px'}}onChange={e= >{ changeDate(e); }} / ><span style={{ marginLeft: '3px', color: '#1890ff' }}>{'}<CloseCircleOutlined
                        onClick={()= >{ setDefinedDatePick(false); }} / ></span>
                  </div>
                ) : (
                  <span />
                )}
              </div>
            )}
            <div className={styles.function_btn_container}>
              <Button onClick={onCancle}>cancel</Button>
              <Button type="primary" className={styles.create_btn} onClick={onCreate}>create</Button>
            </div>
          </div>
        </div>
      ) : (
        <div
          className={styles.task_add_before}
          onClick={()= >{ setTask({ ... task, clickStatu: true }); }} ><div className={styles.task_add_before_icon_container}>
            <PlusOutlined className={styles.task_add_before_icon} />
          </div>
          <div className={styles.task_add_before_name}>Add tasks</div>
        </div>
      )}
    </div>
  );
}

Copy the code
.task_add {
  box-shadow: 0 0 1px 1px #dddadaad;
  transition: all .4s cubic-bezier(.215.610.355.1);
  background: white;
  margin-bottom: 8px;
  border-radius: 5px;
  margin: 3px 20px 10px 10px;

  &_before {
    height: 50px;
    width: 100%;
    display: flex;

    &_icon_container {
      width: 30px;
      line-height: 50px;
      padding-left: 10px;

      .task_add_before_icon {
        font-size: 20px; 
   
        color: #3372fe;
        cursor: pointer;
      
        :hover {
          font-size: 22px;
          color: #2565ee; }}}&_name {
      color: #ada7a7;
      line-height: 50px;
      padding-left: 10px; }}&_after {
    width: 100%;
    padding: 10px;

    .disabled {
      pointer-events: none;
    }

    .btn_container {
      display: flex;
      padding: 8px 0;

      .time_btn_container {
        display: flex;
        flex: 3;
        cursor: pointer;

        .time_btn {
          margin-top: 4px;
          padding: 8px;
          font-size: 12px; 
          background-color: #f5f1f1;
          border-radius: 5px;
          

          &:hover {
            background-color: #cfcbcb ; 
          }

          .icon {
            font-size: 15px;
            margin-right: 3px; }}.time_btn:nth-child(n+2) {
          margin-left: 5px; }}.function_btn_container {
        flex: 2;
        text-align: right;
        line-height: 38px;

        .create_btn {
          margin-left: 10px;
        }
      }
      
    }
  }
}

Copy the code

Input box

When you switch to add content by clicking Add, you will default to the 📌 input box.

Antdesgin’s Input component does just that.

// Import the Input component
import { Input, Button, Tag, DatePicker, Popover } from 'antd';
// useRef can also be introduced in {} and then we don't need the react. useRef directly useRef
import React, { useState, useEffect } from 'react';
// useRef     
const inputRef = React.useRef<any>(null);
// Input gets the focus configuration parameter
const sharedProps = {
   style: { width: '100%' },
   ref: inputRef,
};
// Because this input box does not exist when you click Add task switch state. Direct use of
// inputRef.current! .focus({cursor: 'start'}) returns an error. So we pass in useEffect
// Monitor task.clickStatu changes and assign to true. Cursor: 'start' places the focus on the input box
// Start position, and 'all','end'
useEffect(() = > {
   // Click on the true state to get Input into focustask.clickStatu ? inputRef.current! .focus({cursor: 'start' }) : ' ';
}, [task.clickStatu]);
  <div className={styles.task_add}>{// Data defined above task.clickStatu? (<div className={styles.task_add_after}>// inputRef is obtained in sharedProps<Input  {. sharedProps} placeholder='Enter task content' />
           </div>)
           : (
          <div
            className={styles.task_add_before}
            onClick={()= >{ setTask({ ... task, clickStatu: true }); }} ><div className={styles.task_add_before_icon_container}>
              <PlusOutlined className={styles.task_add_before_icon} />
            </div>
            <div className={styles.task_add_before_name}>Add tasks</div>
          </div>
        )
           
Copy the code
.task_add {
  box-shadow: 0 0 1px 1px #dddadaad;
  transition: all .4s cubic-bezier(.215.610.355.1);
  background: white;
  margin-bottom: 8px;
  border-radius: 5px;
  margin: 3px 20px 10px 10px;
   
   &_before{... }// & stands for task_add
   &_after{
      width: 100%;
      padding: 10px; }}Copy the code

Use of ICONS

We used Alibaba’s iconfont icon, which you can see in my previous article✈ ️

Because the ICONS we use are custom ICONS, they are best encapsulated as a component. I created a new public folder under SRC to hold the Icon component

Create a new index. TSX under Icon

import { createFromIconfontCN } from '@ant-design/icons';
const IconFont = createFromIconfontCN({
  scriptUrl: [ 
    // This address needs to be changed every time the icon is updated
    '//at.alicdn.com/t/font_2503482_lgnt38a7d1f.js']});export default IconFont
Copy the code

It is then referenced in our AddTask component

import IconFont from '@/public/Icon';
// type corresponds to the content of the iconfont official website copy code
<IconFont type="iconjintian" className={styles.icon} />
Copy the code

At the bottom of the layout

A CALSS is definedtime_btn_containerTo store everything at the bottom. The child elements aretime_btn_containerfunction_btn_container.time_btn_containerI’m also going to be a div, class nametime_btn

  &_after {
    width: 100%;
    padding: 10px;
    .btn_container {
      display: flex;
      padding: 8px 0;
      .time_btn_container {
        display: flex;
        flex: 3;
        cursor: pointer;

        .time_btn {
          margin-top: 4px;
          padding: 8px;
          font-size: 12px; 
          background-color: #f5f1f1;
          border-radius: 5px;
   
          &:hover {
            background-color: #cfcbcb ; 
          }

          .icon {
            font-size: 15px;
            margin-right: 3px;
           
          }
        }

        .time_btn:nth-child(n+2) {
          margin-left: 5px;
        }
       
      }

      .function_btn_container {
        flex: 2; text-align: right; line-height: 38px; .create_btn { margin-left: 10px; }}}}Copy the code

Click today to switch to Tag

The default time after clicking today is 18:00 and replace these buttons with CloseTag

Toggle today, tomorrow, custom buttons and tags. Control by task.showTime value

  • Task. showTime is true to display the Tag.

  • Task. showTime is false to display today, tomorrow, custom buttons.

writing

{task.showTime && (<div>Tag</div> ) }
{!task.showTime && (<div>Today tomorrow button</div>)}Copy the code

This is today’s button. The onClick event sets task.showtime to true. Also fires the chooseDefaultTime(‘today’) function.

   <div
     className={styles.time_btn}
     onClick={() = >{ setTask({ ... task,showTime: true });
            chooseDefaultTime('today'); }} ><IconFont type="iconjintian" className={styles.icon} />Today < / div >Copy the code

ChooseDefaultTime function

Here we use a new variable defaultTime, which is the moment object used by the time picker DatePicker. Tag is the time format of the tag. By the way, the time picker is used for moment and we also need to import moment

  // The time used by the time selector and tag
  const [defaultTime, setDefaultTime] = useState<any>({
    time: ' '.tag: ' '});// Select today and tomorrow
  const chooseDefaultTime = (day: string) = > {
     // Click today
    if (day == 'today') {
       // The default is 18:00
      const time = moment(moment().format('YYYY-MM-DD 18:00'));
      // Default 18:00 today shows today, tomorrow, yesterday and other days of the week
      const tag = `${moment().format('M month DD date ')}18:00 ` (today);
      setDefaultTime({ time, tag });
      // Click the tomorrow button
    } else if (day == 'tomorrow') {
    
      const time = moment(moment().add(1.'days').format('YYYY-MM-DD 18:00'));
      const tag = `${moment().add(1.'days').format('M month DD date ')}18:00 ` (tomorrow);
      setDefaultTime({ time, tag });
    }
    setDefinedDatePick(false)};Copy the code

closeTag

// Closable turns the tag color onClose closes the function
  <Tag closable onClose={closeTag} color="gold" style={{ padding: '5px' }}>
       {defaultTime.tag}
  </Tag>
Copy the code

Click Tag to bring up the time picker

The Popover component is used here. Click on the Tag to display the Popover, just write the Tag inside the Popover.

Here again, the time picker is in English, not Chinese. You need to import this and add locale={locale} to DatePicker

import 'moment/locale/zh-cn';
// Time selector bottom English --> Chinese
import locale from 'antd/lib/date-picker/locale/zh_CN';
Copy the code
      <Popover
        // The shell position
        placement="bottomLeft"
        // The title is empty
        title={' '}
        // The shell content here is the time selector
        content={(
           <DatePicker
            showTime
            defaultValue={defaultTime.time}
            locale={locale}
            autoFocus
            onChange={e= >{ changeDate(e); }} / >
            )}
         // Trigger mode
         trigger="click"
        >
          <Tag closable onClose={closeTag} color="gold" style={{ padding: '5px' }}>
              {defaultTime.tag}
          </Tag>
      </Popover>
Copy the code

Change the time picker to re-render the Tag

That’s changeDate(e) in the time selector; function

  • We want today, tomorrow, yesterday and the day of the week

  • The comparison time frame is judged to be today…. Moment object
  • For comparison, several time periods are needed: 00:00 today, 24:00 today, 00:00 yesterday, and 24:00 tomorrow.
  • Subtract is a way for moment to gain the first few days
  • Add is a way for moment to get the next few days
  // Modify the date in the time picker
  const changeDate = date= > {
    const todayStart = moment(moment().format('YYYY-MM-DD 00:00'));
    const yestardayStart = moment(moment(todayStart).subtract(1.'days'));
    const todayEnd = moment(moment().format('YYYY-MM-DD 24:00'));
    const tomorrowEnd = moment(moment(todayEnd).add(1.'days'));
  
    let today = ' ';
    if (todayStart < date && date < todayEnd) {
      today = '(today);
    } else if (todayEnd < date && date < tomorrowEnd) {
      today = '(tomorrow);
    } else if (yestardayStart < date && date < todayStart) {
      today = '(yesterday);
    } else {
      today = ` (week${moment(date).day() == 0 ? 'day' : moment(date).day()}) `;
    }
    setDefaultTime({ time: date, tag: moment(date).format(` on DD MM${today} HH:mm`)}); };Copy the code

Custom time

We write another time picker to the right of it and control whether it is displayed by the variable definedDatePickShow. At the same time when the custom time selector appears today, tomorrow button can not be clicked

  <div className={styles.time_btn}  
  onClick={() = > {
           setDefinedDatePick(true); }} ><IconFont
               type="icona-rili3"
               className={styles.icon}
                />Custom < / div >Copy the code
{definedDatePickShow ? (
           <div style={{height:'30px',lineHeight:'40px',marginLeft:'30px'}} >
                <DatePicker
                      showTime
                      defaultValue={defaultTime.time}
                      locale={locale}
                      autoFocus
                      style={{height:'30px',lineHeight:'40px'}}
                      onChange={e= >{ changeDate(e); }} / ><span style={{marginLeft:'3px',color:'#1890ff'}} > <CloseCircleOutlined onClick={()= >{
                       setDefinedDatePick(false) 
                    }} /></span>
                   
                    </div>
                  ) : (
                    <span />
          )}
Copy the code