diff --git a/front/src/modules/ui/table/components/ColumnHead.tsx b/front/src/modules/ui/table/components/ColumnHead.tsx
index 3309caccb0..b0f15c4d75 100644
--- a/front/src/modules/ui/table/components/ColumnHead.tsx
+++ b/front/src/modules/ui/table/components/ColumnHead.tsx
@@ -14,6 +14,7 @@ const StyledTitle = styled.div`
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
padding-left: ${({ theme }) => theme.spacing(2)};
+ padding-right: ${({ theme }) => theme.spacing(2)};
`;
const StyledIcon = styled.div`
@@ -25,11 +26,17 @@ const StyledIcon = styled.div`
}
`;
+const StyledText = styled.span`
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+`;
+
export function ColumnHead({ viewName, viewIcon }: OwnProps) {
return (
{viewIcon}
- {viewName}
+ {viewName}
);
}
diff --git a/front/src/modules/ui/table/components/EntityTable.tsx b/front/src/modules/ui/table/components/EntityTable.tsx
index 18b5fb6d7c..6f9bcb5655 100644
--- a/front/src/modules/ui/table/components/EntityTable.tsx
+++ b/front/src/modules/ui/table/components/EntityTable.tsx
@@ -1,5 +1,6 @@
import * as React from 'react';
import styled from '@emotion/styled';
+import { useRecoilValue } from 'recoil';
import { SelectedSortType, SortType } from '@/ui/filter-n-sort/types/interface';
import { useListenClickOutside } from '@/ui/utilities/click-outside/hooks/useListenClickOutside';
@@ -7,6 +8,7 @@ import { useListenClickOutside } from '@/ui/utilities/click-outside/hooks/useLis
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus';
import { EntityUpdateMutationHookContext } from '../states/EntityUpdateMutationHookContext';
+import { viewFieldsFamilyState } from '../states/viewFieldsState';
import { TableHeader } from '../table-header/components/TableHeader';
import { EntityTableBody } from './EntityTableBody';
@@ -99,6 +101,8 @@ export function EntityTable({
onSortsUpdate,
useUpdateEntityMutation,
}: OwnProps) {
+ const viewFields = useRecoilValue(viewFieldsFamilyState);
+
const tableBodyRef = React.useRef(null);
useMapKeyboardToSoftFocus();
@@ -123,10 +127,12 @@ export function EntityTable({
onSortsUpdate={onSortsUpdate}
/>
-
-
-
-
+ {viewFields.length && (
+
+
+
+
+ )}
diff --git a/front/src/modules/ui/table/components/EntityTableHeader.tsx b/front/src/modules/ui/table/components/EntityTableHeader.tsx
index a217059918..53ebf7dff2 100644
--- a/front/src/modules/ui/table/components/EntityTableHeader.tsx
+++ b/front/src/modules/ui/table/components/EntityTableHeader.tsx
@@ -1,12 +1,97 @@
-import { useRecoilValue } from 'recoil';
+import { PointerEvent, useCallback, useState } from 'react';
+import styled from '@emotion/styled';
-import { viewFieldsFamilyState } from '../states/viewFieldsState';
+import { ViewFieldDefinition, ViewFieldMetadata } from '../types/ViewField';
import { ColumnHead } from './ColumnHead';
import { SelectAllCheckbox } from './SelectAllCheckbox';
-export function EntityTableHeader() {
- const viewFields = useRecoilValue(viewFieldsFamilyState);
+const COLUMN_MIN_WIDTH = 75;
+
+const StyledColumnHeaderCell = styled.th<{ isResizing?: boolean }>`
+ min-width: ${COLUMN_MIN_WIDTH}px;
+ position: relative;
+ user-select: none;
+ ${({ isResizing, theme }) => {
+ if (isResizing) {
+ return `&:after {
+ background-color: ${theme.color.blue};
+ bottom: 0;
+ content: '';
+ display: block;
+ position: absolute;
+ right: -1px;
+ top: 0;
+ width: 2px;
+ }`;
+ }
+ }};
+`;
+
+const StyledResizeHandler = styled.div`
+ bottom: 0;
+ cursor: col-resize;
+ padding: 0 ${({ theme }) => theme.spacing(2)};
+ position: absolute;
+ right: -9px;
+ top: 0;
+ width: 3px;
+ z-index: 1;
+`;
+
+type OwnProps = {
+ viewFields: ViewFieldDefinition[];
+};
+
+export function EntityTableHeader({ viewFields }: OwnProps) {
+ const initialColumnWidths = viewFields.reduce>(
+ (result, viewField) => ({
+ ...result,
+ [viewField.id]: viewField.columnSize,
+ }),
+ {},
+ );
+ const [columnWidths, setColumnWidths] = useState(initialColumnWidths);
+ const [isResizing, setIsResizing] = useState(false);
+ const [initialPointerPositionX, setInitialPointerPositionX] = useState<
+ number | null
+ >(null);
+
+ const [resizedFieldId, setResizedFieldId] = useState(null);
+ const [offset, setOffset] = useState(0);
+
+ const handleResizeHandlerDragStart = useCallback(
+ (event: PointerEvent, fieldId: string) => {
+ setIsResizing(true);
+ setResizedFieldId(fieldId);
+ setInitialPointerPositionX(event.clientX);
+ },
+ [setIsResizing, setResizedFieldId, setInitialPointerPositionX],
+ );
+
+ const handleResizeHandlerDrag = useCallback(
+ (event: PointerEvent) => {
+ if (!isResizing || initialPointerPositionX === null) return;
+
+ setOffset(event.clientX - initialPointerPositionX);
+ },
+ [isResizing, initialPointerPositionX],
+ );
+
+ const handleResizeHandlerDragEnd = useCallback(() => {
+ setIsResizing(false);
+ if (!resizedFieldId) return;
+
+ const newColumnWidths = {
+ ...columnWidths,
+ [resizedFieldId]: Math.max(
+ columnWidths[resizedFieldId] + offset,
+ COLUMN_MIN_WIDTH,
+ ),
+ };
+ setColumnWidths(newColumnWidths);
+ setOffset(0);
+ }, [offset, setIsResizing, columnWidths, resizedFieldId]);
return (
@@ -20,20 +105,34 @@ export function EntityTableHeader() {
>
+
{viewFields.map((viewField) => (
-
- |
+
+ handleResizeHandlerDragStart(event, viewField.id)
+ }
+ onPointerMove={handleResizeHandlerDrag}
+ onPointerOut={handleResizeHandlerDragEnd}
+ onPointerUp={handleResizeHandlerDragEnd}
+ />
+
))}
|