diff --git a/browser/data-browser/src/components/TableEditor/TableHeader.tsx b/browser/data-browser/src/components/TableEditor/TableHeader.tsx index e82426f2f..081202cf1 100644 --- a/browser/data-browser/src/components/TableEditor/TableHeader.tsx +++ b/browser/data-browser/src/components/TableEditor/TableHeader.tsx @@ -10,17 +10,23 @@ import { DragEndEvent, DragOverlay, DragStartEvent, + DraggableAttributes, useDndMonitor, } from '@dnd-kit/core'; import { createPortal } from 'react-dom'; import { ColumnReorderHandler } from './types'; import { ReorderDropArea } from './ReorderDropArea'; +import { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities'; -export type TableHeadingComponent = ({ - column, -}: { +type TableHeadingComponentProps = { column: T; -}) => JSX.Element; + dragListeners?: SyntheticListenerMap; + dragAttributes?: DraggableAttributes; +}; + +export type TableHeadingComponent = ( + props: TableHeadingComponentProps, +) => JSX.Element; export interface TableHeaderProps { columns: T[]; @@ -100,9 +106,9 @@ export function TableHeader({ index={index} onResize={onResize} isReordering={activeIndex !== undefined} - > - - + column={column} + HeadingComponent={HeadingComponent} + /> ))} diff --git a/browser/data-browser/src/components/TableEditor/TableHeading.tsx b/browser/data-browser/src/components/TableEditor/TableHeading.tsx index a9a2f8ffb..657da5ea1 100644 --- a/browser/data-browser/src/components/TableEditor/TableHeading.tsx +++ b/browser/data-browser/src/components/TableEditor/TableHeading.tsx @@ -6,22 +6,26 @@ import { useDraggable } from '@dnd-kit/core'; import { ReorderDropArea } from './ReorderDropArea'; import { transparentize } from 'polished'; import { DEFAULT_SIZE_PX } from './hooks/useCellSizes'; +import type { TableHeadingComponent } from './TableHeader'; -interface TableHeadingProps { +interface TableHeadingProps { index: number; dragKey: string; onResize: (index: number, size: string) => void; isReordering: boolean; + HeadingComponent: TableHeadingComponent; + column: T; } /** A single column header, mostly used to render Properties */ -export function TableHeading({ - children, +export function TableHeading({ dragKey, index, isReordering, + column, onResize, -}: React.PropsWithChildren): JSX.Element { + HeadingComponent, +}: TableHeadingProps): JSX.Element { const { attributes, listeners, @@ -57,8 +61,11 @@ export function TableHeading({ role='columnheader' aria-colindex={index + 2} > - {children} - + {isReordering && } @@ -108,12 +115,3 @@ const ResizeHandle = styled(DragAreaBase)` z-index: 10; position: absolute; `; - -const ReorderHandle = styled.button` - border: none; - background: none; - position: absolute; - inset: 0; - cursor: grab; - z-index: -1; -`; diff --git a/browser/data-browser/src/hooks/useResizable.ts b/browser/data-browser/src/hooks/useResizable.ts index 058ed0007..26d6c7505 100644 --- a/browser/data-browser/src/hooks/useResizable.ts +++ b/browser/data-browser/src/hooks/useResizable.ts @@ -152,7 +152,7 @@ export const DragAreaBase = styled.div` backdrop-filter: ${({ isDragging }) => (isDragging ? 'blur(5px)' : 'none')}; - :hover { + &:hover { transition: background-color 0.2s; background-color: var(--drag-color); backdrop-filter: blur(5px); diff --git a/browser/data-browser/src/views/TablePage/TableHeading.tsx b/browser/data-browser/src/views/TablePage/TableHeading.tsx index 051463408..4a2aa970b 100644 --- a/browser/data-browser/src/views/TablePage/TableHeading.tsx +++ b/browser/data-browser/src/views/TablePage/TableHeading.tsx @@ -7,62 +7,78 @@ import { } from '@tomic/react'; import { FaAngleDown, FaAngleUp, FaAtom } from 'react-icons/fa'; +import { FaGripVertical } from 'react-icons/fa6'; import { styled } from 'styled-components'; import { dataTypeIconMap } from './dataTypeMaps'; import { TableHeadingMenu } from './TableHeadingMenu'; import { TablePageContext } from './tablePageContext'; import { IconType } from 'react-icons'; import { TableSorting } from './tableSorting'; -import { useContext } from 'react'; - -export interface TableHeadingProps { - column: Property; -} +import { useContext, useState } from 'react'; +import { TableHeadingComponent } from '../../components/TableEditor/TableHeader'; function getIcon( propResource: Resource, sorting: TableSorting, + hoverOrFocus: boolean, dataType: Datatype, ): IconType { if (sorting.prop === propResource.getSubject()) { return sorting.sortDesc ? FaAngleDown : FaAngleUp; } + if (hoverOrFocus) { + return FaGripVertical; + } + return dataTypeIconMap.get(dataType) ?? FaAtom; } -export function TableHeading({ column }: TableHeadingProps): JSX.Element { +export const TableHeading: TableHeadingComponent = ({ + column, + dragListeners, + dragAttributes, +}): JSX.Element => { + const [hoverOrFocus, setHoverOrFocus] = useState(false); + const propResource = useResource(column.subject); const [title] = useTitle(propResource); const { setSortBy, sorting } = useContext(TablePageContext); - const Icon = getIcon(propResource, sorting, column.datatype); + const Icon = getIcon(propResource, sorting, hoverOrFocus, column.datatype); const isSorted = sorting.prop === propResource.getSubject(); + const text = title || column.shortname; + return ( <> - - + setHoverOrFocus(true)} + onMouseLeave={() => setHoverOrFocus(false)} + onFocus={() => setHoverOrFocus(true)} + onBlur={() => setHoverOrFocus(false)} + > + + + setSortBy(propResource.getSubject())} bold={isSorted} + title={text} > - {title || column.shortname} + {text} + - ); -} +}; const Wrapper = styled.div` display: flex; align-items: center; gap: 0.5rem; - - svg { - color: currentColor; - } + width: 100%; `; interface NameButtonProps { @@ -75,8 +91,28 @@ const NameButton = styled.button` color: currentColor; cursor: pointer; font-weight: ${p => (p.bold ? 'bold' : 'normal')}; - // TODO: make this dynamic, don't overflow on names, use grid flex? - max-width: 8rem; overflow: hidden; text-overflow: ellipsis; + padding: 0; +`; + +const DragIconButton = styled.button` + background: none; + color: currentColor; + display: flex; + align-items: center; + border: none; + height: 1rem; + padding: 0; + cursor: grab; + + &:active { + cursor: grabbing; + } + svg { + color: currentColor; + max-width: 1rem; + min-width: 1rem; + flex: 1; + } `;