feat: add resizable kanban column width (#21828)
## What & why Lets users resize the columns of a Kanban (record board) view. Requested by a user; the design avoids the "ragged board" problem by making the width a **single shared value**. ## Behaviour - A drag handle appears on the right edge of every column header. - Because all columns read **one** width value, dragging any handle resizes **every** column together — they can never end up mismatched. - Width is clamped between **150px** and **400px** (default **200px**). - The width is **persisted per view** and restored on reload. ## Approach **Backend** — a new nullable `View.kanbanColumnWidth` field, threaded through the existing view-level setting pattern (the same one `kanbanAggregateOperation` / `shouldHideEmptyGroups` use), so it gets create/update/manifest/override support for free: - entity column + `ViewOverrides` + `@WasIntroducedInUpgrade` - `CreateViewInput` / `UpdateViewInput` (`Int`, `@Min(150)`/`@Max(400)`) + `ViewDTO` - flat-view editable properties, entity-properties config, compare-type, standard-view + manifest converters - a fast instance command adding the `core.view` column **Frontend** — the value hydrates into a view-scoped atom and drives a single CSS variable set on the board container, which both column headers and bodies read. Live dragging only writes that CSS variable (no per-move React re-render); the final width is committed to the atom and persisted via `updateView` on pointer-up. ## Nullability / defaults `kanbanColumnWidth` is nullable — `null` means "never resized" and the UI falls back to the 200px default, so existing rows need no backfill. ## Validation - `nx typecheck twenty-server` ✅ and `nx typecheck twenty-front` ✅ - `nx lint:diff-with-main twenty-server` ✅; frontend lint fixes applied (split constants to one-per-file, removed `useRef`-for-state in favour of `useState`). - Draft pending a final green CI run (the dev container reclaimed `node_modules` mid-session; re-running locally). ## Test plan - [ ] Drag a kanban column edge → all columns resize together, clamped 150–400px - [ ] Reload → width persists for that view; other views unaffected - [ ] A view that was never resized still renders at 200px https://claude.ai/code/session_016Qe6oDBkhVbrq2QkXBJ5nE --- _Generated by [Claude Code](https://claude.ai/code/session_016Qe6oDBkhVbrq2QkXBJ5nE)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21828?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -3,6 +3,7 @@ import { styled } from '@linaria/react';
|
||||
import { useContext, useRef } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { RecordBoardColumnWidthEffect } from '@/object-record/record-board/components/RecordBoardColumnWidthEffect';
|
||||
import { RecordBoardColumns } from '@/object-record/record-board/components/RecordBoardColumns';
|
||||
import { RecordBoardDragDropContext } from '@/object-record/record-board/components/RecordBoardDragDropContext';
|
||||
import { RecordBoardDragSelect } from '@/object-record/record-board/components/RecordBoardDragSelect';
|
||||
@@ -12,6 +13,8 @@ import { RecordBoardHeader } from '@/object-record/record-board/components/Recor
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
import { getRecordBoardHtmlId } from '@/object-record/record-board/utils/getRecordBoardHtmlId';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
@@ -43,7 +46,8 @@ export const RecordBoard = () => {
|
||||
componentInstanceId={`scroll-wrapper-record-board-${recordBoardId}`}
|
||||
>
|
||||
<RecordBoardEffects />
|
||||
<StyledContainerContainer>
|
||||
<RecordBoardColumnWidthEffect />
|
||||
<StyledContainerContainer id={getRecordBoardHtmlId(recordBoardId)}>
|
||||
<RecordBoardHeader />
|
||||
<StyledBoardContentContainer>
|
||||
<StyledContainer ref={boardRef}>
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { useContext, useLayoutEffect } from 'react';
|
||||
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { setRecordBoardColumnWidthCssVariable } from '@/object-record/record-board/utils/setRecordBoardColumnWidthCssVariable';
|
||||
import { recordIndexKanbanColumnWidthComponentState } from '@/object-record/record-index/states/recordIndexKanbanColumnWidthComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const RecordBoardColumnWidthEffect = () => {
|
||||
const { recordBoardId } = useContext(RecordBoardContext);
|
||||
|
||||
const recordIndexKanbanColumnWidth = useAtomComponentStateValue(
|
||||
recordIndexKanbanColumnWidthComponentState,
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setRecordBoardColumnWidthCssVariable(
|
||||
recordBoardId,
|
||||
recordIndexKanbanColumnWidth,
|
||||
);
|
||||
}, [recordBoardId, recordIndexKanbanColumnWidth]);
|
||||
|
||||
return null;
|
||||
};
|
||||
+6
-2
@@ -5,7 +5,7 @@ import { useInView } from 'react-intersection-observer';
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { RECORD_BOARD_COLUMN_PADDING_AND_BORDER_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnPaddingAndBorderWidth';
|
||||
|
||||
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
|
||||
import { recordIndexKanbanColumnWidthComponentState } from '@/object-record/record-index/states/recordIndexKanbanColumnWidthComponentState';
|
||||
import { RECORD_BOARD_QUERY_PAGE_SIZE } from '@/object-record/record-board/constants/RecordBoardQueryPageSize';
|
||||
import { recordBoardShouldFetchMoreComponentState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreComponentState';
|
||||
import { isDraggingRecordComponentState } from '@/object-record/record-drag/states/isDraggingRecordComponentState';
|
||||
@@ -69,8 +69,12 @@ export const RecordBoardFetchMoreInViewTriggerComponent = () => {
|
||||
ViewType.KANBAN,
|
||||
);
|
||||
|
||||
const recordIndexKanbanColumnWidth = useAtomComponentStateValue(
|
||||
recordIndexKanbanColumnWidthComponentState,
|
||||
);
|
||||
|
||||
const componentWidth =
|
||||
visibleRecordGroupIds.length * RECORD_BOARD_COLUMN_WIDTH +
|
||||
visibleRecordGroupIds.length * recordIndexKanbanColumnWidth +
|
||||
visibleRecordGroupIds.length *
|
||||
RECORD_BOARD_COLUMN_PADDING_AND_BORDER_WIDTH -
|
||||
1;
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const RECORD_BOARD_COLUMN_MAX_WIDTH = 400;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const RECORD_BOARD_COLUMN_MIN_WIDTH = 150;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME =
|
||||
'--record-board-column-width';
|
||||
+10
-2
@@ -2,6 +2,8 @@ import { styled } from '@linaria/react';
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
|
||||
import { RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME } from '@/object-record/record-board/constants/RecordBoardColumnWidthCssVariableName';
|
||||
import { RecordBoardColumnCardsContainer } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { useShouldHideRecordGroup } from '@/object-record/record-group/hooks/useShouldHideRecordGroup';
|
||||
@@ -16,8 +18,14 @@ const StyledColumn = styled.div`
|
||||
background-color: ${themeCssVariables.background.primary};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 200px;
|
||||
min-width: 200px;
|
||||
max-width: var(
|
||||
${RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME},
|
||||
${RECORD_BOARD_COLUMN_WIDTH}px
|
||||
);
|
||||
min-width: var(
|
||||
${RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME},
|
||||
${RECORD_BOARD_COLUMN_WIDTH}px
|
||||
);
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
padding-top: 0px;
|
||||
position: relative;
|
||||
|
||||
+11
-2
@@ -8,6 +8,8 @@ import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/reco
|
||||
import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown';
|
||||
|
||||
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
|
||||
import { RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME } from '@/object-record/record-board/constants/RecordBoardColumnWidthCssVariableName';
|
||||
import { RecordBoardColumnResizeHandler } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnResizeHandler';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
|
||||
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
|
||||
@@ -61,8 +63,14 @@ const StyledColumn = styled.div`
|
||||
background-color: ${themeCssVariables.background.primary};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: ${RECORD_BOARD_COLUMN_WIDTH}px;
|
||||
min-width: ${RECORD_BOARD_COLUMN_WIDTH}px;
|
||||
max-width: var(
|
||||
${RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME},
|
||||
${RECORD_BOARD_COLUMN_WIDTH}px
|
||||
);
|
||||
min-width: var(
|
||||
${RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME},
|
||||
${RECORD_BOARD_COLUMN_WIDTH}px
|
||||
);
|
||||
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
|
||||
@@ -202,6 +210,7 @@ export const RecordBoardColumnHeader = () => {
|
||||
</StyledRightContainer>
|
||||
</StyledHeaderContainer>
|
||||
</StyledHeader>
|
||||
<RecordBoardColumnResizeHandler />
|
||||
</StyledColumn>
|
||||
);
|
||||
};
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
import { useResizeBoardColumn } from '@/object-record/record-board/record-board-column/hooks/useResizeBoardColumn';
|
||||
import { RecordColumnResizeHandle } from '@/object-record/record-index/components/RecordColumnResizeHandle';
|
||||
|
||||
export const RecordBoardColumnResizeHandler = () => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const { isResizing, handleResizeStart } = useResizeBoardColumn();
|
||||
|
||||
if (isMobile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<RecordColumnResizeHandle
|
||||
isResizing={isResizing}
|
||||
position="right"
|
||||
onPointerDown={handleResizeStart}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
import {
|
||||
type PointerEvent as ReactPointerEvent,
|
||||
useCallback,
|
||||
useContext,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { clampRecordBoardColumnWidth } from '@/object-record/record-board/utils/clampRecordBoardColumnWidth';
|
||||
import { setRecordBoardColumnWidthCssVariable } from '@/object-record/record-board/utils/setRecordBoardColumnWidthCssVariable';
|
||||
import { recordIndexKanbanColumnWidthComponentState } from '@/object-record/record-index/states/recordIndexKanbanColumnWidthComponentState';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
import { type PointerEventListener } from '@/ui/utilities/pointer-event/types/PointerEventListener';
|
||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useUpdateViewKanbanColumnWidth } from '@/views/hooks/useUpdateViewKanbanColumnWidth';
|
||||
|
||||
export const useResizeBoardColumn = () => {
|
||||
const { recordBoardId } = useContext(RecordBoardContext);
|
||||
|
||||
const [recordIndexKanbanColumnWidth, setRecordIndexKanbanColumnWidth] =
|
||||
useAtomComponentState(recordIndexKanbanColumnWidthComponentState);
|
||||
|
||||
const { updateViewKanbanColumnWidth } = useUpdateViewKanbanColumnWidth();
|
||||
|
||||
const { setDragSelectionStartEnabled } = useDragSelect();
|
||||
|
||||
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
|
||||
const isResizing = isDefined(initialPointerPositionX);
|
||||
|
||||
const handleResizeStart = useCallback(
|
||||
(event: ReactPointerEvent<HTMLDivElement>) => {
|
||||
if (event.pointerType !== 'mouse') {
|
||||
return;
|
||||
}
|
||||
|
||||
setDragSelectionStartEnabled(false);
|
||||
setInitialPointerPositionX(event.clientX);
|
||||
},
|
||||
[setDragSelectionStartEnabled],
|
||||
);
|
||||
|
||||
const handleResizeMove = useCallback<PointerEventListener>(
|
||||
({ x, event }) => {
|
||||
if (!isDefined(initialPointerPositionX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('buttons' in event && event.buttons === 0) {
|
||||
setInitialPointerPositionX(null);
|
||||
setDragSelectionStartEnabled(true);
|
||||
setRecordBoardColumnWidthCssVariable(
|
||||
recordBoardId,
|
||||
recordIndexKanbanColumnWidth,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setRecordBoardColumnWidthCssVariable(
|
||||
recordBoardId,
|
||||
clampRecordBoardColumnWidth(
|
||||
recordIndexKanbanColumnWidth + (x - initialPointerPositionX),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
initialPointerPositionX,
|
||||
recordIndexKanbanColumnWidth,
|
||||
recordBoardId,
|
||||
setDragSelectionStartEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
const handleResizeEnd = useCallback<PointerEventListener>(
|
||||
({ x }) => {
|
||||
if (!isDefined(initialPointerPositionX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setInitialPointerPositionX(null);
|
||||
setDragSelectionStartEnabled(true);
|
||||
|
||||
const nextWidth = Math.round(
|
||||
clampRecordBoardColumnWidth(
|
||||
recordIndexKanbanColumnWidth + (x - initialPointerPositionX),
|
||||
),
|
||||
);
|
||||
|
||||
if (nextWidth !== recordIndexKanbanColumnWidth) {
|
||||
setRecordIndexKanbanColumnWidth(nextWidth);
|
||||
updateViewKanbanColumnWidth(nextWidth);
|
||||
}
|
||||
},
|
||||
[
|
||||
initialPointerPositionX,
|
||||
recordIndexKanbanColumnWidth,
|
||||
setRecordIndexKanbanColumnWidth,
|
||||
updateViewKanbanColumnWidth,
|
||||
setDragSelectionStartEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
useTrackPointer({
|
||||
shouldTrackPointer: isResizing,
|
||||
onMouseMove: handleResizeMove,
|
||||
onMouseUp: handleResizeEnd,
|
||||
});
|
||||
|
||||
return { isResizing, handleResizeStart };
|
||||
};
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { RECORD_BOARD_COLUMN_MAX_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnMaxWidth';
|
||||
import { RECORD_BOARD_COLUMN_MIN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnMinWidth';
|
||||
|
||||
export const clampRecordBoardColumnWidth = (width: number): number =>
|
||||
Math.min(
|
||||
RECORD_BOARD_COLUMN_MAX_WIDTH,
|
||||
Math.max(RECORD_BOARD_COLUMN_MIN_WIDTH, width),
|
||||
);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const getRecordBoardHtmlId = (recordBoardId: string): string =>
|
||||
`record-board-${recordBoardId}`;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME } from '@/object-record/record-board/constants/RecordBoardColumnWidthCssVariableName';
|
||||
import { getRecordBoardHtmlId } from '@/object-record/record-board/utils/getRecordBoardHtmlId';
|
||||
|
||||
export const setRecordBoardColumnWidthCssVariable = (
|
||||
recordBoardId: string,
|
||||
widthInPixels: number,
|
||||
) => {
|
||||
document
|
||||
.getElementById(getRecordBoardHtmlId(recordBoardId))
|
||||
?.style.setProperty(
|
||||
RECORD_BOARD_COLUMN_WIDTH_CSS_VARIABLE_NAME,
|
||||
`${widthInPixels}px`,
|
||||
);
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { type PointerEvent as ReactPointerEvent } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRecordColumnResizeHandle = styled.div<{
|
||||
isResizing: boolean;
|
||||
position: 'left' | 'right';
|
||||
}>`
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
left: ${({ position }) => (position === 'left' ? '-1px' : 'auto')};
|
||||
position: absolute;
|
||||
right: ${({ position }) => (position === 'right' ? '-1px' : 'auto')};
|
||||
top: 0;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
background-color: ${themeCssVariables.color.blue};
|
||||
bottom: 0;
|
||||
content: '';
|
||||
display: ${({ isResizing }) => (isResizing ? 'block' : 'none')};
|
||||
left: ${({ position }) => (position === 'left' ? '-1px' : 'auto')};
|
||||
position: absolute;
|
||||
right: ${({ position }) => (position === 'right' ? '-1px' : 'auto')};
|
||||
top: 0;
|
||||
width: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
type RecordColumnResizeHandleProps = {
|
||||
isResizing: boolean;
|
||||
position: 'left' | 'right';
|
||||
onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;
|
||||
};
|
||||
|
||||
export const RecordColumnResizeHandle = ({
|
||||
isResizing,
|
||||
position,
|
||||
onPointerDown,
|
||||
}: RecordColumnResizeHandleProps) => (
|
||||
<StyledRecordColumnResizeHandle
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
isResizing={isResizing}
|
||||
position={position}
|
||||
onPointerDown={onPointerDown}
|
||||
/>
|
||||
);
|
||||
+15
@@ -15,10 +15,13 @@ import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
|
||||
import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState';
|
||||
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
|
||||
import { clampRecordBoardColumnWidth } from '@/object-record/record-board/utils/clampRecordBoardColumnWidth';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { recordIndexGroupAggregateFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupAggregateFieldMetadataItemComponentState';
|
||||
import { recordIndexGroupAggregateOperationComponentState } from '@/object-record/record-index/states/recordIndexGroupAggregateOperationComponentState';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { recordIndexKanbanColumnWidthComponentState } from '@/object-record/record-index/states/recordIndexKanbanColumnWidthComponentState';
|
||||
import { recordIndexShouldHideEmptyRecordGroupsComponentState } from '@/object-record/record-index/states/recordIndexShouldHideEmptyRecordGroupsComponentState';
|
||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||
import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState';
|
||||
@@ -68,6 +71,10 @@ export const useLoadRecordIndexStates = () => {
|
||||
recordIndexShouldHideEmptyRecordGroupsComponentState,
|
||||
);
|
||||
|
||||
const recordIndexKanbanColumnWidthAtom = useAtomComponentStateCallbackState(
|
||||
recordIndexKanbanColumnWidthComponentState,
|
||||
);
|
||||
|
||||
const { getFieldMetadataItemByIdOrThrow } =
|
||||
useGetFieldMetadataItemByIdOrThrow();
|
||||
|
||||
@@ -318,6 +325,13 @@ export const useLoadRecordIndexStates = () => {
|
||||
view.shouldHideEmptyGroups,
|
||||
);
|
||||
|
||||
batchSet(
|
||||
recordIndexKanbanColumnWidthAtom,
|
||||
clampRecordBoardColumnWidth(
|
||||
view.kanbanColumnWidth ?? RECORD_BOARD_COLUMN_WIDTH,
|
||||
),
|
||||
);
|
||||
|
||||
if (isDefined(recordIndexGroupFieldMetadataItemValue)) {
|
||||
batchSet(
|
||||
recordIndexGroupFieldMetadataItemAtom,
|
||||
@@ -355,6 +369,7 @@ export const useLoadRecordIndexStates = () => {
|
||||
recordIndexGroupAggregateOperationAtom,
|
||||
recordIndexGroupAggregateFieldMetadataItemAtom,
|
||||
recordIndexShouldHideEmptyRecordGroupsAtom,
|
||||
recordIndexKanbanColumnWidthAtom,
|
||||
getFieldMetadataItemByIdOrThrow,
|
||||
setRecordGroupsFromViewGroups,
|
||||
syncRecordIndexViewFields,
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth';
|
||||
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
|
||||
export const recordIndexKanbanColumnWidthComponentState =
|
||||
createAtomComponentState<number>({
|
||||
key: 'recordIndexKanbanColumnWidthComponentState',
|
||||
defaultValue: RECORD_BOARD_COLUMN_WIDTH,
|
||||
componentInstanceContext: ViewComponentInstanceContext,
|
||||
});
|
||||
+3
-32
@@ -1,38 +1,11 @@
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { RecordColumnResizeHandle } from '@/object-record/record-index/components/RecordColumnResizeHandle';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { resizedFieldMetadataIdComponentState } from '@/object-record/record-table/states/resizedFieldMetadataIdComponentState';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledResizeHandler = styled.div<{
|
||||
isResizing: boolean;
|
||||
position: 'left' | 'right';
|
||||
}>`
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
left: ${({ position }) => (position === 'left' ? '-1px' : 'auto')};
|
||||
position: absolute;
|
||||
right: ${({ position }) => (position === 'right' ? '-1px' : 'auto')};
|
||||
top: 0;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
background-color: ${themeCssVariables.color.blue};
|
||||
bottom: 0;
|
||||
content: '';
|
||||
display: ${({ isResizing }) => (isResizing ? 'block' : 'none')};
|
||||
left: ${({ position }) => (position === 'left' ? '-1px' : 'auto')};
|
||||
position: absolute;
|
||||
right: ${({ position }) => (position === 'right' ? '-1px' : 'auto')};
|
||||
top: 0;
|
||||
width: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RecordTableHeaderResizeHandler = ({
|
||||
recordFieldIndex,
|
||||
position,
|
||||
@@ -66,12 +39,10 @@ export const RecordTableHeaderResizeHandler = ({
|
||||
|
||||
return (
|
||||
!columnResizeDisabled && (
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={handlePointerDown}
|
||||
<RecordColumnResizeHandle
|
||||
isResizing={isResizing}
|
||||
position={position}
|
||||
onPointerDown={handlePointerDown}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user