Increase size of input in test setting logic function tab (#18369)
## Before <img width="1031" height="836" alt="image" src="https://github.com/user-attachments/assets/475ca1be-f7c4-49d0-b329-649dbe8da489" /> ## After <img width="1195" height="862" alt="image" src="https://github.com/user-attachments/assets/b1bac131-e562-4439-8f8e-bda4d6e2a646" /> --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+1
-6
@@ -8,11 +8,9 @@ import { LogicFunctionExecutionStatus } from '~/generated-metadata/graphql';
|
||||
|
||||
export const LogicFunctionExecutionResult = ({
|
||||
logicFunctionTestData,
|
||||
maxHeight,
|
||||
isTesting = false,
|
||||
}: {
|
||||
logicFunctionTestData: LogicFunctionTestData;
|
||||
maxHeight?: number;
|
||||
isTesting?: boolean;
|
||||
}) => {
|
||||
const result =
|
||||
@@ -39,10 +37,7 @@ export const LogicFunctionExecutionResult = ({
|
||||
<WorkflowStepExecutionResult
|
||||
result={result}
|
||||
language={logicFunctionTestData.language}
|
||||
height={Math.min(
|
||||
logicFunctionTestData.height,
|
||||
maxHeight ?? logicFunctionTestData.height,
|
||||
)}
|
||||
height={150}
|
||||
status={status}
|
||||
isTesting={isTesting}
|
||||
loadingMessage={t`Running function`}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { ResizeHandle, useResizeHandle } from 'twenty-ui/layout';
|
||||
import { TextArea } from '@/ui/input/components/TextArea';
|
||||
|
||||
type LogicFunctionLogsProps = {
|
||||
componentInstanceId: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export const LogicFunctionLogs = ({
|
||||
componentInstanceId,
|
||||
value,
|
||||
}: LogicFunctionLogsProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const {
|
||||
size: height,
|
||||
handleResizeStart,
|
||||
handleResizeMove,
|
||||
handleResizeEnd,
|
||||
} = useResizeHandle({
|
||||
initialSize: 150,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextArea
|
||||
textAreaId={`logs-${componentInstanceId}`}
|
||||
label={t`Logs`}
|
||||
value={value}
|
||||
height={height}
|
||||
readOnly
|
||||
/>
|
||||
<ResizeHandle
|
||||
onPointerDown={handleResizeStart}
|
||||
onPointerMove={handleResizeMove}
|
||||
onPointerUp={handleResizeEnd}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -75,7 +75,6 @@ export const useExecuteLogicFunction = ({
|
||||
setLogicFunctionTestData((prev) => ({
|
||||
...prev,
|
||||
language: 'json',
|
||||
height: 300,
|
||||
output: {
|
||||
data: executionResult?.data
|
||||
? JSON.stringify(executionResult.data, null, 4)
|
||||
|
||||
+10
-25
@@ -6,6 +6,7 @@ import {
|
||||
IconRadiusTopLeft,
|
||||
IconRadiusTopRight,
|
||||
} from 'twenty-ui/display';
|
||||
import { ResizeHandle } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
|
||||
@@ -67,28 +68,6 @@ const StyledVerticalHandle = styled.div`
|
||||
width: ${themeCssVariables.icon.stroke.lg}px;
|
||||
`;
|
||||
|
||||
const StyledHorizontalHandle = styled.div`
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
height: ${themeCssVariables.icon.stroke.lg}px;
|
||||
width: ${themeCssVariables.spacing[5]};
|
||||
`;
|
||||
|
||||
const StyledHorizontalHandleWrapper = styled.div<{
|
||||
widgetHandleAxis: WidgetHorizontalHandleAxis;
|
||||
}>`
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
cursor: row-resize;
|
||||
transform: ${({ widgetHandleAxis }) =>
|
||||
widgetHandleAxis === 'n' ? 'translateY(-50%)' : 'translateY(50%)'};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
:hover {
|
||||
& > div {
|
||||
background-color: ${themeCssVariables.font.color.tertiary};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledVerticalHandleWrapper = styled.div<{
|
||||
widgetHandleAxis: WidgetVerticalHandleAxis;
|
||||
}>`
|
||||
@@ -201,9 +180,15 @@ export const PageLayoutGridResizeHandle = forwardRef<
|
||||
</StyledVerticalHandleWrapper>
|
||||
)}
|
||||
{isHorizontalHandle(widgetHandleAxis) && (
|
||||
<StyledHorizontalHandleWrapper widgetHandleAxis={widgetHandleAxis}>
|
||||
<StyledHorizontalHandle />
|
||||
</StyledHorizontalHandleWrapper>
|
||||
<ResizeHandle
|
||||
style={{
|
||||
cursor: 'row-resize',
|
||||
transform:
|
||||
widgetHandleAxis === 'n'
|
||||
? 'translateY(-50%)'
|
||||
: 'translateY(50%)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{widgetHandleAxis === 'ne' && (
|
||||
<StyledCornerIconWrapper cursor="nesw-resize" position="ne">
|
||||
|
||||
+4
-12
@@ -1,4 +1,5 @@
|
||||
import { LogicFunctionExecutionResult } from '@/logic-functions/components/LogicFunctionExecutionResult';
|
||||
import { LogicFunctionLogs } from '@/logic-functions/components/LogicFunctionLogs';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
|
||||
import { logicFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/logicFunctionTestDataFamilyState';
|
||||
@@ -8,8 +9,6 @@ import { H2Title, IconPlayerPlay } from 'twenty-ui/display';
|
||||
import { Button, CodeEditor, CoreEditorHeader } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { TextArea } from '@/ui/input/components/TextArea';
|
||||
|
||||
const StyledInputsContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -48,8 +47,6 @@ export const SettingsLogicFunctionTestTab = ({
|
||||
}));
|
||||
};
|
||||
|
||||
const testLogsTextAreaId = `${logicFunctionId}-test-logs`;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
@@ -78,23 +75,18 @@ export const SettingsLogicFunctionTestTab = ({
|
||||
height={100}
|
||||
onChange={onChange}
|
||||
variant="with-header"
|
||||
resizable
|
||||
/>
|
||||
</StyledCodeEditorContainer>
|
||||
<LogicFunctionExecutionResult
|
||||
logicFunctionTestData={logicFunctionTestData}
|
||||
maxHeight={
|
||||
logicFunctionTestData.output.logs.length > 0 ? 200 : undefined
|
||||
}
|
||||
isTesting={isTesting}
|
||||
/>
|
||||
{logicFunctionTestData.output.logs.length > 0 && (
|
||||
<StyledCodeEditorContainer>
|
||||
<InputLabel>{t`Logs`}</InputLabel>
|
||||
<TextArea
|
||||
textAreaId={testLogsTextAreaId}
|
||||
<LogicFunctionLogs
|
||||
componentInstanceId={`settings-logic-function-logs-${logicFunctionId}`}
|
||||
value={isTesting ? '' : logicFunctionTestData.output.logs}
|
||||
maxRows={20}
|
||||
disabled
|
||||
/>
|
||||
</StyledCodeEditorContainer>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
@@ -14,6 +15,7 @@ export type TextAreaProps = {
|
||||
textAreaId: string;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
height?: number;
|
||||
minRows?: number;
|
||||
maxRows?: number;
|
||||
onChange?: (value: string) => void;
|
||||
@@ -73,6 +75,7 @@ export const TextArea = ({
|
||||
textAreaId,
|
||||
label,
|
||||
disabled,
|
||||
height,
|
||||
placeholder,
|
||||
minRows = 1,
|
||||
maxRows = MAX_ROWS,
|
||||
@@ -126,6 +129,7 @@ export const TextArea = ({
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
readOnly={readOnly}
|
||||
style={isDefined(height) ? { height } : undefined}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
+1
-2
@@ -16,14 +16,12 @@ const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
`;
|
||||
|
||||
const StyledCodeEditorWrapper = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 200px;
|
||||
`;
|
||||
|
||||
type OutputAccent = 'default' | 'success' | 'error';
|
||||
@@ -137,6 +135,7 @@ export const WorkflowStepExecutionResult = ({
|
||||
/>
|
||||
<StyledCodeEditorWrapper>
|
||||
<CodeEditor
|
||||
resizable={true}
|
||||
value={result}
|
||||
language={language}
|
||||
height={height}
|
||||
|
||||
+3
-13
@@ -10,9 +10,9 @@ import { setNestedValue } from '@/workflow/workflow-steps/workflow-actions/code-
|
||||
|
||||
import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton';
|
||||
import { LogicFunctionExecutionResult } from '@/logic-functions/components/LogicFunctionExecutionResult';
|
||||
import { LogicFunctionLogs } from '@/logic-functions/components/LogicFunctionLogs';
|
||||
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/mergeDefaultFunctionInputAndFunctionInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { TextArea } from '@/ui/input/components/TextArea';
|
||||
import { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
@@ -50,15 +50,10 @@ import { useDebouncedCallback } from 'use-debounce';
|
||||
import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const CODE_EDITOR_MIN_HEIGHT = 343;
|
||||
|
||||
const StyledCodeEditorContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: ${CODE_EDITOR_MIN_HEIGHT}px;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledTabList = styled(TabList)`
|
||||
@@ -304,8 +299,6 @@ export const WorkflowEditActionCode = ({
|
||||
dependencies: [isFullScreen],
|
||||
});
|
||||
|
||||
const testLogsTextAreaId = `${logicFunctionId}-test-logs`;
|
||||
|
||||
const breadcrumbLinks: BreadcrumbProps['links'] = [
|
||||
{
|
||||
children: workflow?.name?.trim() || t`Untitled Workflow`,
|
||||
@@ -429,12 +422,9 @@ export const WorkflowEditActionCode = ({
|
||||
</StyledCodeEditorContainer>
|
||||
{logicFunctionTestData.output.logs.length > 0 && (
|
||||
<StyledCodeEditorContainer>
|
||||
<InputLabel>{t`Logs`}</InputLabel>
|
||||
<TextArea
|
||||
textAreaId={testLogsTextAreaId}
|
||||
<LogicFunctionLogs
|
||||
componentInstanceId={`workflow-edit-action-logs-${action.id}`}
|
||||
value={isExecuting ? '' : logicFunctionTestData.output.logs}
|
||||
maxRows={20}
|
||||
disabled
|
||||
/>
|
||||
</StyledCodeEditorContainer>
|
||||
)}
|
||||
|
||||
-2
@@ -12,7 +12,6 @@ export type LogicFunctionTestData = {
|
||||
error?: string;
|
||||
};
|
||||
language: 'plaintext' | 'json';
|
||||
height: number;
|
||||
};
|
||||
|
||||
export const DEFAULT_OUTPUT_VALUE = {
|
||||
@@ -28,7 +27,6 @@ export const logicFunctionTestDataFamilyState = createAtomFamilyState<
|
||||
key: 'logicFunctionTestDataFamilyState',
|
||||
defaultValue: {
|
||||
language: 'plaintext',
|
||||
height: 64,
|
||||
input: {},
|
||||
shouldInitInput: true,
|
||||
output: DEFAULT_OUTPUT_VALUE,
|
||||
|
||||
@@ -96,7 +96,7 @@ export const SettingsAIMCP = () => {
|
||||
top: 12,
|
||||
},
|
||||
}}
|
||||
height="220px"
|
||||
height={230}
|
||||
/>
|
||||
</StyledEditorContainer>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import Editor, { type EditorProps, type Monaco } from '@monaco-editor/react';
|
||||
import { Loader } from '@ui/feedback/loader/components/Loader';
|
||||
import { ResizeHandle } from '@ui/layout/resize-handle/components/ResizeHandle';
|
||||
import { BASE_CODE_EDITOR_THEME_ID } from '@ui/input/code-editor/constants/BaseCodeEditorThemeId';
|
||||
import { useResizeHandle } from '@ui/layout/resize-handle/hooks/useResizeHandle';
|
||||
import { getBaseCodeEditorTheme } from '@ui/input/code-editor/theme/utils/getBaseCodeEditorTheme';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
@@ -20,6 +22,7 @@ type CodeEditorProps = Pick<
|
||||
variant?: CodeEditorVariant;
|
||||
isLoading?: boolean;
|
||||
transparentBackground?: boolean;
|
||||
resizable?: boolean;
|
||||
};
|
||||
|
||||
const StyledEditorLoader = styled.div<{
|
||||
@@ -118,6 +121,7 @@ export const CodeEditor = ({
|
||||
transparentBackground,
|
||||
isLoading = false,
|
||||
options,
|
||||
resizable = false,
|
||||
}: CodeEditorProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const [monaco, setMonaco] = useState<Monaco | undefined>(undefined);
|
||||
@@ -126,6 +130,18 @@ export const CodeEditor = ({
|
||||
>(undefined);
|
||||
const [isEditorFocused, setIsEditorFocused] = useState(false);
|
||||
|
||||
const numericHeight = typeof height === 'number' ? height : 450;
|
||||
const {
|
||||
size: resizableHeight,
|
||||
handleResizeStart,
|
||||
handleResizeMove,
|
||||
handleResizeEnd,
|
||||
} = useResizeHandle({
|
||||
initialSize: numericHeight,
|
||||
});
|
||||
|
||||
const currentHeight = resizable ? resizableHeight : height;
|
||||
|
||||
const setModelMarkers = (
|
||||
editor: editor.IStandaloneCodeEditor | undefined,
|
||||
monaco: Monaco | undefined,
|
||||
@@ -147,7 +163,7 @@ export const CodeEditor = ({
|
||||
};
|
||||
|
||||
return isLoading ? (
|
||||
<StyledEditorLoader height={height} variant={variant}>
|
||||
<StyledEditorLoader height={currentHeight} variant={variant}>
|
||||
<Loader />
|
||||
</StyledEditorLoader>
|
||||
) : (
|
||||
@@ -163,7 +179,7 @@ export const CodeEditor = ({
|
||||
transparentBackground={transparentBackground}
|
||||
>
|
||||
<Editor
|
||||
height={height}
|
||||
height={currentHeight}
|
||||
value={value}
|
||||
language={language}
|
||||
loading=""
|
||||
@@ -213,6 +229,13 @@ export const CodeEditor = ({
|
||||
}}
|
||||
/>
|
||||
</StyledEditorWrapper>
|
||||
{resizable && (
|
||||
<ResizeHandle
|
||||
onPointerDown={handleResizeStart}
|
||||
onPointerMove={handleResizeMove}
|
||||
onPointerUp={handleResizeEnd}
|
||||
/>
|
||||
)}
|
||||
</StyledCodeEditorContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -51,6 +51,8 @@ export type { ModalOverlay } from './modal/types/ModalOverlay';
|
||||
export type { ModalPadding } from './modal/types/ModalPadding';
|
||||
export type { ModalProps } from './modal/types/ModalProps';
|
||||
export type { ModalSize } from './modal/types/ModalSize';
|
||||
export { ResizeHandle } from './resize-handle/components/ResizeHandle';
|
||||
export { useResizeHandle } from './resize-handle/hooks/useResizeHandle';
|
||||
export {
|
||||
SectionAlignment,
|
||||
SectionFontColor,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { type CSSProperties } from 'react';
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
|
||||
type ResizeHandleProps = {
|
||||
onPointerDown?: (event: React.PointerEvent<HTMLDivElement>) => void;
|
||||
onPointerMove?: (event: React.PointerEvent<HTMLDivElement>) => void;
|
||||
onPointerUp?: (event: React.PointerEvent<HTMLDivElement>) => void;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
const StyledResizeHandleArea = styled.div`
|
||||
align-items: center;
|
||||
cursor: ns-resize;
|
||||
display: flex;
|
||||
height: 8px;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
|
||||
&:hover > div {
|
||||
background-color: ${themeCssVariables.font.color.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledResizeHandleBar = styled.div`
|
||||
background-color: ${themeCssVariables.background.quaternary};
|
||||
border-radius: ${themeCssVariables.border.radius.pill};
|
||||
height: 3px;
|
||||
transition: background-color ${themeCssVariables.animation.duration.fast}s;
|
||||
width: 32px;
|
||||
`;
|
||||
|
||||
export const ResizeHandle = ({
|
||||
onPointerDown,
|
||||
onPointerMove,
|
||||
onPointerUp,
|
||||
style,
|
||||
}: ResizeHandleProps) => (
|
||||
<StyledResizeHandleArea
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
style={style}
|
||||
>
|
||||
<StyledResizeHandleBar />
|
||||
</StyledResizeHandleArea>
|
||||
);
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { useResizeHandle } from '@ui/layout/resize-handle/hooks/useResizeHandle';
|
||||
import { ComponentDecorator } from '@ui/testing';
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
import { expect, userEvent, within } from 'storybook/test';
|
||||
|
||||
import { ResizeHandle } from '../ResizeHandle';
|
||||
|
||||
const INITIAL_HEIGHT = 150;
|
||||
|
||||
const ResizableDemo = () => {
|
||||
const { size, handleResizeStart, handleResizeMove, handleResizeEnd } =
|
||||
useResizeHandle({ initialSize: INITIAL_HEIGHT });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
data-testid="resizable-area"
|
||||
style={{
|
||||
background: themeCssVariables.background.secondary,
|
||||
height: size,
|
||||
width: 300,
|
||||
}}
|
||||
/>
|
||||
<ResizeHandle
|
||||
onPointerDown={handleResizeStart}
|
||||
onPointerMove={handleResizeMove}
|
||||
onPointerUp={handleResizeEnd}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ResizeHandle> = {
|
||||
title: 'UI/Layout/ResizeHandle',
|
||||
component: ResizeHandle,
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ResizeHandle>;
|
||||
|
||||
export const Resizable: Story = {
|
||||
render: () => <ResizableDemo />,
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const resizableArea = canvas.getByTestId('resizable-area');
|
||||
const handle = resizableArea.nextElementSibling as HTMLElement;
|
||||
|
||||
await expect(resizableArea).toHaveStyle({ height: `${INITIAL_HEIGHT}px` });
|
||||
|
||||
// Split pointer calls so React flushes state between pointerdown and pointermove
|
||||
await userEvent.pointer({
|
||||
keys: '[MouseLeft>]',
|
||||
target: handle,
|
||||
coords: { x: 0, y: 100 },
|
||||
});
|
||||
await userEvent.pointer({ target: handle, coords: { x: 0, y: 200 } });
|
||||
await userEvent.pointer({ keys: '[/MouseLeft]' });
|
||||
|
||||
await expect(resizableArea).toHaveStyle({
|
||||
height: `${INITIAL_HEIGHT + 100}px`,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useState, type PointerEvent } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const RESIZE_MIN_SIZE = 50;
|
||||
const RESIZE_MAX_SIZE = 500;
|
||||
|
||||
export const useResizeHandle = ({
|
||||
initialSize,
|
||||
axis = 'y',
|
||||
}: {
|
||||
initialSize: number;
|
||||
axis?: 'x' | 'y';
|
||||
}) => {
|
||||
const [size, setSize] = useState(initialSize);
|
||||
const [resizeStartState, setResizeStartState] = useState<{
|
||||
startPosition: number;
|
||||
startSize: number;
|
||||
} | null>(null);
|
||||
|
||||
const handleResizeStart = (event: PointerEvent) => {
|
||||
(event.target as HTMLElement).setPointerCapture(event.pointerId);
|
||||
const startPosition = axis === 'y' ? event.clientY : event.clientX;
|
||||
setResizeStartState({ startPosition, startSize: size });
|
||||
};
|
||||
|
||||
const handleResizeMove = (event: PointerEvent) => {
|
||||
if (!isDefined(resizeStartState)) {
|
||||
return;
|
||||
}
|
||||
const currentPosition = axis === 'y' ? event.clientY : event.clientX;
|
||||
const delta = currentPosition - resizeStartState.startPosition;
|
||||
const newSize = Math.min(
|
||||
RESIZE_MAX_SIZE,
|
||||
Math.max(RESIZE_MIN_SIZE, resizeStartState.startSize + delta),
|
||||
);
|
||||
setSize(newSize);
|
||||
};
|
||||
|
||||
const handleResizeEnd = () => {
|
||||
setResizeStartState(null);
|
||||
};
|
||||
|
||||
return { size, handleResizeStart, handleResizeMove, handleResizeEnd };
|
||||
};
|
||||
Reference in New Issue
Block a user