Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@netdata/netdata-ui",
"version": "5.4.16",
"version": "5.4.17",
"description": "netdata UI kit",
"main": "dist/index.js",
"module": "dist/es6/index.js",
Expand Down
5 changes: 5 additions & 0 deletions src/components/table/body/columnFlex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default (column, header, hasExplicitSize) =>
(column.columnDef.notFlex || column.getCanResize()) &&
(!column.columnDef.fullWidth || hasExplicitSize)
? false
: header.colSpan
9 changes: 3 additions & 6 deletions src/components/table/body/header/cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Flex from "@/components/templates/flex"
import { Text } from "@/components/typography"
import { useTableState } from "../../provider"
import ResizeHandler from "./resizeHandler"
import getColumnFlex from "../columnFlex"
import Sorting, { SortIconContainer } from "./sorting"
import Info from "./info"
import Filter from "./filter"
Expand Down Expand Up @@ -73,11 +74,7 @@ const BodyHeaderCell = ({
<Flex
ref={sortableRef}
style={enableColumnReordering ? sortableStyle : undefined}
flex={
!column.columnDef.fullWidth && (column.columnDef.notFlex || column.getCanResize())
? false
: header.colSpan
}
flex={getColumnFlex(column, header, table.getState().columnSizing?.[column.id] != null)}
width={`${header.subHeaders.length ? header.subHeaders.reduce((s, h) => s + h.column.getSize(), 0) : column.getSize()}px`}
height={{ min: "45px" }}
position="relative"
Expand Down Expand Up @@ -125,7 +122,7 @@ const BodyHeaderCell = ({
</LabelContainer>
<Filter column={column} testPrefix={testPrefix} index={index} />
</Flex>
<ResizeHandler header={header} table={table} />
<ResizeHandler header={header} table={table} testPrefix={testPrefix} />
{children}
</Flex>
)
Expand Down
104 changes: 99 additions & 5 deletions src/components/table/body/header/resizeHandler.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,120 @@
import React from "react"
import React, { useRef } from "react"
import { flushSync } from "react-dom"
import styled from "styled-components"
import Flex from "@/components/templates/flex"
import { useTableState } from "../../provider"

const rerenderSelector = state => state.columnSizingInfo?.deltaPercentage

const AUTO_FIT_PADDING = 8

const AUTO_FIT_MAX_RATIO = 0.5

const naturalWidthStyles = {
width: "max-content",
minWidth: "max-content",
maxWidth: "none",
whiteSpace: "nowrap",
overflow: "visible",
textOverflow: "clip",
flexWrap: "nowrap",
flexShrink: "0",
}

export const Handler = styled(Flex).attrs({
position: "absolute",
top: "2px",
right: 0,
bottom: "2px",
})``

const ResizeHandler = ({ header, table }) => {
const measureNaturalWidth = (nodes, forceNowrap) => {
const probe = document.createElement("div")
probe.style.cssText = [
"position:absolute",
"visibility:hidden",
"pointer-events:none",
"left:-9999px",
"top:0",
"width:max-content",
"min-width:max-content",
"max-width:none",
"white-space:nowrap",
].join(";")
document.body.appendChild(probe)

let max = 0
try {
nodes.forEach(node => {
const clone = node.cloneNode(true)
clone.style.width = "max-content"
clone.style.minWidth = "max-content"
clone.style.maxWidth = "none"

const cs = getComputedStyle(node)
clone.style.fontFamily = cs.fontFamily
clone.style.fontSize = cs.fontSize
clone.style.fontWeight = cs.fontWeight
clone.style.fontStyle = cs.fontStyle
clone.style.letterSpacing = cs.letterSpacing

if (forceNowrap) {
clone.querySelectorAll("*").forEach(el => Object.assign(el.style, naturalWidthStyles))
Object.assign(clone.style, naturalWidthStyles)
}

probe.appendChild(clone)
max = Math.max(max, clone.getBoundingClientRect().width, clone.scrollWidth)
probe.removeChild(clone)
})
} finally {
document.body.removeChild(probe)
}

return max
}

const ResizeHandler = ({ header, table, testPrefix = "" }) => {
useTableState(rerenderSelector)
const ref = useRef()
if (!header.column.getCanResize()) return null

const resizingProps = header.column.getIsResizing()
const { column } = header

const resizingProps = column.getIsResizing()
? { transform: `translateX(${table.getState().columnSizingInfo.deltaOffset}px)` }
: {}

const handleResizeStart = e => {
if (column.columnDef.fullWidth && table.getState().columnSizing?.[column.id] == null) {
const width = ref.current?.parentElement?.getBoundingClientRect().width
if (width) flushSync(() => table.setColumnSizing(old => ({ ...old, [column.id]: width })))
}
header.getResizeHandler()(e)
}

const handleAutoFit = () => {
const headerCell = ref.current?.parentElement
if (!headerCell) return

const container = headerCell.closest(`[data-testid="netdata-table${testPrefix}"]`)
const cells = container
? [...container.querySelectorAll('[data-testid^="netdata-table-cell-"]')].filter(
el => el.getAttribute("data-testid") === `netdata-table-cell-${column.id}${testPrefix}`
)
: []

const max = measureNaturalWidth([headerCell, ...cells], !column.columnDef.fullWidth)
if (!max) return

const limit = container ? container.clientWidth * AUTO_FIT_MAX_RATIO : Infinity
const width = Math.min(Math.ceil(max) + AUTO_FIT_PADDING, limit)
table.setColumnSizing(old => ({ ...old, [column.id]: width }))
}

return (
<Handler
ref={ref}
border={{ side: "right", size: "1px", color: "borderSecondary" }}
_hover={{ border: { side: "right", size: "3px", color: "resizerLine" } }}
_active={{ border: { side: "right", size: "3px", color: "resizerLine" } }}
Expand All @@ -32,8 +125,9 @@ const ResizeHandler = ({ header, table }) => {
cursor: "col-resize",
...resizingProps,
}}
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
onMouseDown={handleResizeStart}
onTouchStart={handleResizeStart}
onDoubleClick={handleAutoFit}
/>
)
}
Expand Down
13 changes: 7 additions & 6 deletions src/components/table/body/row.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import styled from "styled-components"
import Flex from "@/components/templates/flex"
import { getColor } from "@/theme"
import { useTableState } from "../provider"
import getColumnFlex from "./columnFlex"

const CellGroup = ({ cell, row, header, testPrefix, coloredSortedColumn }) => {
const CellGroup = ({ cell, row, table, header, testPrefix, coloredSortedColumn }) => {
const { column } = cell

const tableMeta = useMemo(
Expand Down Expand Up @@ -32,11 +33,7 @@ const CellGroup = ({ cell, row, header, testPrefix, coloredSortedColumn }) => {

return (
<Flex
flex={
!column.columnDef.fullWidth && (column.columnDef.notFlex || column.getCanResize())
? false
: header.colSpan
}
flex={getColumnFlex(column, header, table.getState().columnSizing?.[column.id] != null)}
width={`${column.getSize()}px`}
position="relative"
data-testid={`netdata-table-cell-${cell.column.columnDef.id}${testPrefix}`}
Expand All @@ -63,6 +60,7 @@ const rerenderSelector = state => ({
columnVisibility: state.columnVisibility,
selectedRows: state.selectedRows,
allColumns: state.allColumns?.length,
visibleColumns: state.visibleColumns,
})

const StyledRow = styled(Flex)`
Expand Down Expand Up @@ -147,6 +145,7 @@ export default memo(
<CellGroup
cell={cell}
row={row}
table={table}
key={cell.id}
testPrefix={testPrefix}
header={table.getLeftLeafHeaders()[index]}
Expand All @@ -169,6 +168,7 @@ export default memo(
<CellGroup
cell={cell}
row={row}
table={table}
key={cell.id}
testPrefix={testPrefix}
header={table.getCenterLeafHeaders()[index]}
Expand Down Expand Up @@ -196,6 +196,7 @@ export default memo(
<CellGroup
cell={cell}
row={row}
table={table}
key={cell.id}
testPrefix={testPrefix}
header={table.getRightLeafHeaders()[index]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const ColumnsMenuItem = ({ column, dataGa, disabled }) => {
<Checkbox
checked={checked}
disabled={disabled}
labelProps={{ wordBreak: "break-word" }}
label={
column.columnDef.name ||
(typeof column.columnDef.headerString === "function"
Expand Down Expand Up @@ -82,12 +83,12 @@ const ColumnsMenu = ({
return (
<Drop
background="dropdown"
height={{ max: "400px" }}
height={{ max: "70vh" }}
onClickOutside={onClose}
overflow={{ vertical: "auto" }}
round={1}
target={parentRef.current}
width={50}
width={{ base: 100, max: "90vw" }}
align={{ top: "bottom", right: "right" }}
>
<Flex
Expand Down
1 change: 1 addition & 0 deletions src/components/table/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ const Table = memo(props => {
rowsById: table.getRowModel().rowsById,
selectedRows: table.getSelectedRowModel().flatRows,
allColumns: table?.getAllLeafColumns?.(),
visibleColumns: table?.getVisibleLeafColumns?.().map(column => column.id),
})
}, [table.getState()])

Expand Down
Loading