Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/HighTable/Scroller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useMemo } from 'react'

import { CellNavigationContext } from '../../contexts/CellNavigationContext.js'
import { ScrollContext } from '../../contexts/ScrollContext.js'
import { useSetViewportSize } from '../../contexts/ViewportSizeContext.js'
import { SetViewportSizeContext } from '../../contexts/ViewportSizeContext.js'
import styles from '../../HighTable.module.css'

interface Props {
Expand All @@ -13,7 +13,7 @@ interface Props {

export default function Scroller({ children }: Props) {
/** Callback to set the current viewport size */
const setViewportSize = useSetViewportSize()
const setViewportSize = useContext(SetViewportSizeContext)
const { goToCurrentCell } = useContext(CellNavigationContext)
const { canvasHeight, sliceTop, setScrollTop, setScrollTo } = useContext(ScrollContext)

Expand Down
8 changes: 4 additions & 4 deletions src/components/HighTable/Slice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useMemo } from 'react'

import { CellNavigationContext } from '../../contexts/CellNavigationContext.js'
import { ColumnsVisibilityContext } from '../../contexts/ColumnsVisibilityContext.js'
import { useData, useDataVersion, useNumRows } from '../../contexts/DataContext.js'
import { DataContext, DataVersionContext, NumRowsContext } from '../../contexts/DataContext.js'
import { OrderByContext } from '../../contexts/OrderByContext.js'
import { ScrollContext } from '../../contexts/ScrollContext.js'
import { SelectionContext } from '../../contexts/SelectionContext.js'
Expand Down Expand Up @@ -34,10 +34,10 @@ export default function Slice({
const { visibleColumnsParameters: columnsParameters } = useContext(ColumnsVisibilityContext)
const { renderedRowsStart, renderedRowsEnd } = useContext(ScrollContext)
/** A version number that increments whenever a data frame is updated or resolved (the key remains the same). */
const version = useDataVersion()
const version = useContext(DataVersionContext)
/** The actual number of rows in the data frame */
const numRows = useNumRows()
const data = useData()
const numRows = useContext(NumRowsContext)
const data = useContext(DataContext)

// Fetch the required cells if needed (visible + overscan)
// it's a side-effect.
Expand Down
10 changes: 5 additions & 5 deletions src/components/HighTable/Wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { CSSProperties, ReactNode } from 'react'
import { type CSSProperties, type ReactNode, useContext } from 'react'

import { useNumRows } from '../../contexts/DataContext.js'
import { NumRowsContext } from '../../contexts/DataContext.js'
import { PortalContainerContext } from '../../contexts/PortalContainerContext.js'
import { useHeaderHeight } from '../../contexts/TableCornerSizeContext.js'
import { TableCornerHeightContext } from '../../contexts/TableCornerSizeContext.js'
import styles from '../../HighTable.module.css'
import { useHTMLElement } from '../../hooks/useHTMLElement.js'
import type { HighTableProps } from '../../types.js'
Expand All @@ -14,9 +14,9 @@ type Props = Pick<HighTableProps, 'className' | 'maxRowNumber' | 'styled'> & {

export default function Wrapper({ children, className, maxRowNumber, styled }: Props) {
/** Number of rows in the data frame */
const numRows = useNumRows()
const numRows = useContext(NumRowsContext)
/** Height of the header, used to set a CSS variable for row height calculation in the cells */
const headerHeight = useHeaderHeight()
const headerHeight = useContext(TableCornerHeightContext)

// reserve space for at least 3 characters
const numCharacters = Math.max((maxRowNumber ?? numRows).toLocaleString('en-US').length, 3)
Expand Down
6 changes: 3 additions & 3 deletions src/components/TableCorner/TableCorner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ChangeEvent, CSSProperties, KeyboardEvent, ReactNode } from 'react'
import { useCallback, useEffect, useRef } from 'react'
import { useCallback, useContext, useEffect, useRef } from 'react'

import { useSetTableCornerSize } from '../../contexts/TableCornerSizeContext.js'
import { SetTableCornerSizeContext } from '../../contexts/TableCornerSizeContext.js'
import { useCellFocus } from '../../hooks/useCellFocus.js'

interface Props {
Expand All @@ -16,7 +16,7 @@ interface Props {

export default function TableCorner({ children, checked, onCheckboxPress, pendingSelectionGesture, style, ariaColIndex, ariaRowIndex }: Props) {
const { tabIndex, navigateToCell, focusIfNeeded } = useCellFocus({ ariaColIndex, ariaRowIndex })
const setTableCornerSize = useSetTableCornerSize()
const setTableCornerSize = useContext(SetTableCornerSizeContext)

// Focus the cell if needed. We use an effect, as it acts on the DOM element after render.
const ref = useRef<HTMLTableCellElement | null>(null)
Expand Down
41 changes: 6 additions & 35 deletions src/contexts/DataContext.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,18 @@
import { createContext, useContext } from 'react'
import { createContext } from 'react'

import type { ColumnDescriptor, DataFrame } from '../helpers/dataframe/types.js'

export type DataFrameMethods = Pick<DataFrame, 'getRowNumber' | 'getCell' | 'fetch'>
export type DataFrameWithoutMethods = Omit<DataFrame, 'getRowNumber' | 'getCell' | 'fetch'>

export const DataKeyContext = createContext<number>(0)
export const DataVersionContext = createContext<number>(0)
export const NumRowsContext = createContext<number>(0)
export const ColumnDescriptorsContext = createContext<Pick<ColumnDescriptor, 'name' | 'sortable'>[]>([])
export const NumColumnsContext = createContext<number>(0)
export const ExclusiveSortContext = createContext<boolean>(false)
export const DataContext = createContext<DataFrameMethods | undefined>(undefined)

export const DataContext = createContext<DataFrameMethods>({
getRowNumber: () => undefined,
getCell: () => undefined,
})
Comment thread
severo marked this conversation as resolved.
Outdated
// the data key is only used in tests
export function useDataKey() {
return useContext(DataKeyContext)
}

export function useDataVersion() {
return useContext(DataVersionContext)
}

export function useNumRows() {
return useContext(NumRowsContext)
}

export function useColumnDescriptors() {
return useContext(ColumnDescriptorsContext)
}

export function useNumColumns() {
return useContext(NumColumnsContext)
}

export function useExclusiveSort() {
return useContext(ExclusiveSortContext)
}

export function useData(): DataFrameMethods {
const data = useContext(DataContext)
if (data === undefined) {
throw new Error('useData must be used within a DataContext.Provider with a valid DataFrameMethods value')
}
return data
}
export const DataKeyContext = createContext<number>(0)
17 changes: 3 additions & 14 deletions src/contexts/TableCornerSizeContext.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import { createContext, useContext } from 'react'
import { createContext } from 'react'

import { rowHeight } from '../helpers/constants.js'

type SetTableCornerSizeContextType = (element: HTMLElement) => void

export const TableCornerHeightContext = createContext<number | undefined>(undefined)
export const defaultTableCornerHeight = rowHeight
export const TableCornerHeightContext = createContext<number>(defaultTableCornerHeight)
export const TableCornerWidthContext = createContext<number | undefined>(undefined)
export const SetTableCornerSizeContext = createContext<SetTableCornerSizeContextType | undefined>(undefined)

export function useTableCornerWidth() {
return useContext(TableCornerWidthContext)
}

export function useHeaderHeight() {
return useContext(TableCornerHeightContext) ?? rowHeight
}

export function useSetTableCornerSize() {
return useContext(SetTableCornerSizeContext)
}
14 changes: 1 addition & 13 deletions src/contexts/ViewportSizeContext.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
import { createContext, useContext } from 'react'
import { createContext } from 'react'

type SetViewportSizeContextType = (element: HTMLElement) => void

export const ViewportHeightContext = createContext<number | undefined>(undefined)
export const ViewportWidthContext = createContext<number | undefined>(undefined)
export const SetViewportSizeContext = createContext<SetViewportSizeContextType | undefined>(undefined)

export function useViewportWidth() {
return useContext(ViewportWidthContext)
}

export function useViewportHeight() {
return useContext(ViewportHeightContext)
}

export function useSetViewportSize() {
return useContext(SetViewportSizeContext)
}
6 changes: 3 additions & 3 deletions src/hooks/useFetchCells.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext, useEffect, useEffectEvent, useMemo } from 'react'

import { ColumnsVisibilityContext } from '../contexts/ColumnsVisibilityContext.js'
import { useData, useNumRows } from '../contexts/DataContext.js'
import { DataContext, NumRowsContext } from '../contexts/DataContext.js'
import { OrderByContext } from '../contexts/OrderByContext.js'
import { ScrollContext } from '../contexts/ScrollContext.js'
import { defaultOverscan } from '../helpers/constants.js'
Expand All @@ -16,8 +16,8 @@ export function useFetchCells({ overscan = defaultOverscan, onError }: Props) {
const { visibleRowsStart, visibleRowsEnd } = useContext(ScrollContext)
const { visibleColumnsParameters } = useContext(ColumnsVisibilityContext)
const orderBy = useContext(OrderByContext)
const data = useData()
const numRows = useNumRows()
const data = useContext(DataContext)
const numRows = useContext(NumRowsContext)

const fetchedRowsStart = useMemo(() => {
if (visibleRowsStart === undefined) return undefined
Expand Down
4 changes: 2 additions & 2 deletions src/providers/CellNavigationProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useContext, useEffect, useMemo, useReducer } from 'react'
import type { FocusAction, FocusState, MoveCellAction } from '../contexts/CellNavigationContext.js'
import { CellNavigationContext } from '../contexts/CellNavigationContext.js'
import { ColumnsVisibilityContext } from '../contexts/ColumnsVisibilityContext.js'
import { useNumRows } from '../contexts/DataContext.js'
import { NumRowsContext } from '../contexts/DataContext.js'
import { defaultNumRowsPerPage } from '../helpers/constants.js'
import { useInputState } from '../hooks/useInputState.js'
import type { HighTableProps } from '../types.js'
Expand Down Expand Up @@ -55,7 +55,7 @@ export function CellNavigationProvider({
}: CellNavigationProviderProps) {
const [focusState, focusDispatch] = useReducer(reducer, focus, initializeFocusState)
/** The actual number of rows in the data frame */
const numDataRows = useNumRows()
const numDataRows = useContext(NumRowsContext)

const notifyChange = useCallback(() => {
focusDispatch({ type: 'START' })
Expand Down
6 changes: 3 additions & 3 deletions src/providers/ColumnParametersProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactNode, useMemo } from 'react'
import { type ReactNode, useContext, useMemo } from 'react'

import { type ColumnParameters, ColumnParametersContext, SortableColumnsContext } from '../contexts/ColumnParametersContext.js'
import { useColumnDescriptors } from '../contexts/DataContext.js'
import { ColumnDescriptorsContext } from '../contexts/DataContext.js'
import type { HighTableProps } from '../types.js'

type Props = Pick<HighTableProps, 'columnConfiguration'> & {
Expand All @@ -15,7 +15,7 @@ type Props = Pick<HighTableProps, 'columnConfiguration'> & {
* It merges the column descriptors from the data frame with the user-provided configuration.
*/
export function ColumnParametersProvider({ columnConfiguration, children }: Props) {
const columnDescriptors = useColumnDescriptors()
const columnDescriptors = useContext(ColumnDescriptorsContext)

// A column is sortable iif it's marked as sortable in the column descriptors from the data frame. The user configuration can't change that.
const sortableColumns = useMemo(() => {
Expand Down
12 changes: 6 additions & 6 deletions src/providers/ColumnWidthsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r

import { ColumnParametersContext } from '../contexts/ColumnParametersContext.js'
import { ColumnWidthsContext } from '../contexts/ColumnWidthsContext.js'
import { useNumColumns } from '../contexts/DataContext.js'
import { useTableCornerWidth } from '../contexts/TableCornerSizeContext.js'
import { useViewportWidth } from '../contexts/ViewportSizeContext.js'
import { NumColumnsContext } from '../contexts/DataContext.js'
import { TableCornerWidthContext } from '../contexts/TableCornerSizeContext.js'
import { ViewportWidthContext } from '../contexts/ViewportSizeContext.js'
import { cellStyle } from '../helpers/width.js'
import { useLocalStorageState } from '../hooks/useLocalStorageState.js'

Expand Down Expand Up @@ -73,11 +73,11 @@ interface ColumnWidthsProviderProps {
*/
export function ColumnWidthsProvider({ children, localStorageKey, minWidth }: ColumnWidthsProviderProps) {
/** Current viewport width (used to compute the maximum total width) */
const viewportWidth = useViewportWidth()
const viewportWidth = useContext(ViewportWidthContext)
/** Current table corner width (used to compute the maximum total width) */
const tableCornerWidth = useTableCornerWidth()
const tableCornerWidth = useContext(TableCornerWidthContext)
/** Number of columns (used to initialize the widths array, and compute the widths) */
const numColumns = useNumColumns()
const numColumns = useContext(NumColumnsContext)

// Number of columns
if (!Number.isInteger(numColumns) || numColumns < 0) {
Expand Down
4 changes: 2 additions & 2 deletions src/providers/OrderByProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactNode, useContext, useMemo } from 'react'

import { SortableColumnsContext } from '../contexts/ColumnParametersContext.js'
import { useExclusiveSort } from '../contexts/DataContext.js'
import { ExclusiveSortContext } from '../contexts/DataContext.js'
import { OrderByContext, SortInfoAndActionsByColumnContext } from '../contexts/OrderByContext.js'
import { type OrderBy, toggleColumn, toggleColumnExclusive } from '../helpers/sort.js'
import { useInputState } from '../hooks/useInputState.js'
Expand All @@ -24,7 +24,7 @@ type Props = Pick<HighTableProps, 'orderBy' | 'onOrderByChange'> & {
* The context value is memoized and won't change unless the orderBy or the sortable columns change, to avoid unnecessary re-renders of the consumers.
*/
export function OrderByProvider({ children, orderBy: controlledOrderBy, onOrderByChange }: Props) {
const exclusiveSort = useExclusiveSort()
const exclusiveSort = useContext(ExclusiveSortContext)
const sortableColumns = useContext(SortableColumnsContext)

const [orderBy, setOrderBy] = useInputState<OrderBy>({
Expand Down
12 changes: 6 additions & 6 deletions src/providers/ScrollProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type ReactNode, useCallback, useContext, useEffect, useMemo, useReducer, useState } from 'react'

import { CellNavigationContext } from '../contexts/CellNavigationContext.js'
import { useNumRows } from '../contexts/DataContext.js'
import { NumRowsContext } from '../contexts/DataContext.js'
import { ScrollContext } from '../contexts/ScrollContext.js'
import { useHeaderHeight } from '../contexts/TableCornerSizeContext.js'
import { useViewportHeight } from '../contexts/ViewportSizeContext.js'
import { TableCornerHeightContext } from '../contexts/TableCornerSizeContext.js'
import { ViewportHeightContext } from '../contexts/ViewportSizeContext.js'
import { defaultPadding, maxElementHeight, rowHeight } from '../helpers/constants.js'
import { computeDerivedValues, createScale, getScrollActionForRow, initializeScrollState, scrollReducer } from '../helpers/scroll.js'
import type { HighTableProps } from '../types.js'
Expand All @@ -20,11 +20,11 @@ type ScrollProviderProps = Pick<HighTableProps, 'padding'> & {
export function ScrollProvider({ children, padding = defaultPadding }: ScrollProviderProps) {
const [{ scale, scrollTop, scrollTopAnchor, localOffset }, dispatch] = useReducer(scrollReducer, undefined, initializeScrollState)
const { cellPosition, focusState, focusDispatch } = useContext(CellNavigationContext)
const clientHeight = useViewportHeight()
const clientHeight = useContext(ViewportHeightContext)
/** Height of the header row, in pixels */
const headerHeight = useHeaderHeight()
const headerHeight = useContext(TableCornerHeightContext)
/** The actual number of rows in the data frame */
const numRows = useNumRows()
const numRows = useContext(NumRowsContext)

const [scrollTo, setScrollTo] = useState<HTMLElement['scrollTo'] | undefined>(undefined)
const setScrollTop = useCallback((scrollTop: number) => {
Expand Down
6 changes: 3 additions & 3 deletions src/providers/SelectionProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { KeyboardEvent, ReactNode } from 'react'
import { useCallback, useContext, useEffect, useEffectEvent, useMemo, useState } from 'react'

import type { DataFrameMethods } from '../contexts/DataContext.js'
import { useData, useNumRows } from '../contexts/DataContext.js'
import { DataContext, NumRowsContext } from '../contexts/DataContext.js'
import { OrderByContext } from '../contexts/OrderByContext.js'
import { SelectionContext } from '../contexts/SelectionContext.js'
import { checkSignal } from '../helpers/dataframe/helpers.js'
Expand All @@ -29,8 +29,8 @@ interface Gesture {
*/
export function SelectionProvider({ children, selection: controlledSelection, onError, onSelectionChange }: Props) {
/** The actual number of rows in the data frame */
const numRows = useNumRows()
const data = useData()
const numRows = useContext(NumRowsContext)
const data = useContext(DataContext)
// The selection is only useful for the parent component. If no props are passed, hide the selection feature.
const [isEnabled] = useState<boolean>(() => controlledSelection !== undefined || onSelectionChange !== undefined)
const inputState = useInputState<Selection>({
Expand Down
4 changes: 2 additions & 2 deletions src/providers/TableCornerSizeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type ReactNode, useCallback, useState } from 'react'

import { SetTableCornerSizeContext, TableCornerHeightContext, TableCornerWidthContext } from '../contexts/TableCornerSizeContext.js'
import { defaultTableCornerHeight, SetTableCornerSizeContext, TableCornerHeightContext, TableCornerWidthContext } from '../contexts/TableCornerSizeContext.js'

interface Props {
/** Child components */
Expand All @@ -14,7 +14,7 @@ interface Props {
*/
export function TableCornerSizeProvider({ children }: Props) {
const [tableCornerWidth, setTableCornerWidth] = useState<number | undefined>(undefined)
const [tableCornerHeight, setTableCornerHeight] = useState<number | undefined>(undefined)
const [tableCornerHeight, setTableCornerHeight] = useState<number>(defaultTableCornerHeight)
const setTableCornerSize = useCallback((element: HTMLElement) => {
// we use offsetWidth and offsetHeight as they include padding, borders, and scrollbars (when present)
setTableCornerWidth(element.offsetWidth)
Expand Down
18 changes: 8 additions & 10 deletions test/providers/DataProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { render } from '@testing-library/react'
import { act } from 'react'
import { act, useContext } from 'react'
import { describe, expect, it } from 'vitest'

import { useColumnDescriptors, useData, useDataKey, useDataVersion, useExclusiveSort, useNumColumns, useNumRows } from '../../src/contexts/DataContext.js'
import { ColumnDescriptorsContext, DataKeyContext, DataVersionContext, ExclusiveSortContext, NumColumnsContext, NumRowsContext } from '../../src/contexts/DataContext.js'
import type { DataFrame, DataFrameEvents } from '../../src/helpers/dataframe/index.js'
import { arrayDataFrame } from '../../src/helpers/dataframe/index.js'
import { createEventTarget } from '../../src/helpers/typedEventTarget.js'
import { DataProvider } from '../../src/providers/DataProvider.js'

function DisplayComponent() {
const dataKey = useDataKey()
const dataVersion = useDataVersion()
const numRows = useNumRows()
const columnDescriptors = useColumnDescriptors()
const numColumns = useNumColumns()
const exclusiveSort = useExclusiveSort() ? 'true' : 'false'
// used only to check if the data can be obtained (no error thrown)
useData()
const dataKey = useContext(DataKeyContext)
const dataVersion = useContext(DataVersionContext)
const numRows = useContext(NumRowsContext)
const columnDescriptors = useContext(ColumnDescriptorsContext)
const numColumns = useContext(NumColumnsContext)
const exclusiveSort = useContext(ExclusiveSortContext) ? 'true' : 'false'
Comment thread
severo marked this conversation as resolved.

return (
<div>
Expand Down
Loading