当剩余列为空时,网格列跨度将采用全宽

问题描述 投票:0回答:1

我正在开发一个日历元素,其中当天的事件利用网格行跨越其指定的时间范围。

但是,在设置事件宽度时我似乎遇到了困难。默认情况下,当它是当时唯一的事件时,它们将占据一天的整个宽度。如果它们在任何时候同时发生事件,它们都会相应地减小宽度,以便它们都能适合。

当我同时发生一组事件时,就会出现问题,这会生成多个列,然后在当天晚些时候发生一个没有任何同时发生的事件的事件。此事件现在仅限于根据当天早些时候的事件创建的列的宽度。

查看图片了解上下文: Image of annotated 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>

我尝试过一些事情: grid-column: 1/-1 但这将对所有事件执行此操作,因此会错误地格式化并发事件。

grid-auto-flow:密集但是,这在我的示例中似乎并没有起到作用。

grid-template-columns:repeat(auto-fit, minmax()),但作为minmax一部分的最小值需要是固定大小。

我确实想知道是否需要扩展positionEventsOnGrid()函数,以便它检查“grid-column:1/-1”是否可以应用于特定事件。如果当时没有发生其他事件的话。然而,我觉得这可能相当复杂,所以我想问问周围是否有更简单的方法。

我希望单一事件能够跨越一天的整个宽度,无论当天早些时候或晚些时候是否有并发事件。

javascript css css-grid
1个回答
0
投票

解决方案中的网格列是隐式生成的,因为您的 CSS 没有显式设置列布局。请参阅@Michael Benjamin 的回答此处

问题在于数字索引 (1 / -1) 仅适用于显式网格。换句话说,具有定义的列和行的网格。您正在使用隐式网格,其中会根据需要自动生成列和行。

如果您将

event.style.gridColumn = 1 / -1;

 设置为 
.calendar__event
 容器的 CSS 规则,则应用于 
grid-template-columns: repeat()
 元素的 
.calendar__dayEvents
会延伸到所有列。这类似于您的评论“作为 minmax 一部分的最小值需要是固定大小。”

重叠和非重叠事件

我确实想知道是否需要扩展positionEventsOnGrid()函数,以便它检查“grid-column:1/-1”是否可以应用于特定事件。如果当时没有发生其他事件的话。然而,我觉得这可能相当复杂,所以我想问问周围是否有更简单的方法。

我还没有找到解决这个问题的方法。幸运的是,SO 上现有的解决方案可以“对重叠时间或整数间隔进行分组”。在我的日历组件中,我应用/改编了我在链接的 SO 帖子上发布的 GitHub 功能。该调整应用于下面的代码片段。 以下代码片段执行以下操作:

维护您的 HTML,但包括周二和周四的其他活动。
  • 维护您的 CSS,但包含 CSS 规则,以使用自定义
  • data-type
  • 属性设置事件的背景颜色。
    扩展您的 JS。
  • 一般

对每个事件日容器的重叠/非重叠事件进行分组。
  • 每天的活动项目均使用
  • event.style.gridRow
  • 进行布置。
    每天的事件网格容器中隐式生成的网格列的数量是使用内置 
  • window
  • 函数
    getComputedStyle()
    检索的,如本 SO 帖子所提供:
    count columns in a Responsible grid
    将列数值应用到每天的
  • .calendar__dayEvents
  • 网格容器 并将不重叠的事件项设置为
    event.style.gridColumn = 1 / -1;
    或者,可以将列数值设置为每个不重叠的事件项
  • event.style.gridColumn = 1 / ${};
  • 并且
    grid-template-columns
    不需要在事件网格上设置 容器。 (请参阅代码片段中的注释。)
    
    

(function() { function init() { //const calendarElement = document.getElementsByClassName('calendar')[0]; //const events = calendarElement.getElementsByClassName('calendar__event'); //_positionEventsOnGrid(events); const dayEventsElems = document.querySelectorAll('.calendar__dayEvents'); _positionEventsOnGrid2(dayEventsElems); } /* 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; } }); } */ function _positionEventsOnGrid2(dayEventsElems) { if (!dayEventsElems) { return; } dayEventsElems.forEach(dayEventElem => { const events = dayEventElem.querySelectorAll('.calendar__event'); // if there are no event elements in the // '.calendar__dayEvents' element then exit function. if (events.length === 0) { return; } // Grid layout using "implicit grid, where columns and rows // are automatically generated, as needed." // Source: https://stackoverflow.com/a/75250349 // Also see: https://stackoverflow.com/a/73624201 const overlappingGroups = getOverlappingEventGroups(events); overlappingGroups.forEach(group => { group.forEach(event => { event.style.gridRow = `${event.startRow} / ${event.endRow}`; }); }); // After the grid is laid out, count the number of columns // created using the built-in 'gridComputedStyle()'. // See the function 'getGridRowColCount()' below. const gridElem = events[0].closest('.calendar__dayEvents'); // https://www.javascripttutorial.net/javascript-return-multiple-values const [gridRowCount, gridColumnCount] = getGridRowColCount(gridElem); gridElem.style.gridTemplateColumns = `repeat(${gridColumnCount}, 1fr)`; overlappingGroups.forEach(group => { // If the event group does not contain one entry, // then exit function. if (group.length !== 1) { return; } // Otherwise, explicitly set the 'gridColumn' // CSS style property to '1 / -1' so that the event // element spans all columns in the 'gridElem' grid. group[0].style.gridColumn = `1 / -1`; // Or set the span value and remove the code above that // sets the CSS property 'gridTemplateColumns'. //group[0].style.gridColumn = `1 / span ${gridColumnCount}`; }); }); } // Source: https://stackoverflow.com/questions/55204205/a-way-to-count-columns-in-a-responsive-grid function getGridRowColCount(grid) { const gridComputedStyle = window.getComputedStyle(grid); // get number of grid rows const gridRowCount = gridComputedStyle.getPropertyValue("grid-template-rows").split(" ").length; // get number of grid columns const gridColumnCount = gridComputedStyle.getPropertyValue("grid-template-columns").split(" ").length; //console.log(gridRowCount, gridColumnCount); // https://www.javascripttutorial.net/javascript-return-multiple-values return [gridRowCount, gridColumnCount]; } /* https://stackoverflow.com/a/75486209 Format of intervals array from: https://gist.githubusercontent.com/blasten/acfaafc8247e37abf23f/raw/c71f42428e34dbb7ff2d1b5e932fdf4152fe1ad2/group-intervals.js const intervals = [ [2, 5], [5, 6], [3, 4], [7, 8], [6.5, 9], [10, 11.5] ]; The function 'getOverlappingEventGroups()' returns a modified intervals array format containing '.calendar__event' DOM elements with custom 'startRow' and 'endRow' properties. */ function getOverlappingEventGroups(events) { const eventIntervals = []; events.forEach(event => { const gridRow = event.getAttribute('data-grid-row'); const gridRowSpan = event.getAttribute('data-grid-row-span'); if (isNaN(gridRow) || isNaN(gridRowSpan)) { return; } const startRow = parseInt(gridRow, 10); const endRow = startRow + parseInt(gridRowSpan, 10); event.startRow = startRow; event.endRow = endRow; eventIntervals.push(event); }); const eventGroups = groupOverlapingEventIntervals(eventIntervals); //console.log("Overlapping event groups: ", eventGroups); return eventGroups; } // Source: https://gist.githubusercontent.com/blasten/acfaafc8247e37abf23f/raw/c71f42428e34dbb7ff2d1b5e932fdf4152fe1ad2/group-intervals.js function groupOverlapingEventIntervals(eventIntervals) { eventIntervals.sort((a, b) => a.startRow - b.startRow); const groups = [ [eventIntervals[0]] ]; let j = 0; let end = eventIntervals[0].endRow; for (let i = 1; i < eventIntervals.length; i++) { if (eventIntervals[i].startRow <= end) { if (eventIntervals[i].endRow > end) { end = eventIntervals[i].endRow; } groups[j].push(eventIntervals[i]); } else { groups.push([eventIntervals[i]]); j++; end = eventIntervals[i].endRow; } } return groups; } init(); })();
body {
  margin: 0;
  block-size: 100vh;
  inline-size: 100vw;
}

.calendar {
  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;
}


/* Color-coded event elements */

.calendar__event[data-type=aaa] {
  background-color: rgb(243 165 97 / 80%);
}

.calendar__event[data-type=bbb] {
  background-color: rgb(80 142 77 / 80%);
}

.calendar__event[data-type=ccc] {
  background-color: rgb(228 53 53 / 80%);
}

.calendar__event[data-type=ddd] {
  background-color: rgb(96 177 218 / 80%);
}
<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">
        <button type="button" class="calendar__event" data-grid-row="3" data-grid-row-span="7" data-type="bbb"></button>
        <button type="button" class="calendar__event" data-grid-row="12" data-grid-row-span="3" data-type="aaa"></button>
        <button type="button" class="calendar__event" data-grid-row="14" data-grid-row-span="2" data-type="ccc"></button>
      </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">
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="6" data-type="aaa"></button>
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="3" data-type="bbb"></button>
        <button type="button" class="calendar__event" data-grid-row="1" data-grid-row-span="1" data-type="aaa"></button>
        <button type="button" class="calendar__event" data-grid-row="2" data-grid-row-span="1" data-type="ccc"></button>
        <button type="button" class="calendar__event" data-grid-row="2" data-grid-row-span="4" data-type="bbb"></button>
        <button type="button" class="calendar__event" data-grid-row="10" data-grid-row-span="1" data-type="ddd"></button>
        <button type="button" class="calendar__event" data-grid-row="14" data-grid-row-span="3" data-type="ddd"></button>
      </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>

© www.soinside.com 2019 - 2024. All rights reserved.