Copyright notice: This article is the blogger’s original article, shall not be reproduced without the permission of the blogger. Please contact the author by leaving a comment at the bottom of the article.

The background,

In the actual development process, encountered an interesting small function, here to share the implementation process. It is mainly realized through the SLIDER component of ANTD, and a range is marked with parentheses for display. As shown below:

What we want to achieve is:

  • Dynamically displays the time nodes given by the time data
  • If the current time is between two time nodes, the progress is between nodes
  • You cannot drag the Slider manually.

Click to view the full demo code

Second, function realization

1. Data preparation

First of all, our time data is dynamically acquired, and the time order may not be sorted, so we need to process the acquired time. The fake data format after processing is shown below:

// For example, we get the following time data
// data.js
export const timeArray = [
  {
    time: "2022-03-01".type: "start" // Mark the beginning of the parenthesis range
  },
  {
    time: "2022-03-10"
  },
  {
    time: "2022-02-26"
  },
  {
    time: "2022-06-07"
  },
  {
    time: "2022-05-08"}];Copy the code

2. Use the Slider

Check the API for ANTD’s slider and you can see that we need the following parameters:

  • Marks scale
  • Step Indicates the step size. The value must be greater than 0
  • Value Sets the current value
  • If disable is true, the slider is disabled
  • When tooltipVisible is true, Tooltip will always be displayed; Otherwise, it is never displayed, even when dragging and moving
import { Slider } from "antd"; . <Slider marks={marks}// Scale marks
    step={step}   / / step length
    value={value} // Current value
    disabled      // When the value is true, the slider is disabled
    tooltipVisible={false}  // Never display tooltip
/>
Copy the code

3. Handle Slider parameters

We need to define a method called getParam to handle the parameters required by the component Slider.

First deal with the problem of dynamic display of time nodes:

1) Define marks to store the node that handles the slider.

// utils.js
import { timeArray } from "./data.js";

export const getParam = () = > {
  const marks = {}; // Label object on slider. }Copy the code

The marks object is in the format {number: ReactNode} or {number: {style: CSSProperties, label: ReactNode}}.

// antd demo display
const marks = { 
    0: '0 ° C'.26: '26 ° C'.37: '37 ° C'.100: { style: { color: '#f50',},label: <strong>100 ° c.</strong>,}};Copy the code

Therefore, we need to get the data result as shown in demo after processing.

2) timeArray Sorts the time by sort to ensure that the time order is correct

import moment from "moment"; .// Sort the time array
const newTimeArray = timeArray.sort((a, b) = >
    moment(a.time).isSameOrAfter(b.time) ? 1 : -1
);
Copy the code

3) Dynamically calculate node number value and node step according to the number of time nodes in the array.

For example, if there are four time nodes in the current array, the marks object’s 0 and 100 will each occupy two nodes, and the remaining two nodes will be divided equally between 0 and 100. So 0 to 100 is divided into 3 equal parts, and the length of each part is the nmark that we want to calculate.

Since point 2 of our requirement mentioned that if the current time is between two time nodes, the display progress is between nodes, so our step size nstep is actually half of the previous nmark length.

 // Compute node positions according to time nodes
 let nmark =
    newTimeArray.length > 1 
    ? (100 / (newTimeArray.length - 1)).toFixed(2)
    : 0;
    
  // The intermediate value between the two nodes is the step size, indicating the state in the two nodes
  let nstep = (nmark / 2).toFixed(2);
Copy the code

4) Scale the marks object

  • Once we have calculated the length and step size of each piece, we need to scale the marks object for each node displayed in the data through the traversal array.

  • Compare it to today’s date and record what value we should display at the moment.

    • If today’s date is exactly equal to a marked date, value is the graduated value marked with the date
    • If today’s date is between two scales (for example, scales 1 and 2), then value is the scale value between the two scales (this is why nstep is half of nmark), i.e.Value = mark value of scale 1 + nstepIs exactly between the scale 1 and 2
  let sliderValue = 0; // Record the current timeline value

  // Iterate over the sorted time
  newTimeArray.forEach((item, i) = > {
    // Array length
    let length = newTimeArray.length;
    // Is the last node
    let islast = length > 1 ? i === length - 1 : false;
    
    // Calculate the time mark position between 0 and 100
    let markNum = (i * (nmark * 100)) / 100;

    // Today's date
    let todayTime = moment(moment().format("YYYY-MM-DD"));

    // If the time is today, the time node is the mark value of the equal time
    if (moment(item.time).isSame(todayTime)) {
      sliderValue = Number(markNum);
    }
    // If the time of the day is after a time node, the mark value is between this node and the next node, so add step value
    else if (todayTime.isAfter(moment(item.time))) {
      sliderValue = (Number(markNum) * 100 + Number(nstep) * 100) / 100;
    }

    // The scale marks the Marks object
    marks[islast ? 100 : markNum] = {
      label: item.time }; . });return {
    timeArr: newTimeArray,
    marks,
    step: nstep,
    sliderValue,
  };
}
Copy the code

4. Implementation of braces

1) Parameter acquisition processing

This is easier to do, as we display the distance between the two nodes by default. So add a type to the time node to display, in this case type = “start”

{
    time: "2022-03-01".type: "start" // Mark the beginning of the parenthesis range
},
Copy the code

Then, when traversing the array in step 4 of step 3 above, do something about where the braces are to be displayed.

const parenthesisArr = []; / / braces

// Iterate over the sorted time
newTimeArray.forEach((item, i) = >{...// Calculate the time mark position between 0 and 100
    let markNum = (i * (nmark * 100)) / 100;

    // ------ handle curly brace display ------//
    // Record the start node
    if (item.type === "start") {
        parenthesisArr.push({
            name: "Display spacing".left: markNum // The distance to the left of the braces}); }... });Copy the code

Then the bracketed component is processed

import Parenthesis from "./comp/parenthesis.js";
import { getParam } from "./utils"; .const [value, set_value] = useState(0);  / / the current value
 const [marks, set_marks] = useState({});  / / tag
 const [step, set_step] = useState(null); / / step length
 const [parenthesisArr, set_parenthesisArr] = useState([]); // Displays an array of braces

 useEffect(() = > {
    letparam = getParam(); set_marks(param.marks); set_step(param.step); set_value(param.sliderValue); set_parenthesisArr(param.parenthesisArr); } []); . {parenthesisArr.map((item, i) = > {
    return (
        <Parenthesis
            key={i}
            data={item}
            step={Number(step) * 2} // By default, the spacing between adjacent nodes is displayedday={item.name}
        />
    );
})}
Copy the code

2) Style implementation of braces

The width of the braces is set primarily by the step size, with left being the distance moved to the left.

// parenthesis.js

import "./index.css";

const Parenthesis = (props) = > {
  let { data, step, day } = props;

  return (
    <div
      className="parenthesis-box"// Set the width of the braces according to the step sizestyle={{ left:` ${data.left} % `,width:` ${step`}}} % >
      <div className="parenthesis-inner">
        <div className="parenthesis">
          <div className="parenthesis-text">{day}</div>
        </div>
      </div>
    </div>
  );
};

export default Parenthesis;
Copy the code

CSS styles are as follows: mainly implemented using before and after pseudo-classes.

/*index.css*/

.parenthesis-box {
  position: absolute;
  top: 40px;
}

/* Display prompt text */
.parenthesis-text {
  position: relative;
  top: -40px;
}

/* Curly brace style */
.parenthesis-inner {
  position: relative;
  width: 100%;
  height: 40px;
}
.parenthesis-inner::before..parenthesis-inner::after..parenthesis::before..parenthesis::after {
  content: "";
  display: block;
  position: absolute;
  width: calc(50% - 20px);
  height: 20px;
  border-style: solid;
  border-color: rgb(9.113.241);
  border-width: 0;
}
.parenthesis::before..parenthesis::after {
  top: 0;
  border-top-width: 1px;
}
.parenthesis-inner::before..parenthesis-inner::after {
  top: -19px;
  border-bottom-width: 1px;
}
.parenthesis::before {
  left: 0;
  border-top-left-radius: 20px;
}
.parenthesis::after {
  right: 0;
  border-top-right-radius: 20px;
}
.parenthesis-inner::before {
  left: 20px;
  border-bottom-right-radius: 20px;
}
.parenthesis-inner::after {
  right: 20px;
  border-bottom-left-radius: 20px;
}
Copy the code

Third, refer to the article

  • Css3 parallelogram, parentheses
  • antd Slider