Здание CSS Сетка с N столбцами, 2 ряда, максимальная высота контейнера - PullRequest
0 голосов
/ 19 февраля 2020

Новое в Grid в CSS и, конечно, начиная с более запутанной потребности.

Подумайте об отображении прибытия / вылета аэропорта. Нет клавиатуры, нет мыши, нет человеческого взаимодействия. Это приложение выноски раньше было одним большим длинным списком прокрутки. Я превращаю это в немного более организованный макет. В зависимости от того, на каком экране он наконец появляется, может быть место для 2 столбцов, 3 столбцов или даже 5 столбцов - где каждый столбец имеет одинаковую ширину и минимальную ширину.

1-я строка: заголовок / полный ширина / 3 строки текста
2-й ряд: должен быть равным оставшейся высоте, полной ширине и является контейнером
В контейнере:

  • 2 строки (строка заголовка, строка содержимого)
  • N столбцов (где столбец имеет минимальную ширину) -> также панель просмотра текста в выделенном поле (вертикальная прокрутка с помощью js)
@model wsGT4.Models.DictionaryResultSet<string, List<wsGT4.Models.Callout>>
@{
    ViewBag.Title = "Callout";
    Layout = "~/Views/Shared/_EmptyLayout.cshtml";
}


<section class="header">
    <H1>TOMORROW'S CALLOUTS WILL DISPLAY STARTING AT 5PM</H1>
    <H2>BRING DOCS TO CALLOUTS</H2>
    @if (Model.Success == false)
    {
        foreach (var msg in Model.Messages)
        {
            <h2>@msg</h2>
        }
    }
    else
    {
        if (DateTime.Now.AddHours(Model.TimezoneOffset).Hour >= 17)
        {
            <h2>CALLOUTS FOR <span style="color:red; background-color: yellow;">TOMORROW</span> - @DateTime.Now.AddDays(1).ToString("MMM dd") <span style="font-size:.6em">(@DateTime.Now.AddHours(Model.TimezoneOffset).ToString("HH:mm"))</span></h2>
        }
        else
        {
            <h2>CALLOUTS FOR TODAY - @DateTime.Now.ToString("MMM dd") <span style="font-size:.6em">(@DateTime.Now.AddHours(Model.TimezoneOffset).ToString("HH:mm"))</span></h2>
        }
    }
</section>
<section class="container">
    @foreach (var kvp in Model.ResultList.OrderBy(a => a.Key))
    {
        <div class="timeDisplay">
            &gt;&gt;&gt; @kvp.Key
        </div>
        <div class="viewPort">
            <div class="textList">
                @foreach (var subject in kvp.Value.OrderBy(a => a.LastName).ThenBy(a => a.SubjectId))
                {
                    <span>@subject.LastName</span>
                    <span>#@subject.SubjectId</span>
                    <span>@subject.EventTitle</span>
                }
            </div>
        </div>
    }
</section>

CSS (который я по-королевски облажаю)

.header {
    padding: 5px;
    border: groove;
    border-bottom-color: black;
    border-width: 2px;

    /* Grid styles */
    display: grid;
    align-items: center;
    /*grid-template-columns: 1fr;*/
    grid-template-rows: repeat(3, 1fr);
}
.container {
    max-height: 100vh;

    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 30px auto-fill;
    grid-gap: 5px;
}
.timeDisplay{
    max-height: 30px;
}
.viewPort {
    height: 100%;
    overflow: hidden;
}
.textList {
    height: 100%;
    font-size: 125%;
    display: grid;
    grid-template-columns: 3fr 2fr 5fr;
}

Поскольку возвращение коллекции моделей может иметь неизвестное количество наборов времени / списка, я пытаюсь позволить автоматизации сделать некоторые из работа. Идея заключается в том, что если у меня больше столбцов, чем может поместиться, то они либо (A) не отображаются, либо (B) переносятся под нижнюю часть экрана, фактически скрываясь.

1 Ответ

0 голосов
/ 20 февраля 2020

Так что я немного изменил свой подход после погружения в flex против grid в CSS. container был переписан следующим образом:

<section class="wrapper">
    <ul class="colList">
        @foreach (var kvp in Model.ResultList.OrderBy(a => a.Key))
        {
            <li class="columnItem">
                <div class="columnTitle--wrapper">
                    <h2>&gt;&gt;&gt; @kvp.Key</h2>
                </div>
                <div class="cardViewer">
                    <div class="cardList">
                        <ul class="cardList--ul">
                            @foreach (var subject in kvp.Value.OrderBy(a => a.EventTitle).ThenBy(a => a.LastName).ThenBy(a => a.SubjectId))
                            {
                                <li class="cardItem">
                                    <h3>@subject.EventTitle.ToUpper()</h3>
                                    <h3>@subject.LastName.ToUpper()</h3>
                                    <h3>#@subject.SubjectId.ToUpper()</h3>
                                </li>
                            }
                        </ul>
                    </div>
                </div>
            </li>
        }
    </ul>
</section>

и CSS превратился в:

/* column styles */
.wrapper {
}

.colList {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
    grid-gap: .5rem;
    align-items: start;
    top: 500vh;
}

.columnItem {
    border-radius: .2rem;
    padding: .5rem;
}

.columnTitle--wrapper {
}

    .columnTitle--wrapper h2 {
        font-weight: 700;
    }

/* card styles */
.cardViewer {
    overflow: hidden;
    height: 80vh;
    max-height: 80vh;
}

.cardList {
    position: relative;
}

.cardList--ul {
    display: grid;
    grid-template-rows: auto;
    grid-gap: .5rem;
    margin: .5rem 0;
}

.cardItem {
    background-color: white;
    border: 1px solid #BBB;
    border-radius: .25rem;
    box-shadow: 0 1px 0 rgba(9,45,66,.25);
    padding: .5rem;

    display: grid;
    grid-template-columns: 5fr 7fr 2fr;
}

И я добавил JS для обработки нескольких выделенных областей при необходимости (список был слишком долго для области просмотра):

class marqueeInfo {
    constructor(viewer, cardList) {
        this.viewer = viewer;
        this.cardList = cardList;

        if (this.isScrollable) {
            this.cardList.style.top = parseInt(this.viewerHeight()) + "px"
        }
        else {
            this.cardList.style.top = "0px"
        }
    }

    viewer() { return this.viewer; }
    cardList() { return this.cardList; }
    viewerHeight() { return this.viewer.offsetHeight; }
    cardListHeight() { return this.cardList.offsetHeight; }
    isScrollable() { return this.cardList.offsetHeight > this.viewer.offsetHeight; }
}

var marqueeArray = [];
var marqueeSpeed = 4    //Specify marquee scroll speed (larger is faster 1-10)
var delayb4scroll = 100 //Specify initial delay before marquee starts to scroll on page (2000=2 seconds)
var lastRefresh;
var isRefreshing = false;
var refreshMinutes = 1;

// may be overkill here...
if (window.addEventListener)
    window.addEventListener("load", initializeMarqueeHandler, false)
else if (window.attachEvent)
    window.attachEvent("onload", initializeMarqueeHandler)
else if (document.getElementById)
    window.onload = initializeMarqueeHandler


function initializeMarqueeHandler() {
    lastRefresh = new Date();
    isRefreshing = false;
    var viewers = document.getElementsByClassName("cardViewer");
    var cards;
    var i;
    for (i = 0; i < viewers.length; i++) {
        cards = viewers[i].getElementsByClassName("cardList");
        if (cards.length != 1)
            return;
        marqueeArray.push(new marqueeInfo(viewers[i], cards[0]));
    }
    setTimeout('lefttime=setInterval("scrollMarquees()",30)', delayb4scroll)
}

function scrollMarquees() {
    marqueeArray.forEach(function (marquee, index, array) {
        if (marquee.isScrollable()) {
            var cardHeight = marquee.cardListHeight();
            var viewerHeight = marquee.viewerHeight();
            var cardTop = parseInt(marquee.cardList.style.top);
            var targetTop = 0 - cardHeight;
            if (cardTop > targetTop)  // are we thru the list?
                marquee.cardList.style.top = cardTop - marqueeSpeed + "px" //scroll
            else
                marquee.cardList.style.top = viewerHeight + 25 + "px";
        }
        else {
            marquee.cardList.style.top = "0px";
        }
    })

    var milliseconds = (new Date()).getTime() - lastRefresh.getTime();
    if (milliseconds > (refreshMinutes * 60 * 1000) && !isRefreshing) {
        lastRefresh = new Date();
        isRefreshing = true;
        window.location.reload(true);
    }
}

Все работает как положено - наконец.

...