Descriptions

I have to say that adjusting the position of the calendar in the datepicker involved me so much that I had to write a separate package for it! But I still can not claim that it works 100%, if you have a problem with the calendar position, you can open an issue here.

In the early versions, I used the relative position for the datepicker container, which is easy and does not require calculations, but gradually it became clear that the relative position doesn't work, it may be suitable for some layouts, but it doesn't work for complex layouts.

For this reason, in later versions, I changed the position of the container to static, trying to determine the position of the calendar with JavaScript calculations. Computations became more and more complicated and I finally decided to transfer them all to Element Popper package so that it could be used in other projects as well.

Calendar Position

The way of specifying the position of the calendar is that you have to define the two main and relative positions as 'main position-relative position' for the datepicker, the main position has been defined to specify where the calendar should be placed, which You can consider four up, down, left and right values. The relative position is also used to determine where the calendar should be in the main position. Which itself consists of three parts: start, center, and end.

there are all the values you can define for a datepicker as position below:

  • top or top-center
  • top-start or top-left
  • top-end or top-right
  • bottom or bottom-center
  • bottom-start or bottom-left
  • bottom-end or bottom-right
  • left or left-center
  • left-start or left-top
  • left-end or left-bottom
  • right or right-center
  • right-start or right-top
  • right-end or right-bottom

Important Points

  1. The most important thing is that this package only tries to be in the position you specified! This is because the arrangement of the elements containing the date picker may be such that the calendar does not fit in that position, or the user may start scrolling or resizing the page, in which case the calendar position will change. Of course, you can force the date picker to show the calendar position exactly where you want it, in which case you should use the fixMainPosition and fixRelativePosition properties.
  2. Do not use margins and padding to create a space between the calendar and the input, and instead use the offsetY and offsetX properties.
  3. As I said in the descriptions, the relative position doesn't work, so do not try to change the datepicker container position to a relative with CSS or Style, because it will disrupt the calculations.
  4. Set the fixMainPosition to true to fix the main position and the fixRelativePosition to true to fix the relative position.
  5. The default position of the calendar is optimized for the left-to-right direction and its value is bottom-left, so for right-to-left directions that are used in Persian and Arabic languages, you need to use the bottom-right position.
  6. If you want to change the size of the input, please set the box-sizing of the input to border-box.
  7. If the date picker is inside the container with flex position, and flex-direction is column (like the example below), it is better to define the width for datepicker container.
  8. The offsetY value is applied when the main position is top or bottom, and the offsetX value is applied when the main position is left or right.

Example

const containerRef = useRef()
const datePickerRef = useRef()

const [state, setState] = useState({
  mainPosition: "bottom",
  relativePosition: "center",
  fixMainPosition: false,
  fixRelativePosition: false,
  offsetY: 0,
  offsetX: 0
})

const updateState = (key, value) => setState({ 
  ...state, 
  [key]: value
})

useEffect(() => {
  containerRef.current.scrollTo(
    (containerRef.current.clientWidth),
    (containerRef.current.clientHeight / 2)
  )

  datePickerRef.current.openCalendar()
}, [])

const {
  mainPosition,
  relativePosition,
  fixMainPosition,
  fixRelativePosition,
  offsetY,
  offsetX
} = state
.
.
.
<div>
  <div 
    style={{ 
      display: "flex", 
      justifyContent: "space-around",
      flexWrap: "wrap",
      margin: "10px 0" 
    }}
  >
    <label>
      Main Position:
      <select
        value={mainPosition}
        onChange={e => updateState("mainPosition", e.target.value)}
        className="select"
      >
        <option value="top">top</option>
        <option value="bottom">bottom</option>
        <option value="left">left</option>
        <option value="right">right</option>
      </select>
    </label>
    <label>
      <input
        type="checkbox"
        value={fixMainPosition}
        onChange={e => updateState("fixMainPosition", e.target.checked)}
      />
      Fix Main Position
    </label>
    <label>
      Relative Position:
      <select
        value={relativePosition}
        onChange={e => updateState("relativePosition", e.target.value)}
        className="select"
      >
        <option value="start">start</option>
        <option value="center">center</option>
        <option value="end">end</option>
      </select>
    </label>
    <label>
      <input
        type="checkbox"
        value={fixRelativePosition}
        onChange={e => updateState("fixRelativePosition", e.target.checked)}
      />
      Fix Relative Position
    </label>
    <label>
      Offset Y:
      <input
        type="number"
        className="input"
        style={{ width: "30px" }}
        value={offsetY}
        onChange={e => updateState("offsetY", Number(e.target.value))}
      />
    </label>
    <label>
      Offset X:
      <input
        type="number"
        className="input"
        style={{ width: "30px" }}
        value={offsetX}
        onChange={e => updateState("offsetX", Number(e.target.value))}
      />
    </label>
  </div>
  <div
    ref={containerRef}
    style={{ 
      backgroundColor: "whitesmoke", 
      height: "500px", 
      overflow: "auto", 
      position: "relative",
      borderRadius: "5px",
      boxShadow: "inset 0 0 6px 0 #888" 
    }}
  >
    <div
      style={{ 
        width: "300%", 
        height: "200%", 
        display: "flex", 
        flexDirection: "column", 
        justifyContent: "center" 
      }}
    >
      <DatePicker
        ref={datePickerRef}
        containerStyle={{ //datepicker container style
          width: "180px",
          margin: "auto"
        }}
        style={{ //input style
          width: "100%",
          height: "26px",
          boxSizing: "border-box"
        }}
        calendarPosition={`${mainPosition}-${relativePosition}`}
        fixMainPosition={fixMainPosition}
        fixRelativePosition={fixRelativePosition}
        offsetY={offsetY}
        offsetX={offsetX}
        onClose={() => false}
      />
    </div>
  </div>
</div>
,,,,,

Another Examples

There are two other examples that are related to the position of the calendar that you may want to see:

  1. Events #onPositionChange
  2. DatePicker & Calendar Ref #refreshPosition