2023-09-25

Grid column span to take full width when remaining columns are empty

I'm working on a calendar element whereby the day events are utilising grid row to span over their specified time frame.

However, I seem to be struggling when it comes to setting the event widths. By default they will take up the full width of the day when it's the only event at that time. And if they are concurring events at any point they will reduce their widths accordingly so they can all fit.

My problem occurs when I have a group of events happening at the same time, which generates multiple columns, and then an event later in the day which doesn't have any coinciding events. This event is now limited to the widths of the columns created from the events earlier in the day.

See image for context: Image of annotated demo

See below for a working demo.

(function() {

  function init() {
    let calendarElement = document.getElementsByClassName('calendar')[0],
      events = calendarElement.getElementsByClassName('calendar__event');
    _positionEventsOnGrid(events);
  }

  function _positionEventsOnGrid(events) {
    Array.from(events).forEach(event => {
      let gridRow = event.getAttribute('data-grid-row');

      if (gridRow) {
        let gridRowSpan = event.getAttribute('data-grid-row-span');

        event.style.gridRow = gridRowSpan ? `${gridRow} / span ${gridRowSpan}` : gridRow;
      }
    });
  }

  init();
})();
body {
  margin: 0;
  block-size: 100vh;
  inline-size: 100vw;
}

.calendar {
  $block: &;
  inline-size: 100%;
  block-size: 100%;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  grid-template-rows: auto 1fr;
}

.calendar__dayNames {
  grid-row: 1;
  grid-column: 2;
  display: flex;
}

.calendar__dayName {
  flex-grow: 1;
  flex-basis: 0;
}

.calendar__schedule {
  grid-row: 2;
  grid-column: 1/span 2;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  border-block-start: 1px solid #000;
}

.calendar__timeline {
  display: grid;
  grid-template-rows: repeat(19, 1.375rem);
  gap: 0 0.625rem;
  grid-column: 1/ span 2;
  grid-row: 1;
  position: relative;
}

.calendar__timelineItem {
  display: flex;
  align-items: center;
  border-block-end: 1px dotted #222;
}

.calendar__timelineItem:nth-child(even) {
  border-block-end: 1px solid #000;
}

.calendar__timelineItem:nth-child(odd):after {
  display: inline;
  content: attr(data-time);
}

.calendar__dayEventsContainer {
  position: relative;
  grid-row: 1;
  grid-column: 2;
  display: flex;
}

.calendar__dayEvents {
  display: grid;
  grid-template-rows: repeat(19, 1.375rem);
  gap: 0 0.625rem;
  border-inline-start: 1px solid #000;
  flex-grow: 1;
  flex-basis: 0;
}

.calendar__event {
  background-color: #346DA8;
  border: none;
}
<div class="calendar">
  <div class="calendar__dayNames">
    <div class="calendar__dayName">Monday</div>
    <div class="calendar__dayName">Tuesday</div>
    <div class="calendar__dayName">Wednesday</div>
    <div class="calendar__dayName">Thursday</div>
    <div class="calendar__dayName">Friday</div>
  </div>
  <div class="calendar__schedule">
    <div class="calendar__timeline">
      <div class="calendar__timelineItem" data-time="09:00"></div>
      <div class="calendar__timelineItem" data-time="09:15"></div>
      <div class="calendar__timelineItem" data-time="09:30"></div>
      <div class="calendar__timelineItem" data-time="09:45"></div>
      <div class="calendar__timelineItem" data-time="10:00"></div>
      <div class="calendar__timelineItem" data-time="10:15"></div>
      <div class="calendar__timelineItem" data-time="10:30"></div>
      <div class="calendar__timelineItem" data-time="10:45"></div>
      <div class="calendar__timelineItem" data-time="11:00"></div>
      <div class="calendar__timelineItem" data-time="11:15"></div>
      <div class="calendar__timelineItem" data-time="11:30"></div>
      <div class="calendar__timelineItem" data-time="11:45"></div>
      <div class="calendar__timelineItem" data-time="12:00"></div>
      <div class="calendar__timelineItem" data-time="12:15"></div>
      <div class="calendar__timelineItem" data-time="12:30"></div>
      <div class="calendar__timelineItem" data-time="12:45"></div>
      <div class="calendar__timelineItem" data-time="13:00"></div>
      <div class="calendar__timelineItem" data-time="13:15"></div>
      <div class="calendar__timelineItem" data-time="13:30"></div>

    </div>
    <div class="calendar__dayEventsContainer">
      <div class="calendar__dayEvents">

        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="6"></button>
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="3"></button>
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="1"></button>
        <button type="button" class="calendar__event" data-grid-row="10" data-grid-row-span="1"></button>
      </div>
      <div class="calendar__dayEvents">
      </div>
      <div class="calendar__dayEvents">
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="6"></button>
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="1"></button>
      </div>
      <div class="calendar__dayEvents">

      </div>
      <div class="calendar__dayEvents">
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="1"></button>
      </div>
    </div>
  </div>
</div>

I have tried a few things: grid-column: 1/-1 but this will do it to all the events and therefore incorrectly format the concurrent events.

grid-auto-flow: dense however, this didn't seem to do the trick in my example.

grid-template-columns: repeat(auto-fit, minmax()), but the min value as part of the minmax needs to be a fixed size.

I do wonder whether I need to extend the positionEventsOnGrid() function so it checks whether 'grid-column: 1/-1' can be applied to specific events. If it isn't coinciding with other events at that time. However, I feel this might be quite complex so thought I'd ask around in case there was a simpler way.

I'm wanting singular events to span the full width of their day, regardless of whether there are concurrent events earlier or later in the day.



No comments:

Post a Comment