Open showpage on workflow creation (#9714)

- Created an new component state
`isRecordEditableNameRenamingComponentState`
- Updated `useCreateNewTableRecord` to open the ShowPage on workflow
creation
- Refactored `RecordEditableName` and its components to remove the
useEffect (This was causing the recordName state to be updated after the
focus on `NavigationDrawerInput`, but we want the text so be selected
after the update).
- Introduced a new component `EditableBreadcrumbItem`
- Created an autosizing text input: This is done by a hack using a span
inside a div and the input position is set to absolute and takes the
size of the div. There are two problems that I didn't manage to fix:
If the text is too long, the title overflows, and the letter spacing is
different between the span and the input creating a small offset.


https://github.com/user-attachments/assets/4aa1e177-7458-4691-b0c8-96567b482206


New text input component:


https://github.com/user-attachments/assets/94565546-fe2b-457d-a1d8-907007e0e2ce
This commit is contained in:
Raphaël Bosi
2025-01-22 14:44:10 +01:00
committed by GitHub
parent 441b88b7e1
commit 8213995887
16 changed files with 429 additions and 179 deletions

View File

@ -1,3 +1,4 @@
import { InputLabel } from '@/ui/input/components/InputLabel';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
@ -10,28 +11,37 @@ import {
useRef,
useState,
} from 'react';
import { IconComponent, IconEye, IconEyeOff, RGBA } from 'twenty-ui';
import {
ComputeNodeDimensions,
IconComponent,
IconEye,
IconEyeOff,
RGBA,
} from 'twenty-ui';
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
import { InputLabel } from './InputLabel';
const StyledContainer = styled.div<
Pick<TextInputV2ComponentProps, 'fullWidth'>
>`
box-sizing: border-box;
display: inline-flex;
flex-direction: column;
width: ${({ fullWidth }) => (fullWidth ? `100%` : 'auto')};
`;
const StyledInputContainer = styled.div`
background-color: inherit;
display: flex;
flex-direction: row;
width: 100%;
position: relative;
`;
const StyledInput = styled.input<
Pick<TextInputV2ComponentProps, 'fullWidth' | 'LeftIcon' | 'error'>
Pick<
TextInputV2ComponentProps,
'LeftIcon' | 'error' | 'sizeVariant' | 'width'
>
>`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid
@ -44,12 +54,14 @@ const StyledInput = styled.input<
flex-grow: 1;
font-family: ${({ theme }) => theme.font.family};
font-weight: ${({ theme }) => theme.font.weight.regular};
height: 32px;
height: ${({ sizeVariant }) => (sizeVariant === 'sm' ? '20px' : '32px')};
outline: none;
padding: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme, sizeVariant }) =>
sizeVariant === 'sm' ? `${theme.spacing(2)} 0` : theme.spacing(2)};
padding-left: ${({ theme, LeftIcon }) =>
LeftIcon ? `calc(${theme.spacing(4)} + 16px)` : theme.spacing(2)};
width: 100%;
LeftIcon ? `px` : theme.spacing(2)};
width: ${({ theme, width }) =>
width ? `calc(${width}px + ${theme.spacing(5)})` : '100%'};
&::placeholder,
&::-webkit-input-placeholder {
@ -111,6 +123,8 @@ const StyledTrailingIcon = styled.div`
const INPUT_TYPE_PASSWORD = 'password';
export type TextInputV2Size = 'sm' | 'md';
export type TextInputV2ComponentProps = Omit<
InputHTMLAttributes<HTMLInputElement>,
'onChange' | 'onKeyDown'
@ -123,11 +137,15 @@ export type TextInputV2ComponentProps = Omit<
noErrorHelper?: boolean;
RightIcon?: IconComponent;
LeftIcon?: IconComponent;
autoGrow?: boolean;
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
onBlur?: FocusEventHandler<HTMLInputElement>;
dataTestId?: string;
sizeVariant?: TextInputV2Size;
};
type TextInputV2WithAutoGrowWrapperProps = TextInputV2ComponentProps;
const TextInputV2Component = (
{
className,
@ -138,6 +156,7 @@ const TextInputV2Component = (
onBlur,
onKeyDown,
fullWidth,
width,
error,
noErrorHelper = false,
required,
@ -150,6 +169,7 @@ const TextInputV2Component = (
LeftIcon,
autoComplete,
maxLength,
sizeVariant = 'md',
dataTestId,
}: TextInputV2ComponentProps,
// eslint-disable-next-line @nx/workspace-component-props-naming
@ -183,8 +203,10 @@ const TextInputV2Component = (
</StyledTrailingIcon>
</StyledLeftIconContainer>
)}
<StyledInput
id={inputId}
width={width}
data-testid={dataTestId}
autoComplete={autoComplete || 'off'}
ref={combinedRef}
@ -207,8 +229,10 @@ const TextInputV2Component = (
LeftIcon,
maxLength,
error,
sizeVariant,
}}
/>
<StyledTrailingIconContainer {...{ error }}>
{!error && type === INPUT_TYPE_PASSWORD && (
<StyledTrailingIcon
@ -236,4 +260,22 @@ const TextInputV2Component = (
);
};
export const TextInputV2 = forwardRef(TextInputV2Component);
const TextInputV2WithAutoGrowWrapper = (
props: TextInputV2WithAutoGrowWrapperProps,
) => (
<>
{props.autoGrow ? (
<ComputeNodeDimensions node={props.value || props.placeholder}>
{(nodeDimensions) => (
// eslint-disable-next-line
<TextInputV2Component {...props} width={nodeDimensions?.width} />
)}
</ComputeNodeDimensions>
) : (
// eslint-disable-next-line
<TextInputV2Component {...props} />
)}
</>
);
export const TextInputV2 = forwardRef(TextInputV2WithAutoGrowWrapper);

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react';
import { ComponentDecorator } from 'twenty-ui';
import {
@ -40,3 +40,19 @@ export const Filled: Story = {
export const Disabled: Story = {
args: { disabled: true, value: 'Tim' },
};
export const AutoGrow: Story = {
args: { autoGrow: true, value: 'Tim' },
};
export const AutoGrowWithPlaceholder: Story = {
args: { autoGrow: true, placeholder: 'Tim' },
};
export const Small: Story = {
args: { sizeVariant: 'sm', value: 'Tim' },
};
export const AutoGrowSmall: Story = {
args: { autoGrow: true, sizeVariant: 'sm', value: 'Tim' },
};