🚸 (results) Remove useless scrollbars and make header sticky
Closes #297
This commit is contained in:
@ -8,6 +8,7 @@ type Props = {
|
|||||||
cell: CellProps<TableData, unknown>
|
cell: CellProps<TableData, unknown>
|
||||||
size: number
|
size: number
|
||||||
isExpandButtonVisible: boolean
|
isExpandButtonVisible: boolean
|
||||||
|
rowIndex: number
|
||||||
cellIndex: number
|
cellIndex: number
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
onExpandButtonClick: () => void
|
onExpandButtonClick: () => void
|
||||||
@ -17,6 +18,7 @@ const Cell = ({
|
|||||||
cell,
|
cell,
|
||||||
size,
|
size,
|
||||||
isExpandButtonVisible,
|
isExpandButtonVisible,
|
||||||
|
rowIndex,
|
||||||
cellIndex,
|
cellIndex,
|
||||||
onExpandButtonClick,
|
onExpandButtonClick,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
@ -25,7 +27,7 @@ const Cell = ({
|
|||||||
key={cell.id}
|
key={cell.id}
|
||||||
px="4"
|
px="4"
|
||||||
py="2"
|
py="2"
|
||||||
border="1px"
|
borderWidth={rowIndex === 0 ? '0 1px 1px 1px' : '1px'}
|
||||||
borderColor={useColorModeValue('gray.200', 'gray.700')}
|
borderColor={useColorModeValue('gray.200', 'gray.700')}
|
||||||
whiteSpace="nowrap"
|
whiteSpace="nowrap"
|
||||||
wordBreak="normal"
|
wordBreak="normal"
|
||||||
|
@ -96,66 +96,68 @@ export const ColumnSettingsButton = ({
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button leftIcon={<ToolIcon />}>Columns</Button>
|
<Button leftIcon={<ToolIcon />}>Columns</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent w="400px">
|
<Portal>
|
||||||
<PopoverBody
|
<PopoverContent w="400px">
|
||||||
as={Stack}
|
<PopoverBody
|
||||||
spacing="4"
|
as={Stack}
|
||||||
p="4"
|
spacing="4"
|
||||||
maxH="450px"
|
p="4"
|
||||||
overflowY="scroll"
|
maxH="450px"
|
||||||
>
|
overflowY="scroll"
|
||||||
<Stack>
|
>
|
||||||
<Text fontWeight="semibold" fontSize="sm">
|
|
||||||
Shown in table:
|
|
||||||
</Text>
|
|
||||||
<DndContext
|
|
||||||
sensors={sensors}
|
|
||||||
collisionDetection={closestCenter}
|
|
||||||
onDragStart={handleDragStart}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
>
|
|
||||||
<SortableContext
|
|
||||||
items={columnOrder}
|
|
||||||
strategy={verticalListSortingStrategy}
|
|
||||||
>
|
|
||||||
{visibleHeaders.map((header) => (
|
|
||||||
<SortableColumns
|
|
||||||
key={header.id}
|
|
||||||
header={header}
|
|
||||||
onEyeClick={onEyeClick}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</SortableContext>
|
|
||||||
<Portal>
|
|
||||||
<DragOverlay dropAnimation={{ duration: 0 }}>
|
|
||||||
{draggingColumnId ? <Flex /> : null}
|
|
||||||
</DragOverlay>
|
|
||||||
</Portal>
|
|
||||||
</DndContext>
|
|
||||||
</Stack>
|
|
||||||
{hiddenHeaders.length > 0 && (
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text fontWeight="semibold" fontSize="sm">
|
<Text fontWeight="semibold" fontSize="sm">
|
||||||
Hidden in table:
|
Shown in table:
|
||||||
</Text>
|
</Text>
|
||||||
{hiddenHeaders.map((header) => (
|
<DndContext
|
||||||
<Flex key={header.id} justify="space-between">
|
sensors={sensors}
|
||||||
<HStack>
|
collisionDetection={closestCenter}
|
||||||
<HeaderIcon header={header} />
|
onDragStart={handleDragStart}
|
||||||
<Text>{header.label}</Text>
|
onDragEnd={handleDragEnd}
|
||||||
</HStack>
|
>
|
||||||
<IconButton
|
<SortableContext
|
||||||
icon={<EyeOffIcon />}
|
items={columnOrder}
|
||||||
size="sm"
|
strategy={verticalListSortingStrategy}
|
||||||
aria-label={'Hide column'}
|
>
|
||||||
onClick={onEyeClick(header.id)}
|
{visibleHeaders.map((header) => (
|
||||||
/>
|
<SortableColumns
|
||||||
</Flex>
|
key={header.id}
|
||||||
))}
|
header={header}
|
||||||
|
onEyeClick={onEyeClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SortableContext>
|
||||||
|
<Portal>
|
||||||
|
<DragOverlay dropAnimation={{ duration: 0 }}>
|
||||||
|
{draggingColumnId ? <Flex /> : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</Portal>
|
||||||
|
</DndContext>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
{hiddenHeaders.length > 0 && (
|
||||||
</PopoverBody>
|
<Stack>
|
||||||
</PopoverContent>
|
<Text fontWeight="semibold" fontSize="sm">
|
||||||
|
Hidden in table:
|
||||||
|
</Text>
|
||||||
|
{hiddenHeaders.map((header) => (
|
||||||
|
<Flex key={header.id} justify="space-between">
|
||||||
|
<HStack>
|
||||||
|
<HeaderIcon header={header} />
|
||||||
|
<Text>{header.label}</Text>
|
||||||
|
</HStack>
|
||||||
|
<IconButton
|
||||||
|
icon={<EyeOffIcon />}
|
||||||
|
size="sm"
|
||||||
|
aria-label={'Hide column'}
|
||||||
|
onClick={onEyeClick(header.id)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Portal>
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { colors } from '@/lib/theme'
|
||||||
import { Box, BoxProps, chakra, useColorModeValue } from '@chakra-ui/react'
|
import { Box, BoxProps, chakra, useColorModeValue } from '@chakra-ui/react'
|
||||||
import { flexRender, HeaderGroup } from '@tanstack/react-table'
|
import { flexRender, HeaderGroup } from '@tanstack/react-table'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@ -5,10 +6,13 @@ import { TableData } from '../../types'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
headerGroup: HeaderGroup<TableData>
|
headerGroup: HeaderGroup<TableData>
|
||||||
|
isTableScrolled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderRow = ({ headerGroup }: Props) => {
|
export const HeaderRow = ({ headerGroup, isTableScrolled }: Props) => {
|
||||||
const borderColor = useColorModeValue('gray.200', 'gray.700')
|
const borderColor = useColorModeValue(colors.gray[200], colors.gray[700])
|
||||||
|
const backgroundColor = useColorModeValue('white', colors.gray[900])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={headerGroup.id}>
|
<tr key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header) => {
|
{headerGroup.headers.map((header) => {
|
||||||
@ -16,14 +20,18 @@ export const HeaderRow = ({ headerGroup }: Props) => {
|
|||||||
<chakra.th
|
<chakra.th
|
||||||
key={header.id}
|
key={header.id}
|
||||||
px="4"
|
px="4"
|
||||||
py="2"
|
py="3"
|
||||||
pos="relative"
|
borderX="1px"
|
||||||
border="1px"
|
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
|
backgroundColor={isTableScrolled ? backgroundColor : undefined}
|
||||||
|
zIndex={10}
|
||||||
|
pos="sticky"
|
||||||
|
top="0"
|
||||||
fontWeight="normal"
|
fontWeight="normal"
|
||||||
whiteSpace="nowrap"
|
whiteSpace="nowrap"
|
||||||
wordBreak="normal"
|
wordBreak="normal"
|
||||||
colSpan={header.colSpan}
|
colSpan={header.colSpan}
|
||||||
|
shadow={`inset 0 1px 0 ${borderColor}, inset 0 -1px 0 ${borderColor}; `}
|
||||||
style={{
|
style={{
|
||||||
minWidth: header.getSize(),
|
minWidth: header.getSize(),
|
||||||
maxWidth: header.getSize(),
|
maxWidth: header.getSize(),
|
||||||
|
@ -50,6 +50,7 @@ export const ResultsTable = ({
|
|||||||
const background = useColorModeValue('white', colors.gray[900])
|
const background = useColorModeValue('white', colors.gray[900])
|
||||||
const { updateTypebot } = useTypebot()
|
const { updateTypebot } = useTypebot()
|
||||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({})
|
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({})
|
||||||
|
const [isTableScrolled, setIsTableScrolled] = useState(false)
|
||||||
const bottomElement = useRef<HTMLDivElement | null>(null)
|
const bottomElement = useRef<HTMLDivElement | null>(null)
|
||||||
const tableWrapper = useRef<HTMLDivElement | null>(null)
|
const tableWrapper = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
@ -197,13 +198,7 @@ export const ResultsTable = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack maxW="1600px" px="4" overflowY="hidden" spacing={6}>
|
||||||
maxW="1600px"
|
|
||||||
px="4"
|
|
||||||
overflow="scroll"
|
|
||||||
spacing={6}
|
|
||||||
ref={tableWrapper}
|
|
||||||
>
|
|
||||||
<Flex w="full" justifyContent="flex-end">
|
<Flex w="full" justifyContent="flex-end">
|
||||||
<ResultsActionButtons
|
<ResultsActionButtons
|
||||||
selectedResultsId={Object.keys(rowSelection)}
|
selectedResultsId={Object.keys(rowSelection)}
|
||||||
@ -219,6 +214,7 @@ export const ResultsTable = ({
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box
|
<Box
|
||||||
|
ref={tableWrapper}
|
||||||
overflow="scroll"
|
overflow="scroll"
|
||||||
rounded="md"
|
rounded="md"
|
||||||
data-testid="results-table"
|
data-testid="results-table"
|
||||||
@ -227,11 +223,18 @@ export const ResultsTable = ({
|
|||||||
backgroundRepeat="no-repeat"
|
backgroundRepeat="no-repeat"
|
||||||
backgroundSize="30px 100%, 30px 100%, 15px 100%, 15px 100%"
|
backgroundSize="30px 100%, 30px 100%, 15px 100%, 15px 100%"
|
||||||
backgroundAttachment="local, local, scroll, scroll"
|
backgroundAttachment="local, local, scroll, scroll"
|
||||||
|
onScroll={(e) =>
|
||||||
|
setIsTableScrolled((e.target as HTMLElement).scrollTop > 0)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<chakra.table rounded="md">
|
<chakra.table rounded="md">
|
||||||
<thead>
|
<thead>
|
||||||
{instance.getHeaderGroups().map((headerGroup) => (
|
{instance.getHeaderGroups().map((headerGroup) => (
|
||||||
<HeaderRow key={headerGroup.id} headerGroup={headerGroup} />
|
<HeaderRow
|
||||||
|
key={headerGroup.id}
|
||||||
|
headerGroup={headerGroup}
|
||||||
|
isTableScrolled={isTableScrolled}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ export const Row = ({
|
|||||||
cell={cell}
|
cell={cell}
|
||||||
size={cell.column.getSize()}
|
size={cell.column.getSize()}
|
||||||
isExpandButtonVisible={isExpandButtonVisible}
|
isExpandButtonVisible={isExpandButtonVisible}
|
||||||
|
rowIndex={row.index}
|
||||||
cellIndex={cellIndex}
|
cellIndex={cellIndex}
|
||||||
onExpandButtonClick={onExpandButtonClick}
|
onExpandButtonClick={onExpandButtonClick}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
|
@ -1 +1 @@
|
|||||||
export { ResultsTable as SubmissionsTable } from './ResultsTable'
|
export { ResultsTable } from './ResultsTable'
|
||||||
|
@ -4,7 +4,7 @@ import { LogsModal } from './LogsModal'
|
|||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { useResults } from '../ResultsProvider'
|
import { useResults } from '../ResultsProvider'
|
||||||
import { ResultModal } from './ResultModal'
|
import { ResultModal } from './ResultModal'
|
||||||
import { SubmissionsTable } from './ResultsTable'
|
import { ResultsTable } from './ResultsTable'
|
||||||
|
|
||||||
export const ResultsTableContainer = () => {
|
export const ResultsTableContainer = () => {
|
||||||
const {
|
const {
|
||||||
@ -35,14 +35,7 @@ export const ResultsTableContainer = () => {
|
|||||||
setExpandedResultIndex(index)
|
setExpandedResultIndex(index)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack pb="28" px={['4', '0']} spacing="4" maxW="1600px" w="full">
|
||||||
pb="28"
|
|
||||||
px={['4', '0']}
|
|
||||||
spacing="4"
|
|
||||||
maxW="1600px"
|
|
||||||
overflow="scroll"
|
|
||||||
w="full"
|
|
||||||
>
|
|
||||||
{publishedTypebot && (
|
{publishedTypebot && (
|
||||||
<LogsModal
|
<LogsModal
|
||||||
typebotId={publishedTypebot?.typebotId}
|
typebotId={publishedTypebot?.typebotId}
|
||||||
@ -56,7 +49,7 @@ export const ResultsTableContainer = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{typebot && (
|
{typebot && (
|
||||||
<SubmissionsTable
|
<ResultsTable
|
||||||
preferences={typebot.resultsTablePreferences ?? undefined}
|
preferences={typebot.resultsTablePreferences ?? undefined}
|
||||||
resultHeader={resultHeader}
|
resultHeader={resultHeader}
|
||||||
data={tableData}
|
data={tableData}
|
||||||
|
Reference in New Issue
Block a user