feat: start creating the form number field input

This commit is contained in:
Devessier
2024-11-20 18:37:10 +01:00
parent 78b7df5eb4
commit 06999e91af
4 changed files with 238 additions and 13 deletions

View File

@@ -0,0 +1,196 @@
import SearchVariablesDropdown from '@/workflow/search-variables/components/SearchVariablesDropdown';
import { VARIABLE_TAG_STYLES } from '@/workflow/search-variables/components/VariableTagInput';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useId, useState } from 'react';
import { IconX, TEXT_INPUT_STYLE, VisibilityHidden } from 'twenty-ui';
import {
canBeCastAsNumberOrNull,
castAsNumberOrNull,
} from '~/utils/cast-as-number-or-null';
const LINE_HEIGHT = 24;
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
`;
const StyledInputContainer = styled.div<{
multiline?: boolean;
}>`
display: flex;
flex-direction: row;
position: relative;
line-height: ${({ multiline }) => (multiline ? `${LINE_HEIGHT}px` : 'auto')};
min-height: ${({ multiline }) =>
multiline ? `${3 * LINE_HEIGHT}px` : 'auto'};
max-height: ${({ multiline }) =>
multiline ? `${5 * LINE_HEIGHT}px` : 'auto'};
`;
const StyledInputContainer2 = styled.div<{
multiline?: boolean;
readonly?: boolean;
}>`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
border-bottom-right-radius: ${({ multiline, theme }) =>
multiline ? theme.border.radius.sm : 'none'};
border-right: ${({ multiline }) => (multiline ? 'auto' : 'none')};
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
border-top-right-radius: ${({ multiline, theme }) =>
multiline ? theme.border.radius.sm : 'none'};
box-sizing: border-box;
display: flex;
height: ${({ multiline }) => (multiline ? 'auto' : `${1.5 * LINE_HEIGHT}px`)};
overflow: ${({ multiline }) => (multiline ? 'auto' : 'hidden')};
/* padding-right: ${({ multiline, theme }) =>
multiline ? theme.spacing(6) : theme.spacing(2)}; */
width: 100%;
`;
const StyledInput = styled.input`
${TEXT_INPUT_STYLE}
padding: ${({ theme }) => `${theme.spacing(1)} ${theme.spacing(2)}`};
width: 100%;
`;
const StyledVariableContainer = styled.div`
${VARIABLE_TAG_STYLES}
margin: ${({ theme }) => `${theme.spacing(1)} ${theme.spacing(2)}`};
align-self: center;
display: flex;
align-items: center;
`;
const StyledSearchVariablesDropdownContainer = styled.div<{
multiline?: boolean;
readonly?: boolean;
}>`
align-items: center;
display: flex;
justify-content: center;
${({ theme, readonly }) =>
!readonly &&
`
:hover {
background-color: ${theme.background.transparent.light};
}`}
${({ theme, multiline }) =>
multiline
? `
position: absolute;
top: ${theme.spacing(0)};
right: ${theme.spacing(0)};
padding: ${theme.spacing(0.5)} ${theme.spacing(0)};
border-radius: ${theme.border.radius.sm};
`
: `
background-color: ${theme.background.transparent.lighter};
border-top-right-radius: ${theme.border.radius.sm};
border-bottom-right-radius: ${theme.border.radius.sm};
border: 1px solid ${theme.border.color.medium};
`}
`;
type EditingMode = 'input' | 'variable';
type FormNumberFieldInputProps = {
placeholder: string;
defaultValue: string | undefined;
onPersist: (value: number | null | string) => void;
};
export const FormNumberFieldInput = ({
placeholder,
defaultValue,
onPersist,
}: FormNumberFieldInputProps) => {
const theme = useTheme();
const id = useId();
const [draftValue, setDraftValue] = useState(defaultValue ?? '');
const [editingMode, setEditingMode] = useState<EditingMode>(() => {
return defaultValue?.startsWith('{{') ? 'variable' : 'input';
});
const persistNumber = (newValue: string) => {
if (!canBeCastAsNumberOrNull(newValue)) {
return;
}
const castedValue = castAsNumberOrNull(newValue);
onPersist(castedValue);
};
const handleChange = (newText: string) => {
setDraftValue(newText);
persistNumber(newText.trim());
};
return (
<StyledContainer>
<StyledInputContainer>
<StyledInputContainer2>
{editingMode === 'input' ? (
<StyledInput
type="text"
placeholder={placeholder}
value={draftValue}
onChange={(event) => {
handleChange(event.target.value);
}}
/>
) : (
<StyledVariableContainer>
{draftValue}
<button
style={{
all: 'unset',
display: 'inline-flex',
cursor: 'pointer',
marginLeft: theme.spacing(1),
}}
onClick={() => {
setDraftValue('');
setEditingMode('input');
onPersist(null);
}}
>
<VisibilityHidden>Unlink the variable</VisibilityHidden>
<IconX size={theme.icon.size.sm} />
</button>
</StyledVariableContainer>
)}
</StyledInputContainer2>
<StyledSearchVariablesDropdownContainer
multiline={false}
readonly={false}
>
<SearchVariablesDropdown
inputId={id}
insertVariableTag={(variable) => {
setDraftValue(variable);
setEditingMode('variable');
onPersist(variable);
}}
disabled={false}
/>
</StyledSearchVariablesDropdownContainer>
</StyledInputContainer>
</StyledContainer>
);
};

View File

@@ -0,0 +1,19 @@
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
type WorkflowEditActionFormFieldProps = {
defaultValue: string;
};
export const WorkflowEditActionFormField = ({
defaultValue,
}: WorkflowEditActionFormFieldProps) => {
return (
<FormNumberFieldInput
defaultValue={defaultValue}
placeholder="Placeholder"
onPersist={(value) => {
console.log('save value to database', value);
}}
/>
);
};

View File

@@ -1,6 +1,6 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowEditActionFormField } from '@/workflow/components/WorkflowEditActionFormField';
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
import { WorkflowRecordCreateAction } from '@/workflow/types/Workflow';
import { useTheme } from '@emotion/react';
@@ -152,15 +152,20 @@ export const WorkflowEditActionFormRecordCreate = ({
<HorizontalSeparator noMargin />
{editableFields.map((field) => (
<FormFieldInput
<WorkflowEditActionFormField
key={field.id}
recordFieldInputdId={field.id}
label={field.label}
value={formData[field.name] as string}
onChange={(value) => {
handleFieldChange(field.name, value);
}}
defaultValue={formData[field.name] as string}
/>
// <FormFieldInput
// key={field.id}
// recordFieldInputdId={field.id}
// label={field.label}
// value={formData[field.name] as string}
// onChange={(value) => {
// handleFieldChange(field.name, value);
// }}
// />
))}
</WorkflowEditGenericFormBase>
);

View File

@@ -2,6 +2,7 @@ import SearchVariablesDropdown from '@/workflow/search-variables/components/Sear
import { initializeEditorContent } from '@/workflow/search-variables/utils/initializeEditorContent';
import { parseEditorContent } from '@/workflow/search-variables/utils/parseEditorContent';
import { VariableTag } from '@/workflow/search-variables/utils/variableTag';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import Document from '@tiptap/extension-document';
import HardBreak from '@tiptap/extension-hard-break';
@@ -9,7 +10,7 @@ import Paragraph from '@tiptap/extension-paragraph';
import Placeholder from '@tiptap/extension-placeholder';
import Text from '@tiptap/extension-text';
import { EditorContent, useEditor } from '@tiptap/react';
import { isDefined } from 'twenty-ui';
import { isDefined, ThemeType } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce';
const LINE_HEIGHT = 24;
@@ -71,6 +72,13 @@ const StyledSearchVariablesDropdownContainer = styled.div<{
`}
`;
export const VARIABLE_TAG_STYLES = ({ theme }: { theme: ThemeType }) => css`
background-color: ${theme.color.blue10};
border-radius: ${theme.border.radius.sm};
color: ${theme.color.blue};
padding: ${theme.spacing(1)};
`;
const StyledEditor = styled.div<{ multiline?: boolean; readonly?: boolean }>`
display: flex;
width: 100%;
@@ -119,10 +127,7 @@ const StyledEditor = styled.div<{ multiline?: boolean; readonly?: boolean }>`
}
.variable-tag {
color: ${({ theme }) => theme.color.blue};
background-color: ${({ theme }) => theme.color.blue10};
padding: ${({ theme }) => theme.spacing(1)};
border-radius: ${({ theme }) => theme.border.radius.sm};
${VARIABLE_TAG_STYLES}
}
}