Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
30 changes: 13 additions & 17 deletions packages/core/data/Table/TableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-bitwise */
import React, { forwardRef, useRef, useState } from 'react'
import React, { forwardRef, useState } from 'react'

import { isBrowser } from '@stage-ui/system'

Expand Down Expand Up @@ -82,22 +82,12 @@ function TableRow(props: Types.RowProps, ref: React.ForwardedRef<HTMLTableRowEle
}
}

const rowClickTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
const onClick = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>) => {
if (typeof onRowDoubleClick === 'function') {
if (rowClickTimer.current) {
clearTimeout(rowClickTimer.current)
rowClickTimer.current = null
onRowDoubleClick(e)
} else {
rowClickTimer.current = setTimeout(() => {
rowClickTimer.current = null
onRowClick?.(e)
}, 250)
}
} else {
onRowClick?.(e)
}
onRowClick?.(e)
}

const onDoubleClick = (e: React.MouseEvent<HTMLTableRowElement, MouseEvent>) => {
onRowDoubleClick?.(e)
}

const handleCheckboxClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
Expand All @@ -117,6 +107,7 @@ function TableRow(props: Types.RowProps, ref: React.ForwardedRef<HTMLTableRowEle
style={style}
{...primaryEvents}
onClick={onClick}
onDoubleClick={onDoubleClick}
ref={ref}
css={styles.row({
selected: rowCtxItem.isSelected,
Expand Down Expand Up @@ -154,4 +145,9 @@ function TableRow(props: Types.RowProps, ref: React.ForwardedRef<HTMLTableRowEle
return <tr ref={ref} id={rowId} style={style} />
}

export default forwardRef(TableRow)
export default React.memo(forwardRef(TableRow), (prev, next) => {
return (
prev.rowCtxItem.row === next.rowCtxItem.row &&
prev.rowCtxItem.isSelected === next.rowCtxItem.isSelected
)
})
124 changes: 61 additions & 63 deletions packages/core/data/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-for-of-loops/no-for-of-loops */
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react'
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'

import { isBrowser, useSystem } from '@stage-ui/system'

Expand Down Expand Up @@ -42,37 +36,65 @@ function Table(props: Types.Props, ref: React.ForwardedRef<Types.Ref>) {
dataKey,
} = props

const [reloadData, reload] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [primaryKey, setPrimaryKey] = useState<Types.TableCellKey>('')
const [sort, setSort] = useState<Types.TableSortObject>({ key: '', sort: 'ASC' })

const [currentPage, setCurrentPage] = useState(1)
const [reloadData, reload] = useState(false)
const [sort, setSort] = useState<Types.TableSortObject>({
key: '',
sort: 'ASC',
})

const mapRowContext = (row: Types.Row): Types.TableRowContext => {
const isCellModify: Types.TableRowContext['isCellModify'] = {}
columns.forEach((column) => {
isCellModify[column.key] = false
const selectedSet = useMemo(() => {
return new Set((selected || []).map((s) => s[primaryKey] as string | number))
}, [selected, primaryKey])

const baseRowCtx = useMemo(() => {
const rows = data.slice()

if (!sort.key) return rows

return rows.sort((a, b) => {
const aValue = a[sort.key] as string | number
const bValue = b[sort.key] as string | number

if (sort.sort === 'ASC') {
if (typeof aValue === 'string') {
return aValue.localeCompare(bValue as string)
}
return aValue - (bValue as number)
}

if (sort.sort === 'DESC') {
if (typeof bValue === 'string') {
return bValue.localeCompare(aValue as string)
}
return bValue - (aValue as number)
}

return 0
})
}, [data, sort])

let isSelected: Types.TableCellContext['isSelected'] = false
if (Array.isArray(selected) && primaryKey) {
isSelected = selected.findIndex((c) => c[primaryKey] === row[primaryKey]) >= 0
}
const rowCtx = useMemo(() => {
return baseRowCtx.map((row: Types.Row): Types.TableRowContext => {
const isCellModify: Types.TableRowContext['isCellModify'] = {}
columns.forEach((column) => {
isCellModify[column.key] = false
})

return {
row,
isExpand: false,
isVisible: true,
isCellModify,
isSelected,
setModifyState: {},
}
}
let isSelected: Types.TableCellContext['isSelected'] = false

if (primaryKey) {
isSelected = selectedSet.has(row[primaryKey] as string | number)
}

let rowCtx = data.slice().map(mapRowContext)
return {
row,
isExpand: false,
isVisible: true,
isCellModify,
isSelected,
setModifyState: {},
}
})
}, [baseRowCtx, selected, columns, primaryKey])

/**
* Getting current data state
Expand All @@ -82,26 +104,6 @@ function Table(props: Types.Props, ref: React.ForwardedRef<Types.Ref>) {
/**
* Soring column by its settings
*/
const columnSort = (value: Types.TableSortObject) => {
if (value.sort) {
rowCtx = rowCtx.sort((a, b) => {
if (value.sort === 'ASC') {
if (typeof a.row[value.key] === 'string') {
return (a.row[value.key] as string).localeCompare(b.row[value.key] as string)
}
return a.row[value.key] - b.row[value.key]
}
if (value.sort === 'DESC') {
if (typeof b.row[value.key] === 'string') {
return (b.row[value.key] as string).localeCompare(a.row[value.key] as string)
}
return b.row[value.key] - a.row[value.key]
}
return 0
})
}
}

const toggleSort = (value: Types.TableSortObject) => {
return new Promise((resolve) => {
const column = columns.find((currentColumn) => currentColumn.key === value.key)
Expand Down Expand Up @@ -171,14 +173,6 @@ function Table(props: Types.Props, ref: React.ForwardedRef<Types.Ref>) {
}
}

if (sort.key) {
columnSort(sort)
} else {
for (const column of columns) {
columnSort(column as Types.TableSortObject)
}
}

const setNeedDisplay = () => {
let state = 1
for (const rowCtxItem of rowCtx) {
Expand Down Expand Up @@ -342,6 +336,10 @@ function Table(props: Types.Props, ref: React.ForwardedRef<Types.Ref>) {
)
}

export default forwardRef(Table) as <Row extends Types.Row>(
props: Types.Props<Row> & { ref?: React.ForwardedRef<Types.Ref<Row>> },
) => React.ReactElement
interface TableComponent {
<R extends Types.Row>(
props: Types.Props<R> & React.RefAttributes<HTMLDivElement>,
): React.ReactElement
}

export default React.memo(React.forwardRef(Table)) as TableComponent
8 changes: 8 additions & 0 deletions packages/core/utils/fastMemo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'

import { fastObjectShallowCompare } from './fastObjectShallowCompare'

export function fastMemo<T>(component: T): T {
return React.memo(component as any, fastObjectShallowCompare) as unknown as T
}
33 changes: 33 additions & 0 deletions packages/core/utils/fastObjectShallowCompare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable @typescript-eslint/no-explicit-any */
const { is } = Object

/**
* Fast shallow compare for objects.
* @returns true if objects are equal.
*/
export function fastObjectShallowCompare<T extends Record<string, any> | null>(
a: T,
b: T,
) {
if (a === b) return true

if (!(a instanceof Object) || !(b instanceof Object)) return false

let aLength = 0
let bLength = 0

for (const key in a) {
aLength += 1
if (!is(a[key], b[key])) return false
if (!(key in b)) return false
}

// eslint-disable-next-line @typescript-eslint/naming-convention
for (const _ in b) {
bLength += 1
}

return aLength === bLength
}