Невозможно прокрутить последний дочерний элемент с помощью ScrollSync с несколькими сетками и коллекциями

Я пытаюсь разработать компонент временной шкалы. Это урезанная версия для демонстрации проблемы:

https://jsfiddle.net/mauron85/5vverasn/

image

Он должен отображать 48-часовые интервалы между двумя датами (30.01.2007 - 31.01.2007) в заголовке таблицы и маршруты курьера (отображаемые в виде каскада квадратов), часы работы курьера и полосатый фон в теле таблицы.

Моя проблема в том, что прокрутка возможна только до последнего маршрута курьера (15:00). Других маршрутов нет, но я хотел бы включить прокрутку до самого последнего часового интервала (23:00)

Это работает, когда в теле таблицы есть только один компонент, но не когда их больше. В этом случае есть только полосатый фон.

image

Псевдокод:

это работает отлично:

<ScrollSync>
    <AutoSizer disableHeight>
        <Grid className={styles.HeaderGrid} /> {/* hour intervals */}
        <div className={styles.Body}>
            <Grid /> {/* table striped background */}
        </div>
    </AutoSizer>
</ScrollSync>

но прокрутка ограничена, когда в AutoSizer несколько дочерних элементов:

<ScrollSync>
    <AutoSizer disableHeight>
        <div>
            <Grid className={styles.HeaderGrid} /> {/* hour intervals */}
            <div className={styles.Body}>
                <Grid /> {/* table striped background */}
                <Collection/> {/* courier routes has to be collection */}
                <Collection/> {/* courier working hours has to be collection */}
            </div>
        </div>
    </AutoSizer>
</ScrollSync>

Блок комментирования: {/* при следовании комментарию прокрутка работает должным образом */} в примере проекта для получить ожидаемое поведение прокрутки.

Не уверен на 100%, в чем подвох, но я считаю, что это потому, что каждый дочерний компонент Grid, Collection... имеет свой собственный ReactVirtualized__Collection__innerScrollContainer, который имеет разную ширину, и только самый последний в дереве DOM фактически прокручивается/ видимый. Игра с z-индексом компонентов подтверждает это.

введите здесь описание изображения


person mauron85    schedule 31.01.2017    source источник


Ответы (2)


Не на 100% уверен, в чем подвох, но я считаю, что это потому, что каждый дочерний компонент Grid, Collection... имеет свой собственный ReactVirtualized__Collection__innerScrollContainer, который имеет разную ширину, и только самый последний в дереве DOM фактически прокручивается/видим. Игра с z-индексом компонентов подтверждает это.

Да. Это не что-то конкретное для реакции-виртуализации. Если вы сложите несколько прокручиваемых элементов DOM, только самый верхний будет получать события прокрутки. Вы можете разрешить событиям проходить через самый верхний элемент, используя pointer-events: none, но вы по-прежнему будет иметь только один элемент, получающий события прокрутки.

Если вы хотите координировать прокрутку нескольких элементов, это можно сделать, поместив их все в прокручиваемый контейнер. Конечно, это сложнее сделать с такой библиотекой, как react-virtualized, но... глядя на картинки, которые вы вставили, я бы предположил, что вам на самом деле могут не понадобиться два Collection. Вместо этого вам может сойти с рук использование Grid и предоставление собственного пользовательского cellRangeRenderer, который можно использовать для отображения пользовательского интерфейса наложения. (Это то, что было доступно для обработки свойства.) Это также будет работать лучше, потому что Grid работает лучше, чем Collection (а 1 компонент обычно работает лучше, чем 3).

Вот примерный план того, что я предлагаю:

import { defaultCellRangeRenderer, Grid } from 'react-virtualized'

function cellRangeRenderer (props) {
  // Use the default cellRangeRenderer to render your Grid's cells
  // No need to re-implement that
  const children = defaultCellRangeRenderer(props)

  // cellRangeRenderer is passed params representing the Grid's current offset
  // Use these to decide what Collection items you should display
  const { scrollLeft, scrollTop } = props

  // Add the additional items you were putting into the Collection
  // They were just absolutely positioned elements anyway so treat them the same
  children.push(
    <div style={...}>This could be a Collection cell</div>
  )

  // NOTE If you think your Grid will be larger than 1.5M pixels,
  // You will also need to account for compression, see:
  // https://github.com/bvaughn/react-virtualized/blob/eb50a1569c8a59ec3d296145b636dff4b24ccae8/source/Grid/defaultCellRangeRenderer.js#L77-L83

  return children
}

function YourGrid (props) {
  return (
    <Grid
      cellRangeRenderer={cellRangeRenderer}
      {...props}
    />
  )
}
person bvaughn    schedule 21.02.2017
comment
Спасибо. Я попробую это. Тем временем я пришел с собственным решением. Я добавлю его как отдельный ответ только для альтернативной реализации (но худшей по производительности и элегантности, как у вас). - person mauron85; 21.02.2017
comment
Пытаюсь реализовать отрисовку всех компонентов в cellRangeRender как вы предложили. Так что будет Grid с cellRangeRender, внутри которого я рендерю дополнительную Collection. Однако проблема в том, что вложенная коллекция будет иметь собственный innerScrollContainer. Есть ли простой способ отрисовать коллекцию в методе cellRangeRender без собственного innerScrollContainer? jsfiddle.net/mauron85/p562pnoe/2 - person mauron85; 23.02.2017
comment
Я не предлагал вам визуализировать вложенный Collection. Это было бы дорого. Я предлагал вам визуализировать элементы, которые вы поместите в этот Collection, непосредственно внутри ваших Grid (предоставленных вами cellRangeRenderer) в дополнение к вашим Grid ячейкам. - person bvaughn; 23.02.2017
comment
Я отредактировал свой ответ, чтобы предоставить фрагмент кода, объясняющий, что я предлагал. - person bvaughn; 23.02.2017
comment
Понял. Мое намерение поместить коллекции внутри Grid состоит в том, что я хотел бы получить преимущество частичного рендеринга данных на основе положения прокрутки, как для сетки, так и для вложенной коллекции. Таким образом, визуализируйте сетку с 48 колонками, сетка будет представлять только фон зебры. Внутри cellRangeRenderer я хотел бы отображать дополнительные коллекции, представляющие маршруты курьеров (прямоугольники), но из-за производительности я хотел бы не отображать их все сразу (как вы предложили), а поместить их в коллекцию. - person mauron85; 23.02.2017
comment
Для этого я переопределил Collection и CollectionView. По сути, я удалил только рендеринг innerScrollContainer. Но должен быть лучший способ, например, просто использовать некоторые функции из этих классов. - person mauron85; 23.02.2017
comment
Я не предлагаю вам рендерить их все сразу. Просто визуализируйте те, которые необходимы для текущего набора отображаемых ячеек. У вас есть индексы начала/остановки столбца, индексы остановки/начала строки и прокрутка влево/вверх. Это все, что вам нужно, чтобы решить, какие Collection ячейки отображать. То, что вы делаете, является довольно продвинутым вариантом использования. - person bvaughn; 23.02.2017
comment
По сути, я хотел повторно использовать как можно больше кода из Collection/CollectionView, чтобы избежать глубокого погружения в кодовую базу. Но, кажется, я все равно не могу этого избежать. Нужно выяснить, какие индексы... В любом случае, большое спасибо @brainvaughn. - person mauron85; 23.02.2017
comment
Просто для полноты. Это демонстрация с Grid с вложенной коллекцией с использованием переопределенных классов Collection и CollectionView. jsfiddle.net/mauron85/p562pnoe/7 - person mauron85; 23.02.2017
comment
Один из последних комментариев на эту тему. Я последовал вашему совету и реализовал рендеринг компонента в cellRangeRender, максимально используя ваш код. Демонстрация: jsfiddle.net/mauron85/p562pnoe/8 (cellRangeRender начинается со строки 180). Конечно, он нуждается в некоторой полировке. Я обнаружил, что в моем случае использования было бы неплохо, если бы плагин также экспортировал: calculateSizeAndPositionData и defaultCellGroupRenderer, поэтому мне не нужно их дублировать или делать пользовательские сборки. Во всяком случае, реактивная виртуализация — это фантастика. Большое спасибо. - person mauron85; 24.02.2017

Я пришел со следующим решением. По сути, это пользовательский компонент Scroller, который оборачивает все прокручиваемые компоненты и устанавливает ширину и высоту всех прокручиваемых компонентов с высотой и шириной наибольшего из них.

<Scroller
    width={width}
    height={height}
    leftOffset={100}
    totalWidth={totalWidth}
    totalHeight={totalHeight}
    scrollTop={scrollTop}
    scrollLeft={scrollLeft}
    onScroll={onScroll}
>
    <div className="Body">
        <div className="BodyPart">
            <Grid
                className="BodyGrid"
                width={totalWidth}
                height={totalHeight}
                columnWidth={columnWidth}
                columnCount={columnCount}
                rowHeight={rowHeight}
                rowCount={rowCount}
                overscanColumnCount={overscanColumnCount}
                overscanRowCount={overscanRowCount}
                cellRenderer={this.renderBodyCell}
                scrollTop={scrollTop}
                scrollLeft={scrollLeft}
            />
        </div>
        <div className="BodyPart">
            <Routes
                startTime={startDate.getTime()}
                routes={Object.values(routes)}
                courierIds={courierIds}
                height={totalHeight}
                width={totalWidth}
                rowHeight={rowHeight}
                columnWidth={columnWidth}
                scrollTop={scrollTop}
                scrollLeft={scrollLeft}
            />
        </div>
    </div>
</Scroller>

Демонстрация: https://jsfiddle.net/mauron85/97dj3baq/

person mauron85    schedule 21.02.2017